Actual source code: plexfem.c

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

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

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

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

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

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

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

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

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

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

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

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

 92:   Not Collective

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

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

101:   Level: advanced

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

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

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

119:   Not Collective

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

126:   Level: advanced

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

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

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

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

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

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

160:   Not collective

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

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

168:   Level: intermediate

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

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

185:   Not collective

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

191:   Level: intermediate

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

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

207:   Not collective

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

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

215:   Level: intermediate

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

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

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

233:   Not collective

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

239:   Level: intermediate

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

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

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

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

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

282:   Collective

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

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

291:   Level: advanced

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

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

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

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

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

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

358:   Collective

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

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

370:   Level: advanced

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

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

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

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

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

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

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

436:   Level: advanced

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

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

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

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

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

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

468:   Level: intermediate

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

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

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

491: /*
492:   Note: Following https://en.wikipedia.org/wiki/Euler_angles, we will specify Euler angles by extrinsic rotations, meaning that
493:   we rotate with respect to a fixed initial coordinate system, the local basis (x-y-z). The global basis (X-Y-Z) is reached as follows:
494:   $ The XYZ system rotates about the z axis by alpha. The X axis is now at angle alpha with respect to the x axis.
495:   $ The XYZ system rotates again about the x axis by beta. The Z axis is now at angle beta with respect to the z axis.
496:   $ The XYZ system rotates a third time about the z axis by gamma.
497: */
498: static PetscErrorCode DMPlexBasisTransformSetUp_Rotation_Internal(DM dm, void *ctx)
499: {
500:   RotCtx   *rc  = (RotCtx *)ctx;
501:   PetscInt  dim = rc->dim;
502:   PetscReal c1, s1, c2, s2, c3, s3;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

795:   Level: developer

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

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

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

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

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

821:   Level: developer

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

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

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

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

847:   Level: developer

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

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

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

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

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

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

899:   Level: developer

901: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLabel`, `DMPlexInsertBoundaryValuesEssentialField()`, `DMPlexInsertBoundaryValuesEssentialBdField()`, `DMAddBoundary()`
902: @*/
903: PetscErrorCode DMPlexInsertBoundaryValuesEssential(DM dm, PetscReal time, PetscInt field, PetscInt Nc, const PetscInt comps[], DMLabel label, PetscInt numids, const PetscInt ids[], PetscErrorCode (*func)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void *ctx, Vec locX)
904: {
905:   PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal x[], PetscInt, PetscScalar *u, void *ctx);
906:   void   **ctxs;
907:   PetscInt numFields;

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

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

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

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

938:   Level: developer

940: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexInsertBoundaryValuesEssential()`, `DMPlexInsertBoundaryValuesEssentialBdField()`, `DMAddBoundary()`
941: @*/
942: PetscErrorCode DMPlexInsertBoundaryValuesEssentialField(DM dm, PetscReal time, Vec locU, PetscInt field, PetscInt Nc, const PetscInt comps[], DMLabel label, PetscInt numids, const PetscInt ids[], void (*func)(PetscInt, PetscInt, PetscInt, const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], PetscReal, const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]), void *ctx, Vec locX)
943: {
944:   void (**funcs)(PetscInt, PetscInt, PetscInt, const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], PetscReal, const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]);
945:   void   **ctxs;
946:   PetscInt numFields;

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

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

961:   Collective

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

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

979:   Level: developer

981: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectBdFieldLabelLocal()`, `DMPlexInsertBoundaryValuesEssential()`, `DMPlexInsertBoundaryValuesEssentialField()`, `DMAddBoundary()`
982: @*/
983: PetscErrorCode DMPlexInsertBoundaryValuesEssentialBdField(DM dm, PetscReal time, Vec locU, PetscInt field, PetscInt Nc, const PetscInt comps[], DMLabel label, PetscInt numids, const PetscInt ids[], void (*func)(PetscInt, PetscInt, PetscInt, const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], PetscReal, const PetscReal[], const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]), void *ctx, Vec locX)
984: {
985:   void (**funcs)(PetscInt, PetscInt, PetscInt, const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], PetscReal, const PetscReal[], const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]);
986:   void   **ctxs;
987:   PetscInt numFields;

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

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

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

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

1020:   Level: developer

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

1025: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexInsertBoundaryValuesEssential()`, `DMPlexInsertBoundaryValuesEssentialField()`, `DMAddBoundary()`
1026: @*/
1027: PetscErrorCode DMPlexInsertBoundaryValuesRiemann(DM dm, PetscReal time, Vec faceGeometry, Vec cellGeometry, Vec Grad, PetscInt field, PetscInt Nc, const PetscInt comps[], DMLabel label, PetscInt numids, const PetscInt ids[], PetscErrorCode (*func)(PetscReal, const PetscReal *, const PetscReal *, const PetscScalar *, PetscScalar *, void *), void *ctx, Vec locX)
1028: {
1029:   PetscDS            prob;
1030:   PetscSF            sf;
1031:   DM                 dmFace, dmCell, dmGrad;
1032:   const PetscScalar *facegeom, *cellgeom = NULL, *grad;
1033:   const PetscInt    *leaves;
1034:   PetscScalar       *x, *fx;
1035:   PetscInt           dim, nleaves, loc, fStart, fEnd, pdim, i;
1036:   PetscErrorCode     ierru = PETSC_SUCCESS;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1246: PetscErrorCode DMPlexInsertBounds_Plex(DM dm, PetscBool lower, PetscReal time, Vec locB)
1247: {
1248:   PetscDS  ds;
1249:   PetscInt numBd;

1251:   PetscFunctionBegin;
1252:   PetscCall(DMGetDS(dm, &ds));
1253:   PetscCall(PetscDSGetNumBoundary(ds, &numBd));
1254:   PetscCall(PetscDSUpdateBoundaryLabels(ds, dm));
1255:   for (PetscInt b = 0; b < numBd; ++b) {
1256:     PetscWeakForm           wf;
1257:     DMBoundaryConditionType type;
1258:     const char             *name;
1259:     DMLabel                 label;
1260:     PetscInt                numids;
1261:     const PetscInt         *ids;
1262:     PetscInt                field, Nc;
1263:     const PetscInt         *comps;
1264:     void (*bvfunc)(void);
1265:     void *ctx;

1267:     PetscCall(PetscDSGetBoundary(ds, b, &wf, &type, &name, &label, &numids, &ids, &field, &Nc, &comps, &bvfunc, NULL, &ctx));
1268:     if (lower && type != DM_BC_LOWER_BOUND) continue;
1269:     if (!lower && type != DM_BC_UPPER_BOUND) continue;
1270:     PetscCall(DMPlexLabelAddCells(dm, label));
1271:     {
1272:       PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal x[], PetscInt, PetscScalar *u, void *ctx);
1273:       void   **ctxs;
1274:       PetscInt Nf;

1276:       PetscCall(DMGetNumFields(dm, &Nf));
1277:       PetscCall(PetscCalloc2(Nf, &funcs, Nf, &ctxs));
1278:       funcs[field] = (PetscSimplePointFn *)bvfunc;
1279:       ctxs[field]  = ctx;
1280:       PetscCall(DMProjectFunctionLabelLocal(dm, time, label, numids, ids, Nc, comps, funcs, ctxs, INSERT_ALL_VALUES, locB));
1281:       PetscCall(PetscFree2(funcs, ctxs));
1282:     }
1283:     PetscCall(DMPlexLabelClearCells(dm, label));
1284:   }
1285:   PetscFunctionReturn(PETSC_SUCCESS);
1286: }

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

1291:   Not Collective

1293:   Input Parameters:
1294: + dm              - The `DM`
1295: . insertEssential - Should I insert essential (e.g. Dirichlet) or inessential (e.g. Neumann) boundary conditions
1296: . time            - The time
1297: . faceGeomFVM     - Face geometry data for FV discretizations
1298: . cellGeomFVM     - Cell geometry data for FV discretizations
1299: - gradFVM         - Gradient reconstruction data for FV discretizations

1301:   Output Parameter:
1302: . locX - Solution updated with boundary values

1304:   Level: intermediate

1306: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunctionLabelLocal()`, `DMAddBoundary()`
1307: @*/
1308: PetscErrorCode DMPlexInsertBoundaryValues(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
1309: {
1310:   PetscFunctionBegin;
1316:   PetscTryMethod(dm, "DMPlexInsertBoundaryValues_C", (DM, PetscBool, Vec, PetscReal, Vec, Vec, Vec), (dm, insertEssential, locX, time, faceGeomFVM, cellGeomFVM, gradFVM));
1317:   PetscFunctionReturn(PETSC_SUCCESS);
1318: }

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

1323:   Input Parameters:
1324: + dm              - The `DM`
1325: . insertEssential - Should I insert essential (e.g. Dirichlet) or inessential (e.g. Neumann) boundary conditions
1326: . time            - The time
1327: . faceGeomFVM     - Face geometry data for FV discretizations
1328: . cellGeomFVM     - Cell geometry data for FV discretizations
1329: - gradFVM         - Gradient reconstruction data for FV discretizations

1331:   Output Parameter:
1332: . locX_t - Solution updated with boundary values

1334:   Level: developer

1336: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunctionLabelLocal()`
1337: @*/
1338: PetscErrorCode DMPlexInsertTimeDerivativeBoundaryValues(DM dm, PetscBool insertEssential, Vec locX_t, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
1339: {
1340:   PetscFunctionBegin;
1346:   PetscTryMethod(dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", (DM, PetscBool, Vec, PetscReal, Vec, Vec, Vec), (dm, insertEssential, locX_t, time, faceGeomFVM, cellGeomFVM, gradFVM));
1347:   PetscFunctionReturn(PETSC_SUCCESS);
1348: }

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

1353:   Not Collective

1355:   Input Parameters:
1356: + dm    - The `DM`
1357: . lower - If `PETSC_TRUE` use `DM_BC_LOWER_BOUND` conditions, otherwise use `DM_BC_UPPER_BOUND`
1358: - time  - The time

1360:   Output Parameter:
1361: . locB - Bounds vector updated with new bounds

1363:   Level: intermediate

1365: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunctionLabelLocal()`, `PetscDSAddBoundary()`
1366: @*/
1367: PetscErrorCode DMPlexInsertBounds(DM dm, PetscBool lower, PetscReal time, Vec locB)
1368: {
1369:   PetscFunctionBegin;
1372:   PetscTryMethod(dm, "DMPlexInsertBounds_C", (DM, PetscBool, PetscReal, Vec), (dm, lower, time, locB));
1373:   PetscFunctionReturn(PETSC_SUCCESS);
1374: }

1376: // Handle non-essential (e.g. outflow) boundary values
1377: PetscErrorCode DMPlexInsertBoundaryValuesFVM(DM dm, PetscFV fv, Vec locX, PetscReal time, Vec *locGradient)
1378: {
1379:   DM  dmGrad;
1380:   Vec cellGeometryFVM, faceGeometryFVM, locGrad = NULL;

1382:   PetscFunctionBegin;
1386:   if (locGradient) {
1387:     PetscAssertPointer(locGradient, 5);
1388:     *locGradient = NULL;
1389:   }
1390:   PetscCall(DMPlexGetGeometryFVM(dm, &faceGeometryFVM, &cellGeometryFVM, NULL));
1391:   /* Reconstruct and limit cell gradients */
1392:   PetscCall(DMPlexGetGradientDM(dm, fv, &dmGrad));
1393:   if (dmGrad) {
1394:     Vec      grad;
1395:     PetscInt fStart, fEnd;

1397:     PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
1398:     PetscCall(DMGetGlobalVector(dmGrad, &grad));
1399:     PetscCall(DMPlexReconstructGradients_Internal(dm, fv, fStart, fEnd, faceGeometryFVM, cellGeometryFVM, locX, grad));
1400:     /* Communicate gradient values */
1401:     PetscCall(DMGetLocalVector(dmGrad, &locGrad));
1402:     PetscCall(DMGlobalToLocalBegin(dmGrad, grad, INSERT_VALUES, locGrad));
1403:     PetscCall(DMGlobalToLocalEnd(dmGrad, grad, INSERT_VALUES, locGrad));
1404:     PetscCall(DMRestoreGlobalVector(dmGrad, &grad));
1405:   }
1406:   PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_FALSE, locX, time, faceGeometryFVM, cellGeometryFVM, locGrad));
1407:   if (locGradient) *locGradient = locGrad;
1408:   else if (locGrad) PetscCall(DMRestoreLocalVector(dmGrad, &locGrad));
1409:   PetscFunctionReturn(PETSC_SUCCESS);
1410: }

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

1416:   PetscFunctionBegin;
1417:   PetscCall(DMGetLocalVector(dm, &localX));
1418:   PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, localX, time, NULL, NULL, NULL));
1419:   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
1420:   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
1421:   PetscCall(DMPlexComputeL2DiffLocal(dm, time, funcs, ctxs, localX, diff));
1422:   PetscCall(DMRestoreLocalVector(dm, &localX));
1423:   PetscFunctionReturn(PETSC_SUCCESS);
1424: }

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

1429:   Collective

1431:   Input Parameters:
1432: + dm     - The `DM`
1433: . time   - The time
1434: . funcs  - The functions to evaluate for each field component
1435: . ctxs   - Optional array of contexts to pass to each function, or `NULL`.
1436: - localX - The coefficient vector u_h, a local vector

1438:   Output Parameter:
1439: . diff - The diff ||u - u_h||_2

1441:   Level: developer

1443: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
1444: @*/
1445: PetscErrorCode DMPlexComputeL2DiffLocal(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec localX, PetscReal *diff)
1446: {
1447:   const PetscInt   debug = ((DM_Plex *)dm->data)->printL2;
1448:   DM               tdm;
1449:   Vec              tv;
1450:   PetscSection     section;
1451:   PetscQuadrature  quad;
1452:   PetscFEGeom      fegeom;
1453:   PetscScalar     *funcVal, *interpolant;
1454:   PetscReal       *coords, *gcoords;
1455:   PetscReal        localDiff = 0.0;
1456:   const PetscReal *quadWeights;
1457:   PetscInt         dim, coordDim, numFields, numComponents = 0, qNc, Nq, cellHeight, cStart, cEnd, c, field, fieldOffset;
1458:   PetscBool        transform;

1460:   PetscFunctionBegin;
1461:   PetscCall(DMGetDimension(dm, &dim));
1462:   PetscCall(DMGetCoordinateDim(dm, &coordDim));
1463:   fegeom.dimEmbed = coordDim;
1464:   PetscCall(DMGetLocalSection(dm, &section));
1465:   PetscCall(PetscSectionGetNumFields(section, &numFields));
1466:   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
1467:   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
1468:   PetscCall(DMHasBasisTransform(dm, &transform));
1469:   PetscCheck(numFields, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fields is zero!");
1470:   for (field = 0; field < numFields; ++field) {
1471:     PetscObject  obj;
1472:     PetscClassId id;
1473:     PetscInt     Nc;

1475:     PetscCall(DMGetField(dm, field, NULL, &obj));
1476:     PetscCall(PetscObjectGetClassId(obj, &id));
1477:     if (id == PETSCFE_CLASSID) {
1478:       PetscFE fe = (PetscFE)obj;

1480:       PetscCall(PetscFEGetQuadrature(fe, &quad));
1481:       PetscCall(PetscFEGetNumComponents(fe, &Nc));
1482:     } else if (id == PETSCFV_CLASSID) {
1483:       PetscFV fv = (PetscFV)obj;

1485:       PetscCall(PetscFVGetQuadrature(fv, &quad));
1486:       PetscCall(PetscFVGetNumComponents(fv, &Nc));
1487:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1488:     numComponents += Nc;
1489:   }
1490:   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, NULL, &quadWeights));
1491:   PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
1492:   PetscCall(PetscMalloc6(numComponents, &funcVal, numComponents, &interpolant, coordDim * (Nq + 1), &coords, Nq, &fegeom.detJ, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ));
1493:   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1494:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
1495:   for (c = cStart; c < cEnd; ++c) {
1496:     PetscScalar *x        = NULL;
1497:     PetscReal    elemDiff = 0.0;
1498:     PetscInt     qc       = 0;

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

1503:     for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1504:       PetscObject  obj;
1505:       PetscClassId id;
1506:       void *const  ctx = ctxs ? ctxs[field] : NULL;
1507:       PetscInt     Nb, Nc, q, fc;

1509:       PetscCall(DMGetField(dm, field, NULL, &obj));
1510:       PetscCall(PetscObjectGetClassId(obj, &id));
1511:       if (id == PETSCFE_CLASSID) {
1512:         PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
1513:         PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
1514:       } else if (id == PETSCFV_CLASSID) {
1515:         PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
1516:         Nb = 1;
1517:       } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1518:       if (debug) {
1519:         char title[1024];
1520:         PetscCall(PetscSNPrintf(title, 1023, "Solution for Field %" PetscInt_FMT, field));
1521:         PetscCall(DMPrintCellVector(c, title, Nb, &x[fieldOffset]));
1522:       }
1523:       for (q = 0; q < Nq; ++q) {
1524:         PetscFEGeom    qgeom;
1525:         PetscErrorCode ierr;

1527:         qgeom.dimEmbed = fegeom.dimEmbed;
1528:         qgeom.J        = &fegeom.J[q * coordDim * coordDim];
1529:         qgeom.invJ     = &fegeom.invJ[q * coordDim * coordDim];
1530:         qgeom.detJ     = &fegeom.detJ[q];
1531:         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);
1532:         if (transform) {
1533:           gcoords = &coords[coordDim * Nq];
1534:           PetscCall(DMPlexBasisTransformApplyReal_Internal(dm, &coords[coordDim * q], PETSC_TRUE, coordDim, &coords[coordDim * q], gcoords, dm->transformCtx));
1535:         } else {
1536:           gcoords = &coords[coordDim * q];
1537:         }
1538:         PetscCall(PetscArrayzero(funcVal, Nc));
1539:         ierr = (*funcs[field])(coordDim, time, gcoords, Nc, funcVal, ctx);
1540:         if (ierr) {
1541:           PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1542:           PetscCall(DMRestoreLocalVector(dm, &localX));
1543:           PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1544:         }
1545:         if (transform) PetscCall(DMPlexBasisTransformApply_Internal(dm, &coords[coordDim * q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx));
1546:         if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[fieldOffset], &qgeom, q, interpolant));
1547:         else if (id == PETSCFV_CLASSID) PetscCall(PetscFVInterpolate_Static((PetscFV)obj, &x[fieldOffset], q, interpolant));
1548:         else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1549:         for (fc = 0; fc < Nc; ++fc) {
1550:           const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
1551:           if (debug)
1552:             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),
1553:                                   (double)(PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q]), (double)PetscRealPart(interpolant[fc]), (double)PetscRealPart(funcVal[fc])));
1554:           elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
1555:         }
1556:       }
1557:       fieldOffset += Nb;
1558:       qc += Nc;
1559:     }
1560:     PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1561:     if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  elem %" PetscInt_FMT " diff %g\n", c, (double)elemDiff));
1562:     localDiff += elemDiff;
1563:   }
1564:   PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1565:   PetscCallMPI(MPIU_Allreduce(&localDiff, diff, 1, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
1566:   *diff = PetscSqrtReal(*diff);
1567:   PetscFunctionReturn(PETSC_SUCCESS);
1568: }

1570: 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)
1571: {
1572:   const PetscInt   debug = ((DM_Plex *)dm->data)->printL2;
1573:   DM               tdm;
1574:   PetscSection     section;
1575:   PetscQuadrature  quad;
1576:   Vec              localX, tv;
1577:   PetscScalar     *funcVal, *interpolant;
1578:   const PetscReal *quadWeights;
1579:   PetscFEGeom      fegeom;
1580:   PetscReal       *coords, *gcoords;
1581:   PetscReal        localDiff = 0.0;
1582:   PetscInt         dim, coordDim, qNc = 0, Nq = 0, numFields, numComponents = 0, cStart, cEnd, c, field, fieldOffset;
1583:   PetscBool        transform;

1585:   PetscFunctionBegin;
1586:   PetscCall(DMGetDimension(dm, &dim));
1587:   PetscCall(DMGetCoordinateDim(dm, &coordDim));
1588:   fegeom.dimEmbed = coordDim;
1589:   PetscCall(DMGetLocalSection(dm, &section));
1590:   PetscCall(PetscSectionGetNumFields(section, &numFields));
1591:   PetscCall(DMGetLocalVector(dm, &localX));
1592:   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
1593:   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
1594:   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
1595:   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
1596:   PetscCall(DMHasBasisTransform(dm, &transform));
1597:   for (field = 0; field < numFields; ++field) {
1598:     PetscFE  fe;
1599:     PetscInt Nc;

1601:     PetscCall(DMGetField(dm, field, NULL, (PetscObject *)&fe));
1602:     PetscCall(PetscFEGetQuadrature(fe, &quad));
1603:     PetscCall(PetscFEGetNumComponents(fe, &Nc));
1604:     numComponents += Nc;
1605:   }
1606:   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, NULL, &quadWeights));
1607:   PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
1608:   /* PetscCall(DMProjectFunctionLocal(dm, fe, funcs, INSERT_BC_VALUES, localX)); */
1609:   PetscCall(PetscMalloc6(numComponents, &funcVal, coordDim * (Nq + 1), &coords, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ, numComponents * coordDim, &interpolant, Nq, &fegeom.detJ));
1610:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
1611:   for (c = cStart; c < cEnd; ++c) {
1612:     PetscScalar *x        = NULL;
1613:     PetscReal    elemDiff = 0.0;
1614:     PetscInt     qc       = 0;

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

1619:     for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1620:       PetscFE     fe;
1621:       void *const ctx = ctxs ? ctxs[field] : NULL;
1622:       PetscInt    Nb, Nc, q, fc;

1624:       PetscCall(DMGetField(dm, field, NULL, (PetscObject *)&fe));
1625:       PetscCall(PetscFEGetDimension(fe, &Nb));
1626:       PetscCall(PetscFEGetNumComponents(fe, &Nc));
1627:       if (debug) {
1628:         char title[1024];
1629:         PetscCall(PetscSNPrintf(title, 1023, "Solution for Field %" PetscInt_FMT, field));
1630:         PetscCall(DMPrintCellVector(c, title, Nb, &x[fieldOffset]));
1631:       }
1632:       for (q = 0; q < Nq; ++q) {
1633:         PetscFEGeom    qgeom;
1634:         PetscErrorCode ierr;

1636:         qgeom.dimEmbed = fegeom.dimEmbed;
1637:         qgeom.J        = &fegeom.J[q * coordDim * coordDim];
1638:         qgeom.invJ     = &fegeom.invJ[q * coordDim * coordDim];
1639:         qgeom.detJ     = &fegeom.detJ[q];
1640:         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);
1641:         if (transform) {
1642:           gcoords = &coords[coordDim * Nq];
1643:           PetscCall(DMPlexBasisTransformApplyReal_Internal(dm, &coords[coordDim * q], PETSC_TRUE, coordDim, &coords[coordDim * q], gcoords, dm->transformCtx));
1644:         } else {
1645:           gcoords = &coords[coordDim * q];
1646:         }
1647:         PetscCall(PetscArrayzero(funcVal, Nc));
1648:         ierr = (*funcs[field])(coordDim, time, gcoords, n, Nc, funcVal, ctx);
1649:         if (ierr) {
1650:           PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1651:           PetscCall(DMRestoreLocalVector(dm, &localX));
1652:           PetscCall(PetscFree6(funcVal, coords, fegeom.J, fegeom.invJ, interpolant, fegeom.detJ));
1653:         }
1654:         if (transform) PetscCall(DMPlexBasisTransformApply_Internal(dm, &coords[coordDim * q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx));
1655:         PetscCall(PetscFEInterpolateGradient_Static(fe, 1, &x[fieldOffset], &qgeom, q, interpolant));
1656:         /* Overwrite with the dot product if the normal is given */
1657:         if (n) {
1658:           for (fc = 0; fc < Nc; ++fc) {
1659:             PetscScalar sum = 0.0;
1660:             PetscInt    d;
1661:             for (d = 0; d < dim; ++d) sum += interpolant[fc * dim + d] * n[d];
1662:             interpolant[fc] = sum;
1663:           }
1664:         }
1665:         for (fc = 0; fc < Nc; ++fc) {
1666:           const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
1667:           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])));
1668:           elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
1669:         }
1670:       }
1671:       fieldOffset += Nb;
1672:       qc += Nc;
1673:     }
1674:     PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1675:     if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  elem %" PetscInt_FMT " diff %g\n", c, (double)elemDiff));
1676:     localDiff += elemDiff;
1677:   }
1678:   PetscCall(PetscFree6(funcVal, coords, fegeom.J, fegeom.invJ, interpolant, fegeom.detJ));
1679:   PetscCall(DMRestoreLocalVector(dm, &localX));
1680:   PetscCallMPI(MPIU_Allreduce(&localDiff, diff, 1, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
1681:   *diff = PetscSqrtReal(*diff);
1682:   PetscFunctionReturn(PETSC_SUCCESS);
1683: }

1685: PetscErrorCode DMComputeL2FieldDiff_Plex(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, PetscReal *diff)
1686: {
1687:   const PetscInt debug = ((DM_Plex *)dm->data)->printL2;
1688:   DM             tdm;
1689:   DMLabel        depthLabel;
1690:   PetscSection   section;
1691:   Vec            localX, tv;
1692:   PetscReal     *localDiff;
1693:   PetscInt       dim, depth, dE, Nf, f, Nds, s;
1694:   PetscBool      transform;

1696:   PetscFunctionBegin;
1697:   PetscCall(DMGetDimension(dm, &dim));
1698:   PetscCall(DMGetCoordinateDim(dm, &dE));
1699:   PetscCall(DMGetLocalSection(dm, &section));
1700:   PetscCall(DMGetLocalVector(dm, &localX));
1701:   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
1702:   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
1703:   PetscCall(DMHasBasisTransform(dm, &transform));
1704:   PetscCall(DMGetNumFields(dm, &Nf));
1705:   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
1706:   PetscCall(DMLabelGetNumValues(depthLabel, &depth));

1708:   PetscCall(VecSet(localX, 0.0));
1709:   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
1710:   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
1711:   PetscCall(DMProjectFunctionLocal(dm, time, funcs, ctxs, INSERT_BC_VALUES, localX));
1712:   PetscCall(DMGetNumDS(dm, &Nds));
1713:   PetscCall(PetscCalloc1(Nf, &localDiff));
1714:   for (s = 0; s < Nds; ++s) {
1715:     PetscDS          ds;
1716:     DMLabel          label;
1717:     IS               fieldIS, pointIS;
1718:     const PetscInt  *fields, *points = NULL;
1719:     PetscQuadrature  quad;
1720:     const PetscReal *quadPoints, *quadWeights;
1721:     PetscFEGeom      fegeom;
1722:     PetscReal       *coords, *gcoords;
1723:     PetscScalar     *funcVal, *interpolant;
1724:     PetscBool        isCohesive;
1725:     PetscInt         qNc, Nq, totNc, cStart = 0, cEnd, c, dsNf;

1727:     PetscCall(DMGetRegionNumDS(dm, s, &label, &fieldIS, &ds, NULL));
1728:     PetscCall(ISGetIndices(fieldIS, &fields));
1729:     PetscCall(PetscDSIsCohesive(ds, &isCohesive));
1730:     PetscCall(PetscDSGetNumFields(ds, &dsNf));
1731:     PetscCall(PetscDSGetTotalComponents(ds, &totNc));
1732:     PetscCall(PetscDSGetQuadrature(ds, &quad));
1733:     PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
1734:     PetscCheck(!(qNc != 1) || !(qNc != totNc), PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, totNc);
1735:     PetscCall(PetscCalloc6(totNc, &funcVal, totNc, &interpolant, dE * (Nq + 1), &coords, Nq, &fegeom.detJ, dE * dE * Nq, &fegeom.J, dE * dE * Nq, &fegeom.invJ));
1736:     if (!label) {
1737:       PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
1738:     } else {
1739:       PetscCall(DMLabelGetStratumIS(label, 1, &pointIS));
1740:       PetscCall(ISGetLocalSize(pointIS, &cEnd));
1741:       PetscCall(ISGetIndices(pointIS, &points));
1742:     }
1743:     for (c = cStart; c < cEnd; ++c) {
1744:       const PetscInt  cell = points ? points[c] : c;
1745:       PetscScalar    *x    = NULL;
1746:       const PetscInt *cone;
1747:       PetscInt        qc = 0, fOff = 0, dep;

1749:       PetscCall(DMLabelGetValue(depthLabel, cell, &dep));
1750:       if (dep != depth - 1) continue;
1751:       if (isCohesive) {
1752:         PetscCall(DMPlexGetCone(dm, cell, &cone));
1753:         PetscCall(DMPlexComputeCellGeometryFEM(dm, cone[0], quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1754:       } else {
1755:         PetscCall(DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1756:       }
1757:       PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, NULL, PETSC_FALSE, localX, cell, 0, NULL, &x));
1758:       for (f = 0; f < dsNf; ++f) {
1759:         PetscObject  obj;
1760:         PetscClassId id;
1761:         void *const  ctx = ctxs ? ctxs[fields[f]] : NULL;
1762:         PetscInt     Nb, Nc, q, fc;
1763:         PetscReal    elemDiff = 0.0;
1764:         PetscBool    cohesive;

1766:         PetscCall(PetscDSGetCohesive(ds, f, &cohesive));
1767:         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
1768:         PetscCall(PetscObjectGetClassId(obj, &id));
1769:         if (id == PETSCFE_CLASSID) {
1770:           PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
1771:           PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
1772:         } else if (id == PETSCFV_CLASSID) {
1773:           PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
1774:           Nb = 1;
1775:         } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, fields[f]);
1776:         if (isCohesive && !cohesive) {
1777:           fOff += Nb * 2;
1778:           qc += Nc;
1779:           continue;
1780:         }
1781:         if (debug) {
1782:           char title[1024];
1783:           PetscCall(PetscSNPrintf(title, 1023, "Solution for Field %" PetscInt_FMT, fields[f]));
1784:           PetscCall(DMPrintCellVector(cell, title, Nb, &x[fOff]));
1785:         }
1786:         for (q = 0; q < Nq; ++q) {
1787:           PetscFEGeom    qgeom;
1788:           PetscErrorCode ierr;

1790:           qgeom.dimEmbed = fegeom.dimEmbed;
1791:           qgeom.J        = &fegeom.J[q * dE * dE];
1792:           qgeom.invJ     = &fegeom.invJ[q * dE * dE];
1793:           qgeom.detJ     = &fegeom.detJ[q];
1794:           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);
1795:           if (transform) {
1796:             gcoords = &coords[dE * Nq];
1797:             PetscCall(DMPlexBasisTransformApplyReal_Internal(dm, &coords[dE * q], PETSC_TRUE, dE, &coords[dE * q], gcoords, dm->transformCtx));
1798:           } else {
1799:             gcoords = &coords[dE * q];
1800:           }
1801:           for (fc = 0; fc < Nc; ++fc) funcVal[fc] = 0.;
1802:           ierr = (*funcs[fields[f]])(dE, time, gcoords, Nc, funcVal, ctx);
1803:           if (ierr) {
1804:             PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, cell, NULL, &x));
1805:             PetscCall(DMRestoreLocalVector(dm, &localX));
1806:             PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1807:           }
1808:           if (transform) PetscCall(DMPlexBasisTransformApply_Internal(dm, &coords[dE * q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx));
1809:           /* Call once for each face, except for lagrange field */
1810:           if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[fOff], &qgeom, q, interpolant));
1811:           else if (id == PETSCFV_CLASSID) PetscCall(PetscFVInterpolate_Static((PetscFV)obj, &x[fOff], q, interpolant));
1812:           else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, fields[f]);
1813:           for (fc = 0; fc < Nc; ++fc) {
1814:             const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
1815:             if (debug)
1816:               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),
1817:                                     (double)(PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q])));
1818:             elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
1819:           }
1820:         }
1821:         fOff += Nb;
1822:         qc += Nc;
1823:         localDiff[fields[f]] += elemDiff;
1824:         if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  cell %" PetscInt_FMT " field %" PetscInt_FMT " cum diff %g\n", cell, fields[f], (double)localDiff[fields[f]]));
1825:       }
1826:       PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, cell, NULL, &x));
1827:     }
1828:     if (label) {
1829:       PetscCall(ISRestoreIndices(pointIS, &points));
1830:       PetscCall(ISDestroy(&pointIS));
1831:     }
1832:     PetscCall(ISRestoreIndices(fieldIS, &fields));
1833:     PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1834:   }
1835:   PetscCall(DMRestoreLocalVector(dm, &localX));
1836:   PetscCallMPI(MPIU_Allreduce(localDiff, diff, Nf, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
1837:   PetscCall(PetscFree(localDiff));
1838:   for (f = 0; f < Nf; ++f) diff[f] = PetscSqrtReal(diff[f]);
1839:   PetscFunctionReturn(PETSC_SUCCESS);
1840: }

1842: /*@C
1843:   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.

1845:   Collective

1847:   Input Parameters:
1848: + dm    - The `DM`
1849: . time  - The time
1850: . funcs - The functions to evaluate for each field component: `NULL` means that component does not contribute to error calculation
1851: . ctxs  - Optional array of contexts to pass to each function, or `NULL`.
1852: - X     - The coefficient vector u_h

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

1857:   Level: developer

1859: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
1860: @*/
1861: PetscErrorCode DMPlexComputeL2DiffVec(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, Vec D)
1862: {
1863:   PetscSection     section;
1864:   PetscQuadrature  quad;
1865:   Vec              localX;
1866:   PetscFEGeom      fegeom;
1867:   PetscScalar     *funcVal, *interpolant;
1868:   PetscReal       *coords;
1869:   const PetscReal *quadPoints, *quadWeights;
1870:   PetscInt         dim, coordDim, numFields, numComponents = 0, qNc, Nq, cStart, cEnd, c, field, fieldOffset;

1872:   PetscFunctionBegin;
1873:   PetscCall(VecSet(D, 0.0));
1874:   PetscCall(DMGetDimension(dm, &dim));
1875:   PetscCall(DMGetCoordinateDim(dm, &coordDim));
1876:   PetscCall(DMGetLocalSection(dm, &section));
1877:   PetscCall(PetscSectionGetNumFields(section, &numFields));
1878:   PetscCall(DMGetLocalVector(dm, &localX));
1879:   PetscCall(DMProjectFunctionLocal(dm, time, funcs, ctxs, INSERT_BC_VALUES, localX));
1880:   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
1881:   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
1882:   for (field = 0; field < numFields; ++field) {
1883:     PetscObject  obj;
1884:     PetscClassId id;
1885:     PetscInt     Nc;

1887:     PetscCall(DMGetField(dm, field, NULL, &obj));
1888:     PetscCall(PetscObjectGetClassId(obj, &id));
1889:     if (id == PETSCFE_CLASSID) {
1890:       PetscFE fe = (PetscFE)obj;

1892:       PetscCall(PetscFEGetQuadrature(fe, &quad));
1893:       PetscCall(PetscFEGetNumComponents(fe, &Nc));
1894:     } else if (id == PETSCFV_CLASSID) {
1895:       PetscFV fv = (PetscFV)obj;

1897:       PetscCall(PetscFVGetQuadrature(fv, &quad));
1898:       PetscCall(PetscFVGetNumComponents(fv, &Nc));
1899:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1900:     numComponents += Nc;
1901:   }
1902:   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
1903:   PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
1904:   PetscCall(PetscMalloc6(numComponents, &funcVal, numComponents, &interpolant, coordDim * Nq, &coords, Nq, &fegeom.detJ, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ));
1905:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
1906:   for (c = cStart; c < cEnd; ++c) {
1907:     PetscScalar *x        = NULL;
1908:     PetscScalar  elemDiff = 0.0;
1909:     PetscInt     qc       = 0;

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

1914:     for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1915:       PetscObject  obj;
1916:       PetscClassId id;
1917:       void *const  ctx = ctxs ? ctxs[field] : NULL;
1918:       PetscInt     Nb, Nc, q, fc;

1920:       PetscCall(DMGetField(dm, field, NULL, &obj));
1921:       PetscCall(PetscObjectGetClassId(obj, &id));
1922:       if (id == PETSCFE_CLASSID) {
1923:         PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
1924:         PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
1925:       } else if (id == PETSCFV_CLASSID) {
1926:         PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
1927:         Nb = 1;
1928:       } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1929:       if (funcs[field]) {
1930:         for (q = 0; q < Nq; ++q) {
1931:           PetscFEGeom qgeom;

1933:           qgeom.dimEmbed = fegeom.dimEmbed;
1934:           qgeom.J        = &fegeom.J[q * coordDim * coordDim];
1935:           qgeom.invJ     = &fegeom.invJ[q * coordDim * coordDim];
1936:           qgeom.detJ     = &fegeom.detJ[q];
1937:           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);
1938:           PetscCall((*funcs[field])(coordDim, time, &coords[q * coordDim], Nc, funcVal, ctx));
1939: #if defined(needs_fix_with_return_code_argument)
1940:           if (ierr) {
1941:             PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1942:             PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1943:             PetscCall(DMRestoreLocalVector(dm, &localX));
1944:           }
1945: #endif
1946:           if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[fieldOffset], &qgeom, q, interpolant));
1947:           else if (id == PETSCFV_CLASSID) PetscCall(PetscFVInterpolate_Static((PetscFV)obj, &x[fieldOffset], q, interpolant));
1948:           else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1949:           for (fc = 0; fc < Nc; ++fc) {
1950:             const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
1951:             elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
1952:           }
1953:         }
1954:       }
1955:       fieldOffset += Nb;
1956:       qc += Nc;
1957:     }
1958:     PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1959:     PetscCall(VecSetValue(D, c - cStart, elemDiff, INSERT_VALUES));
1960:   }
1961:   PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1962:   PetscCall(DMRestoreLocalVector(dm, &localX));
1963:   PetscCall(VecSqrtAbs(D));
1964:   PetscFunctionReturn(PETSC_SUCCESS);
1965: }

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

1970:   Collective

1972:   Input Parameters:
1973: + lu  - The local `Vec` containing the primal solution
1974: . f   - The field number for the potential
1975: . lmu - The local `Vec` containing the mixed solution
1976: - mf  - The field number for the flux

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

1981:   Level: advanced

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

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

1988: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeL2FluxDiffVec()`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
1989: @*/
1990: PetscErrorCode DMPlexComputeL2FluxDiffVecLocal(Vec lu, PetscInt f, Vec lmu, PetscInt mf, Vec eFlux)
1991: {
1992:   DM               dm, mdm, edm;
1993:   PetscFE          fe, mfe;
1994:   PetscFEGeom      fegeom;
1995:   PetscQuadrature  quad;
1996:   const PetscReal *quadWeights;
1997:   PetscReal       *coords;
1998:   PetscScalar     *interpolant, *minterpolant, *earray;
1999:   PetscInt         cdim, mcdim, cStart, cEnd, Nc, mNc, qNc, Nq;
2000:   MPI_Comm         comm;

2002:   PetscFunctionBegin;
2003:   PetscCall(VecGetDM(lu, &dm));
2004:   PetscCall(VecGetDM(lmu, &mdm));
2005:   PetscCall(VecGetDM(eFlux, &edm));
2006:   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
2007:   PetscCall(VecSet(eFlux, 0.0));

2009:   // Check if the both problems are on the same mesh
2010:   PetscCall(DMGetCoordinateDim(dm, &cdim));
2011:   PetscCall(DMGetCoordinateDim(mdm, &mcdim));
2012:   PetscCheck(cdim == mcdim, comm, PETSC_ERR_ARG_SIZ, "primal coordinate Dim %" PetscInt_FMT " != %" PetscInt_FMT " mixed coordinate Dim", cdim, mcdim);
2013:   fegeom.dimEmbed = cdim;

2015:   PetscCall(DMGetField(dm, f, NULL, (PetscObject *)&fe));
2016:   PetscCall(DMGetField(mdm, mf, NULL, (PetscObject *)&mfe));
2017:   PetscCall(PetscFEGetNumComponents(fe, &Nc));
2018:   PetscCall(PetscFEGetNumComponents(mfe, &mNc));
2019:   PetscCall(PetscFEGetQuadrature(fe, &quad));
2020:   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, NULL, &quadWeights));
2021:   PetscCheck(qNc == 1 || qNc == mNc, comm, PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, mNc);

2023:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
2024:   PetscCall(VecGetArrayWrite(eFlux, &earray));
2025:   PetscCall(PetscMalloc6(Nc * cdim, &interpolant, mNc * cdim, &minterpolant, cdim * (Nq + 1), &coords, cdim * cdim * Nq, &fegeom.J, cdim * cdim * Nq, &fegeom.invJ, Nq, &fegeom.detJ));
2026:   for (PetscInt c = cStart; c < cEnd; ++c) {
2027:     PetscScalar *x            = NULL;
2028:     PetscScalar *mx           = NULL;
2029:     PetscScalar *eval         = NULL;
2030:     PetscReal    fluxElemDiff = 0.0;

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

2036:     for (PetscInt q = 0; q < Nq; ++q) {
2037:       PetscFEGeom qgeom;

2039:       qgeom.dimEmbed = fegeom.dimEmbed;
2040:       qgeom.J        = &fegeom.J[q * cdim * cdim];
2041:       qgeom.invJ     = &fegeom.invJ[q * cdim * cdim];
2042:       qgeom.detJ     = &fegeom.detJ[q];

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

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

2049:       /* Now take the elementwise difference and store that in a vector. */
2050:       for (PetscInt fc = 0; fc < mNc; ++fc) {
2051:         const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : fc)];
2052:         fluxElemDiff += PetscSqr(PetscRealPart(interpolant[fc] - minterpolant[fc])) * wt * fegeom.detJ[q];
2053:       }
2054:     }
2055:     PetscCall(DMPlexVecRestoreClosure(dm, NULL, lu, c, NULL, &x));
2056:     PetscCall(DMPlexVecRestoreClosure(mdm, NULL, lmu, c, NULL, &mx));
2057:     PetscCall(DMPlexPointGlobalRef(edm, c, earray, (void *)&eval));
2058:     if (eval) eval[0] = fluxElemDiff;
2059:   }
2060:   PetscCall(PetscFree6(interpolant, minterpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
2061:   PetscCall(VecRestoreArrayWrite(eFlux, &earray));

2063:   PetscCall(VecAssemblyBegin(eFlux));
2064:   PetscCall(VecAssemblyEnd(eFlux));
2065:   PetscCall(VecSqrtAbs(eFlux));
2066:   PetscFunctionReturn(PETSC_SUCCESS);
2067: }

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

2072:   Collective

2074:   Input Parameters:
2075: + u  - The global `Vec` containing the primal solution
2076: . f  - The field number for the potential
2077: . mu - The global `Vec` containing the mixed solution
2078: - mf - The field number for the flux

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

2083:   Level: advanced

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

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

2090: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeL2FluxDiffVecLocal()`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
2091: @*/
2092: PetscErrorCode DMPlexComputeL2FluxDiffVec(Vec u, PetscInt f, Vec mu, PetscInt mf, Vec eFlux)
2093: {
2094:   DM  dm, mdm;
2095:   Vec lu, lmu;

2097:   PetscFunctionBegin;
2098:   PetscCall(VecGetDM(u, &dm));
2099:   PetscCall(DMGetLocalVector(dm, &lu));
2100:   PetscCall(DMGlobalToLocal(dm, u, INSERT_VALUES, lu));
2101:   PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, lu, 0.0, NULL, NULL, NULL));

2103:   PetscCall(VecGetDM(mu, &mdm));
2104:   PetscCall(DMGetLocalVector(mdm, &lmu));
2105:   PetscCall(DMGlobalToLocal(mdm, mu, INSERT_VALUES, lmu));
2106:   PetscCall(DMPlexInsertBoundaryValues(mdm, PETSC_TRUE, lmu, 0.0, NULL, NULL, NULL));

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

2110:   PetscCall(DMRestoreLocalVector(dm, &lu));
2111:   PetscCall(DMRestoreLocalVector(mdm, &lmu));
2112:   PetscFunctionReturn(PETSC_SUCCESS);
2113: }

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

2118:   Collective

2120:   Input Parameters:
2121: + dm   - The `DM`
2122: - locX - The coefficient vector u_h

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

2127:   Level: developer

2129:   Note:
2130:   $ 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

2132: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
2133: @*/
2134: PetscErrorCode DMPlexComputeClementInterpolant(DM dm, Vec locX, Vec locC)
2135: {
2136:   PetscInt         debug = ((DM_Plex *)dm->data)->printFEM;
2137:   DM               dmc;
2138:   PetscQuadrature  quad;
2139:   PetscScalar     *interpolant, *valsum;
2140:   PetscFEGeom      fegeom;
2141:   PetscReal       *coords;
2142:   const PetscReal *quadPoints, *quadWeights;
2143:   PetscInt         dim, cdim, Nf, f, Nc = 0, Nq, qNc, cStart, cEnd, vStart, vEnd, v;

2145:   PetscFunctionBegin;
2146:   PetscCall(PetscCitationsRegister(ClementCitation, &Clementcite));
2147:   PetscCall(VecGetDM(locC, &dmc));
2148:   PetscCall(VecSet(locC, 0.0));
2149:   PetscCall(DMGetDimension(dm, &dim));
2150:   PetscCall(DMGetCoordinateDim(dm, &cdim));
2151:   fegeom.dimEmbed = cdim;
2152:   PetscCall(DMGetNumFields(dm, &Nf));
2153:   PetscCheck(Nf > 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fields is zero!");
2154:   for (f = 0; f < Nf; ++f) {
2155:     PetscObject  obj;
2156:     PetscClassId id;
2157:     PetscInt     fNc;

2159:     PetscCall(DMGetField(dm, f, NULL, &obj));
2160:     PetscCall(PetscObjectGetClassId(obj, &id));
2161:     if (id == PETSCFE_CLASSID) {
2162:       PetscFE fe = (PetscFE)obj;

2164:       PetscCall(PetscFEGetQuadrature(fe, &quad));
2165:       PetscCall(PetscFEGetNumComponents(fe, &fNc));
2166:     } else if (id == PETSCFV_CLASSID) {
2167:       PetscFV fv = (PetscFV)obj;

2169:       PetscCall(PetscFVGetQuadrature(fv, &quad));
2170:       PetscCall(PetscFVGetNumComponents(fv, &fNc));
2171:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
2172:     Nc += fNc;
2173:   }
2174:   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
2175:   PetscCheck(qNc == 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " > 1", qNc);
2176:   PetscCall(PetscMalloc6(Nc * 2, &valsum, Nc, &interpolant, cdim * Nq, &coords, Nq, &fegeom.detJ, cdim * cdim * Nq, &fegeom.J, cdim * cdim * Nq, &fegeom.invJ));
2177:   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
2178:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
2179:   for (v = vStart; v < vEnd; ++v) {
2180:     PetscScalar volsum = 0.0;
2181:     PetscInt   *star   = NULL;
2182:     PetscInt    starSize, st, fc;

2184:     PetscCall(PetscArrayzero(valsum, Nc));
2185:     PetscCall(DMPlexGetTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
2186:     for (st = 0; st < starSize * 2; st += 2) {
2187:       const PetscInt cell = star[st];
2188:       PetscScalar   *val  = &valsum[Nc];
2189:       PetscScalar   *x    = NULL;
2190:       PetscReal      vol  = 0.0;
2191:       PetscInt       foff = 0;

2193:       if ((cell < cStart) || (cell >= cEnd)) continue;
2194:       PetscCall(DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
2195:       PetscCall(DMPlexVecGetClosure(dm, NULL, locX, cell, NULL, &x));
2196:       for (f = 0; f < Nf; ++f) {
2197:         PetscObject  obj;
2198:         PetscClassId id;
2199:         PetscInt     Nb, fNc, q;

2201:         PetscCall(PetscArrayzero(val, Nc));
2202:         PetscCall(DMGetField(dm, f, NULL, &obj));
2203:         PetscCall(PetscObjectGetClassId(obj, &id));
2204:         if (id == PETSCFE_CLASSID) {
2205:           PetscCall(PetscFEGetNumComponents((PetscFE)obj, &fNc));
2206:           PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
2207:         } else if (id == PETSCFV_CLASSID) {
2208:           PetscCall(PetscFVGetNumComponents((PetscFV)obj, &fNc));
2209:           Nb = 1;
2210:         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
2211:         for (q = 0; q < Nq; ++q) {
2212:           const PetscReal wt = quadWeights[q] * fegeom.detJ[q];
2213:           PetscFEGeom     qgeom;

2215:           qgeom.dimEmbed = fegeom.dimEmbed;
2216:           qgeom.J        = &fegeom.J[q * cdim * cdim];
2217:           qgeom.invJ     = &fegeom.invJ[q * cdim * cdim];
2218:           qgeom.detJ     = &fegeom.detJ[q];
2219:           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);
2220:           if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[foff], &qgeom, q, interpolant));
2221:           else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
2222:           for (fc = 0; fc < fNc; ++fc) val[foff + fc] += interpolant[fc] * wt;
2223:           vol += wt;
2224:         }
2225:         foff += Nb;
2226:       }
2227:       PetscCall(DMPlexVecRestoreClosure(dm, NULL, locX, cell, NULL, &x));
2228:       for (fc = 0; fc < Nc; ++fc) valsum[fc] += val[fc];
2229:       volsum += vol;
2230:       if (debug) {
2231:         PetscCall(PetscPrintf(PETSC_COMM_SELF, "Vertex %" PetscInt_FMT " Cell %" PetscInt_FMT " value: [", v, cell));
2232:         for (fc = 0; fc < Nc; ++fc) {
2233:           if (fc) PetscCall(PetscPrintf(PETSC_COMM_SELF, ", "));
2234:           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%g", (double)PetscRealPart(val[fc])));
2235:         }
2236:         PetscCall(PetscPrintf(PETSC_COMM_SELF, "]\n"));
2237:       }
2238:     }
2239:     for (fc = 0; fc < Nc; ++fc) valsum[fc] /= volsum;
2240:     PetscCall(DMPlexRestoreTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
2241:     PetscCall(DMPlexVecSetClosure(dmc, NULL, locC, v, valsum, INSERT_VALUES));
2242:   }
2243:   PetscCall(PetscFree6(valsum, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
2244:   PetscFunctionReturn(PETSC_SUCCESS);
2245: }

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

2250:   Collective

2252:   Input Parameters:
2253: + dm   - The `DM`
2254: - locX - The coefficient vector u_h

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

2259:   Level: developer

2261:   Note:
2262:   $\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

2264: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
2265: @*/
2266: PetscErrorCode DMPlexComputeGradientClementInterpolant(DM dm, Vec locX, Vec locC)
2267: {
2268:   DM_Plex         *mesh  = (DM_Plex *)dm->data;
2269:   PetscInt         debug = mesh->printFEM;
2270:   DM               dmC;
2271:   PetscQuadrature  quad;
2272:   PetscScalar     *interpolant, *gradsum;
2273:   PetscFEGeom      fegeom;
2274:   PetscReal       *coords;
2275:   const PetscReal *quadPoints, *quadWeights;
2276:   PetscInt         dim, coordDim, numFields, numComponents = 0, qNc, Nq, cStart, cEnd, vStart, vEnd, v, field, fieldOffset;

2278:   PetscFunctionBegin;
2279:   PetscCall(PetscCitationsRegister(ClementCitation, &Clementcite));
2280:   PetscCall(VecGetDM(locC, &dmC));
2281:   PetscCall(VecSet(locC, 0.0));
2282:   PetscCall(DMGetDimension(dm, &dim));
2283:   PetscCall(DMGetCoordinateDim(dm, &coordDim));
2284:   fegeom.dimEmbed = coordDim;
2285:   PetscCall(DMGetNumFields(dm, &numFields));
2286:   PetscCheck(numFields, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fields is zero!");
2287:   for (field = 0; field < numFields; ++field) {
2288:     PetscObject  obj;
2289:     PetscClassId id;
2290:     PetscInt     Nc;

2292:     PetscCall(DMGetField(dm, field, NULL, &obj));
2293:     PetscCall(PetscObjectGetClassId(obj, &id));
2294:     if (id == PETSCFE_CLASSID) {
2295:       PetscFE fe = (PetscFE)obj;

2297:       PetscCall(PetscFEGetQuadrature(fe, &quad));
2298:       PetscCall(PetscFEGetNumComponents(fe, &Nc));
2299:     } else if (id == PETSCFV_CLASSID) {
2300:       PetscFV fv = (PetscFV)obj;

2302:       PetscCall(PetscFVGetQuadrature(fv, &quad));
2303:       PetscCall(PetscFVGetNumComponents(fv, &Nc));
2304:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
2305:     numComponents += Nc;
2306:   }
2307:   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
2308:   PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
2309:   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));
2310:   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
2311:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
2312:   for (v = vStart; v < vEnd; ++v) {
2313:     PetscScalar volsum = 0.0;
2314:     PetscInt   *star   = NULL;
2315:     PetscInt    starSize, st, d, fc;

2317:     PetscCall(PetscArrayzero(gradsum, coordDim * numComponents));
2318:     PetscCall(DMPlexGetTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
2319:     for (st = 0; st < starSize * 2; st += 2) {
2320:       const PetscInt cell = star[st];
2321:       PetscScalar   *grad = &gradsum[coordDim * numComponents];
2322:       PetscScalar   *x    = NULL;
2323:       PetscReal      vol  = 0.0;

2325:       if ((cell < cStart) || (cell >= cEnd)) continue;
2326:       PetscCall(DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
2327:       PetscCall(DMPlexVecGetClosure(dm, NULL, locX, cell, NULL, &x));
2328:       for (field = 0, fieldOffset = 0; field < numFields; ++field) {
2329:         PetscObject  obj;
2330:         PetscClassId id;
2331:         PetscInt     Nb, Nc, q, qc = 0;

2333:         PetscCall(PetscArrayzero(grad, coordDim * numComponents));
2334:         PetscCall(DMGetField(dm, field, NULL, &obj));
2335:         PetscCall(PetscObjectGetClassId(obj, &id));
2336:         if (id == PETSCFE_CLASSID) {
2337:           PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
2338:           PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
2339:         } else if (id == PETSCFV_CLASSID) {
2340:           PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
2341:           Nb = 1;
2342:         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
2343:         for (q = 0; q < Nq; ++q) {
2344:           PetscFEGeom qgeom;

2346:           qgeom.dimEmbed = fegeom.dimEmbed;
2347:           qgeom.J        = &fegeom.J[q * coordDim * coordDim];
2348:           qgeom.invJ     = &fegeom.invJ[q * coordDim * coordDim];
2349:           qgeom.detJ     = &fegeom.detJ[q];
2350:           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);
2351:           if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolateGradient_Static((PetscFE)obj, 1, &x[fieldOffset], &qgeom, q, interpolant));
2352:           else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
2353:           for (fc = 0; fc < Nc; ++fc) {
2354:             const PetscReal wt = quadWeights[q * qNc + qc];

2356:             for (d = 0; d < coordDim; ++d) grad[fc * coordDim + d] += interpolant[fc * dim + d] * wt * fegeom.detJ[q];
2357:           }
2358:           vol += quadWeights[q * qNc] * fegeom.detJ[q];
2359:         }
2360:         fieldOffset += Nb;
2361:         qc += Nc;
2362:       }
2363:       PetscCall(DMPlexVecRestoreClosure(dm, NULL, locX, cell, NULL, &x));
2364:       for (fc = 0; fc < numComponents; ++fc) {
2365:         for (d = 0; d < coordDim; ++d) gradsum[fc * coordDim + d] += grad[fc * coordDim + d];
2366:       }
2367:       volsum += vol;
2368:       if (debug) {
2369:         PetscCall(PetscPrintf(PETSC_COMM_SELF, "Vertex %" PetscInt_FMT " Cell %" PetscInt_FMT " gradient: [", v, cell));
2370:         for (fc = 0; fc < numComponents; ++fc) {
2371:           for (d = 0; d < coordDim; ++d) {
2372:             if (fc || d > 0) PetscCall(PetscPrintf(PETSC_COMM_SELF, ", "));
2373:             PetscCall(PetscPrintf(PETSC_COMM_SELF, "%g", (double)PetscRealPart(grad[fc * coordDim + d])));
2374:           }
2375:         }
2376:         PetscCall(PetscPrintf(PETSC_COMM_SELF, "]\n"));
2377:       }
2378:     }
2379:     for (fc = 0; fc < numComponents; ++fc) {
2380:       for (d = 0; d < coordDim; ++d) gradsum[fc * coordDim + d] /= volsum;
2381:     }
2382:     PetscCall(DMPlexRestoreTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
2383:     PetscCall(DMPlexVecSetClosure(dmC, NULL, locC, v, gradsum, INSERT_VALUES));
2384:   }
2385:   PetscCall(PetscFree6(gradsum, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
2386:   PetscFunctionReturn(PETSC_SUCCESS);
2387: }

2389: PetscErrorCode DMPlexComputeIntegral_Internal(DM dm, Vec locX, PetscInt cStart, PetscInt cEnd, PetscScalar *cintegral, void *user)
2390: {
2391:   DM           dmAux = NULL, plexA = NULL;
2392:   PetscDS      prob, probAux       = NULL;
2393:   PetscSection section, sectionAux;
2394:   Vec          locA;
2395:   PetscInt     dim, numCells = cEnd - cStart, c, f;
2396:   PetscBool    useFVM = PETSC_FALSE;
2397:   /* DS */
2398:   PetscInt           Nf, totDim, *uOff, *uOff_x, numConstants;
2399:   PetscInt           NfAux, totDimAux, *aOff;
2400:   PetscScalar       *u, *a = NULL;
2401:   const PetscScalar *constants;
2402:   /* Geometry */
2403:   PetscFEGeom       *cgeomFEM;
2404:   DM                 dmGrad;
2405:   PetscQuadrature    affineQuad      = NULL;
2406:   Vec                cellGeometryFVM = NULL, faceGeometryFVM = NULL, locGrad = NULL;
2407:   PetscFVCellGeom   *cgeomFVM;
2408:   const PetscScalar *lgrad;
2409:   PetscInt           maxDegree;
2410:   DMField            coordField;
2411:   IS                 cellIS;

2413:   PetscFunctionBegin;
2414:   PetscCall(DMGetDS(dm, &prob));
2415:   PetscCall(DMGetDimension(dm, &dim));
2416:   PetscCall(DMGetLocalSection(dm, &section));
2417:   PetscCall(DMGetNumFields(dm, &Nf));
2418:   /* Determine which discretizations we have */
2419:   for (f = 0; f < Nf; ++f) {
2420:     PetscObject  obj;
2421:     PetscClassId id;

2423:     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
2424:     PetscCall(PetscObjectGetClassId(obj, &id));
2425:     if (id == PETSCFV_CLASSID) useFVM = PETSC_TRUE;
2426:   }
2427:   /* Read DS information */
2428:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
2429:   PetscCall(PetscDSGetComponentOffsets(prob, &uOff));
2430:   PetscCall(PetscDSGetComponentDerivativeOffsets(prob, &uOff_x));
2431:   PetscCall(ISCreateStride(PETSC_COMM_SELF, numCells, cStart, 1, &cellIS));
2432:   PetscCall(PetscDSGetConstants(prob, &numConstants, &constants));
2433:   /* Read Auxiliary DS information */
2434:   PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA));
2435:   if (locA) {
2436:     PetscCall(VecGetDM(locA, &dmAux));
2437:     PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
2438:     PetscCall(DMGetDS(dmAux, &probAux));
2439:     PetscCall(PetscDSGetNumFields(probAux, &NfAux));
2440:     PetscCall(DMGetLocalSection(dmAux, &sectionAux));
2441:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
2442:     PetscCall(PetscDSGetComponentOffsets(probAux, &aOff));
2443:   }
2444:   /* Allocate data  arrays */
2445:   PetscCall(PetscCalloc1(numCells * totDim, &u));
2446:   if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
2447:   /* Read out geometry */
2448:   PetscCall(DMGetCoordinateField(dm, &coordField));
2449:   PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
2450:   if (maxDegree <= 1) {
2451:     PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad));
2452:     if (affineQuad) PetscCall(DMFieldCreateFEGeom(coordField, cellIS, affineQuad, PETSC_FEGEOM_BASIC, &cgeomFEM));
2453:   }
2454:   if (useFVM) {
2455:     PetscFV   fv = NULL;
2456:     Vec       grad;
2457:     PetscInt  fStart, fEnd;
2458:     PetscBool compGrad;

2460:     for (f = 0; f < Nf; ++f) {
2461:       PetscObject  obj;
2462:       PetscClassId id;

2464:       PetscCall(PetscDSGetDiscretization(prob, f, &obj));
2465:       PetscCall(PetscObjectGetClassId(obj, &id));
2466:       if (id == PETSCFV_CLASSID) {
2467:         fv = (PetscFV)obj;
2468:         break;
2469:       }
2470:     }
2471:     PetscCall(PetscFVGetComputeGradients(fv, &compGrad));
2472:     PetscCall(PetscFVSetComputeGradients(fv, PETSC_TRUE));
2473:     PetscCall(DMPlexComputeGeometryFVM(dm, &cellGeometryFVM, &faceGeometryFVM));
2474:     PetscCall(DMPlexComputeGradientFVM(dm, fv, faceGeometryFVM, cellGeometryFVM, &dmGrad));
2475:     PetscCall(PetscFVSetComputeGradients(fv, compGrad));
2476:     PetscCall(VecGetArrayRead(cellGeometryFVM, (const PetscScalar **)&cgeomFVM));
2477:     /* Reconstruct and limit cell gradients */
2478:     PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
2479:     PetscCall(DMGetGlobalVector(dmGrad, &grad));
2480:     PetscCall(DMPlexReconstructGradients_Internal(dm, fv, fStart, fEnd, faceGeometryFVM, cellGeometryFVM, locX, grad));
2481:     /* Communicate gradient values */
2482:     PetscCall(DMGetLocalVector(dmGrad, &locGrad));
2483:     PetscCall(DMGlobalToLocalBegin(dmGrad, grad, INSERT_VALUES, locGrad));
2484:     PetscCall(DMGlobalToLocalEnd(dmGrad, grad, INSERT_VALUES, locGrad));
2485:     PetscCall(DMRestoreGlobalVector(dmGrad, &grad));
2486:     /* Handle non-essential (e.g. outflow) boundary values */
2487:     PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_FALSE, locX, 0.0, faceGeometryFVM, cellGeometryFVM, locGrad));
2488:     PetscCall(VecGetArrayRead(locGrad, &lgrad));
2489:   }
2490:   /* Read out data from inputs */
2491:   for (c = cStart; c < cEnd; ++c) {
2492:     PetscScalar *x = NULL;
2493:     PetscInt     i;

2495:     PetscCall(DMPlexVecGetClosure(dm, section, locX, c, NULL, &x));
2496:     for (i = 0; i < totDim; ++i) u[c * totDim + i] = x[i];
2497:     PetscCall(DMPlexVecRestoreClosure(dm, section, locX, c, NULL, &x));
2498:     if (dmAux) {
2499:       PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, c, NULL, &x));
2500:       for (i = 0; i < totDimAux; ++i) a[c * totDimAux + i] = x[i];
2501:       PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, c, NULL, &x));
2502:     }
2503:   }
2504:   /* Do integration for each field */
2505:   for (f = 0; f < Nf; ++f) {
2506:     PetscObject  obj;
2507:     PetscClassId id;
2508:     PetscInt     numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;

2510:     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
2511:     PetscCall(PetscObjectGetClassId(obj, &id));
2512:     if (id == PETSCFE_CLASSID) {
2513:       PetscFE         fe = (PetscFE)obj;
2514:       PetscQuadrature q;
2515:       PetscFEGeom    *chunkGeom = NULL;
2516:       PetscInt        Nq, Nb;

2518:       PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
2519:       PetscCall(PetscFEGetQuadrature(fe, &q));
2520:       PetscCall(PetscQuadratureGetData(q, NULL, NULL, &Nq, NULL, NULL));
2521:       PetscCall(PetscFEGetDimension(fe, &Nb));
2522:       blockSize = Nb * Nq;
2523:       batchSize = numBlocks * blockSize;
2524:       PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
2525:       numChunks = numCells / (numBatches * batchSize);
2526:       Ne        = numChunks * numBatches * batchSize;
2527:       Nr        = numCells % (numBatches * batchSize);
2528:       offset    = numCells - Nr;
2529:       if (!affineQuad) PetscCall(DMFieldCreateFEGeom(coordField, cellIS, q, PETSC_FEGEOM_BASIC, &cgeomFEM));
2530:       PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
2531:       PetscCall(PetscFEIntegrate(prob, f, Ne, chunkGeom, u, probAux, a, cintegral));
2532:       PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &chunkGeom));
2533:       PetscCall(PetscFEIntegrate(prob, f, Nr, chunkGeom, &u[offset * totDim], probAux, PetscSafePointerPlusOffset(a, offset * totDimAux), &cintegral[offset * Nf]));
2534:       PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &chunkGeom));
2535:       if (!affineQuad) PetscCall(PetscFEGeomDestroy(&cgeomFEM));
2536:     } else if (id == PETSCFV_CLASSID) {
2537:       PetscInt       foff;
2538:       PetscPointFunc obj_func;

2540:       PetscCall(PetscDSGetObjective(prob, f, &obj_func));
2541:       PetscCall(PetscDSGetFieldOffset(prob, f, &foff));
2542:       if (obj_func) {
2543:         for (c = 0; c < numCells; ++c) {
2544:           PetscScalar *u_x;
2545:           PetscScalar  lint = 0.;

2547:           PetscCall(DMPlexPointLocalRead(dmGrad, c, lgrad, &u_x));
2548:           obj_func(dim, Nf, NfAux, uOff, uOff_x, &u[totDim * c + foff], NULL, u_x, aOff, NULL, PetscSafePointerPlusOffset(a, totDimAux * c), NULL, NULL, 0.0, cgeomFVM[c].centroid, numConstants, constants, &lint);
2549:           cintegral[c * Nf + f] += PetscRealPart(lint) * cgeomFVM[c].volume;
2550:         }
2551:       }
2552:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
2553:   }
2554:   /* Cleanup data arrays */
2555:   if (useFVM) {
2556:     PetscCall(VecRestoreArrayRead(locGrad, &lgrad));
2557:     PetscCall(VecRestoreArrayRead(cellGeometryFVM, (const PetscScalar **)&cgeomFVM));
2558:     PetscCall(DMRestoreLocalVector(dmGrad, &locGrad));
2559:     PetscCall(VecDestroy(&faceGeometryFVM));
2560:     PetscCall(VecDestroy(&cellGeometryFVM));
2561:     PetscCall(DMDestroy(&dmGrad));
2562:   }
2563:   if (dmAux) PetscCall(PetscFree(a));
2564:   PetscCall(DMDestroy(&plexA));
2565:   PetscCall(PetscFree(u));
2566:   /* Cleanup */
2567:   if (affineQuad) PetscCall(PetscFEGeomDestroy(&cgeomFEM));
2568:   PetscCall(PetscQuadratureDestroy(&affineQuad));
2569:   PetscCall(ISDestroy(&cellIS));
2570:   PetscFunctionReturn(PETSC_SUCCESS);
2571: }

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

2576:   Input Parameters:
2577: + dm   - The mesh
2578: . X    - Global input vector
2579: - user - The user context

2581:   Output Parameter:
2582: . integral - Integral for each field

2584:   Level: developer

2586: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSNESComputeResidualFEM()`
2587: @*/
2588: PetscErrorCode DMPlexComputeIntegralFEM(DM dm, Vec X, PetscScalar *integral, void *user)
2589: {
2590:   PetscInt     printFEM;
2591:   PetscScalar *cintegral, *lintegral;
2592:   PetscInt     Nf, f, cellHeight, cStart, cEnd, cell;
2593:   Vec          locX;

2595:   PetscFunctionBegin;
2598:   PetscAssertPointer(integral, 3);
2599:   PetscCall(PetscLogEventBegin(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2600:   PetscCall(DMPlexConvertPlex(dm, &dm, PETSC_TRUE));
2601:   PetscCall(DMGetNumFields(dm, &Nf));
2602:   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
2603:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
2604:   /* TODO Introduce a loop over large chunks (right now this is a single chunk) */
2605:   PetscCall(PetscCalloc2(Nf, &lintegral, (cEnd - cStart) * Nf, &cintegral));
2606:   /* Get local solution with boundary values */
2607:   PetscCall(DMGetLocalVector(dm, &locX));
2608:   PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL));
2609:   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX));
2610:   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX));
2611:   PetscCall(DMPlexComputeIntegral_Internal(dm, locX, cStart, cEnd, cintegral, user));
2612:   PetscCall(DMRestoreLocalVector(dm, &locX));
2613:   printFEM = ((DM_Plex *)dm->data)->printFEM;
2614:   /* Sum up values */
2615:   for (cell = cStart; cell < cEnd; ++cell) {
2616:     const PetscInt c = cell - cStart;

2618:     if (printFEM > 1) PetscCall(DMPrintCellVector(cell, "Cell Integral", Nf, &cintegral[c * Nf]));
2619:     for (f = 0; f < Nf; ++f) lintegral[f] += cintegral[c * Nf + f];
2620:   }
2621:   PetscCallMPI(MPIU_Allreduce(lintegral, integral, Nf, MPIU_SCALAR, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
2622:   if (printFEM) {
2623:     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "Integral:"));
2624:     for (f = 0; f < Nf; ++f) PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), " %g", (double)PetscRealPart(integral[f])));
2625:     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "\n"));
2626:   }
2627:   PetscCall(PetscFree2(lintegral, cintegral));
2628:   PetscCall(PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2629:   PetscCall(DMDestroy(&dm));
2630:   PetscFunctionReturn(PETSC_SUCCESS);
2631: }

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

2636:   Input Parameters:
2637: + dm   - The mesh
2638: . X    - Global input vector
2639: - user - The user context

2641:   Output Parameter:
2642: . F - Cellwise integrals for each field

2644:   Level: developer

2646: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSNESComputeResidualFEM()`
2647: @*/
2648: PetscErrorCode DMPlexComputeCellwiseIntegralFEM(DM dm, Vec X, Vec F, void *user)
2649: {
2650:   PetscInt     printFEM;
2651:   DM           dmF;
2652:   PetscSection sectionF = NULL;
2653:   PetscScalar *cintegral, *af;
2654:   PetscInt     Nf, f, cellHeight, cStart, cEnd, cell, n;
2655:   Vec          locX;

2657:   PetscFunctionBegin;
2661:   PetscCall(PetscLogEventBegin(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2662:   PetscCall(DMPlexConvertPlex(dm, &dm, PETSC_TRUE));
2663:   PetscCall(DMGetNumFields(dm, &Nf));
2664:   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
2665:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
2666:   /* TODO Introduce a loop over large chunks (right now this is a single chunk) */
2667:   PetscCall(PetscCalloc1((cEnd - cStart) * Nf, &cintegral));
2668:   /* Get local solution with boundary values */
2669:   PetscCall(DMGetLocalVector(dm, &locX));
2670:   PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL));
2671:   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX));
2672:   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX));
2673:   PetscCall(DMPlexComputeIntegral_Internal(dm, locX, cStart, cEnd, cintegral, user));
2674:   PetscCall(DMRestoreLocalVector(dm, &locX));
2675:   /* Put values in F */
2676:   PetscCall(VecGetArray(F, &af));
2677:   PetscCall(VecGetDM(F, &dmF));
2678:   if (dmF) PetscCall(DMGetLocalSection(dmF, &sectionF));
2679:   PetscCall(VecGetLocalSize(F, &n));
2680:   PetscCheck(n >= (cEnd - cStart) * Nf, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Vector size %" PetscInt_FMT " < %" PetscInt_FMT, n, (cEnd - cStart) * Nf);
2681:   printFEM = ((DM_Plex *)dm->data)->printFEM;
2682:   for (cell = cStart; cell < cEnd; ++cell) {
2683:     const PetscInt c   = cell - cStart;
2684:     PetscInt       dof = Nf, off = c * Nf;

2686:     if (printFEM > 1) PetscCall(DMPrintCellVector(cell, "Cell Integral", Nf, &cintegral[c * Nf]));
2687:     if (sectionF) {
2688:       PetscCall(PetscSectionGetDof(sectionF, cell, &dof));
2689:       PetscCall(PetscSectionGetOffset(sectionF, cell, &off));
2690:     }
2691:     PetscCheck(dof == Nf, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "The number of cell dofs %" PetscInt_FMT " != %" PetscInt_FMT, dof, Nf);
2692:     for (f = 0; f < Nf; ++f) af[off + f] = cintegral[c * Nf + f];
2693:   }
2694:   PetscCall(VecRestoreArray(F, &af));
2695:   PetscCall(PetscFree(cintegral));
2696:   PetscCall(PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2697:   PetscCall(DMDestroy(&dm));
2698:   PetscFunctionReturn(PETSC_SUCCESS);
2699: }

2701: static PetscErrorCode DMPlexComputeBdIntegral_Internal(DM dm, Vec locX, IS pointIS, void (**funcs)(PetscInt, PetscInt, PetscInt, const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], PetscReal, const PetscReal[], const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]), PetscScalar *fintegral, void *user)
2702: {
2703:   DM                 plex = NULL, plexA = NULL;
2704:   DMEnclosureType    encAux;
2705:   PetscDS            prob, probAux       = NULL;
2706:   PetscSection       section, sectionAux = NULL;
2707:   Vec                locA = NULL;
2708:   DMField            coordField;
2709:   PetscInt           Nf, totDim, *uOff, *uOff_x;
2710:   PetscInt           NfAux = 0, totDimAux = 0, *aOff = NULL;
2711:   PetscScalar       *u, *a = NULL;
2712:   const PetscScalar *constants;
2713:   PetscInt           numConstants, f;

2715:   PetscFunctionBegin;
2716:   PetscCall(DMGetCoordinateField(dm, &coordField));
2717:   PetscCall(DMConvert(dm, DMPLEX, &plex));
2718:   PetscCall(DMGetDS(dm, &prob));
2719:   PetscCall(DMGetLocalSection(dm, &section));
2720:   PetscCall(PetscSectionGetNumFields(section, &Nf));
2721:   /* Determine which discretizations we have */
2722:   for (f = 0; f < Nf; ++f) {
2723:     PetscObject  obj;
2724:     PetscClassId id;

2726:     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
2727:     PetscCall(PetscObjectGetClassId(obj, &id));
2728:     PetscCheck(id != PETSCFV_CLASSID, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Not supported for FVM (field %" PetscInt_FMT ")", f);
2729:   }
2730:   /* Read DS information */
2731:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
2732:   PetscCall(PetscDSGetComponentOffsets(prob, &uOff));
2733:   PetscCall(PetscDSGetComponentDerivativeOffsets(prob, &uOff_x));
2734:   PetscCall(PetscDSGetConstants(prob, &numConstants, &constants));
2735:   /* Read Auxiliary DS information */
2736:   PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA));
2737:   if (locA) {
2738:     DM dmAux;

2740:     PetscCall(VecGetDM(locA, &dmAux));
2741:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
2742:     PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
2743:     PetscCall(DMGetDS(dmAux, &probAux));
2744:     PetscCall(PetscDSGetNumFields(probAux, &NfAux));
2745:     PetscCall(DMGetLocalSection(dmAux, &sectionAux));
2746:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
2747:     PetscCall(PetscDSGetComponentOffsets(probAux, &aOff));
2748:   }
2749:   /* Integrate over points */
2750:   {
2751:     PetscFEGeom    *fgeom, *chunkGeom = NULL;
2752:     PetscInt        maxDegree;
2753:     PetscQuadrature qGeom = NULL;
2754:     const PetscInt *points;
2755:     PetscInt        numFaces, face, Nq, field;
2756:     PetscInt        numChunks, chunkSize, chunk, Nr, offset;

2758:     PetscCall(ISGetLocalSize(pointIS, &numFaces));
2759:     PetscCall(ISGetIndices(pointIS, &points));
2760:     PetscCall(PetscCalloc2(numFaces * totDim, &u, (locA ? (size_t)numFaces * totDimAux : 0), &a));
2761:     PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
2762:     for (face = 0; face < numFaces; ++face) {
2763:       const PetscInt point = points[face], *support;
2764:       PetscScalar   *x     = NULL;

2766:       PetscCall(DMPlexGetSupport(dm, point, &support));
2767:       PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
2768:       for (PetscInt i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
2769:       PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
2770:       if (locA) {
2771:         PetscInt subp;
2772:         PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
2773:         PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
2774:         for (PetscInt i = 0; i < totDimAux; ++i) a[f * totDimAux + i] = x[i];
2775:         PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
2776:       }
2777:     }
2778:     for (field = 0; field < Nf; ++field) {
2779:       PetscFE fe;

2781:       PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fe));
2782:       if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom));
2783:       if (!qGeom) {
2784:         PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
2785:         PetscCall(PetscObjectReference((PetscObject)qGeom));
2786:       }
2787:       PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
2788:       PetscCall(DMPlexGetFEGeom(coordField, pointIS, qGeom, PETSC_FEGEOM_BOUNDARY, &fgeom));
2789:       /* Get blocking */
2790:       {
2791:         PetscQuadrature q;
2792:         PetscInt        numBatches, batchSize, numBlocks, blockSize;
2793:         PetscInt        Nq, Nb;

2795:         PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
2796:         PetscCall(PetscFEGetQuadrature(fe, &q));
2797:         PetscCall(PetscQuadratureGetData(q, NULL, NULL, &Nq, NULL, NULL));
2798:         PetscCall(PetscFEGetDimension(fe, &Nb));
2799:         blockSize = Nb * Nq;
2800:         batchSize = numBlocks * blockSize;
2801:         chunkSize = numBatches * batchSize;
2802:         PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
2803:         numChunks = numFaces / chunkSize;
2804:         Nr        = numFaces % chunkSize;
2805:         offset    = numFaces - Nr;
2806:       }
2807:       /* Do integration for each field */
2808:       for (chunk = 0; chunk < numChunks; ++chunk) {
2809:         PetscCall(PetscFEGeomGetChunk(fgeom, chunk * chunkSize, (chunk + 1) * chunkSize, &chunkGeom));
2810:         PetscCall(PetscFEIntegrateBd(prob, field, funcs[field], chunkSize, chunkGeom, &u[chunk * chunkSize * totDim], probAux, PetscSafePointerPlusOffset(a, chunk * chunkSize * totDimAux), &fintegral[chunk * chunkSize * Nf]));
2811:         PetscCall(PetscFEGeomRestoreChunk(fgeom, 0, offset, &chunkGeom));
2812:       }
2813:       PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom));
2814:       PetscCall(PetscFEIntegrateBd(prob, field, funcs[field], Nr, chunkGeom, &u[offset * totDim], probAux, PetscSafePointerPlusOffset(a, offset * totDimAux), &fintegral[offset * Nf]));
2815:       PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom));
2816:       /* Cleanup data arrays */
2817:       PetscCall(DMPlexRestoreFEGeom(coordField, pointIS, qGeom, PETSC_FEGEOM_BOUNDARY, &fgeom));
2818:       PetscCall(PetscQuadratureDestroy(&qGeom));
2819:     }
2820:     PetscCall(PetscFree2(u, a));
2821:     PetscCall(ISRestoreIndices(pointIS, &points));
2822:   }
2823:   if (plex) PetscCall(DMDestroy(&plex));
2824:   if (plexA) PetscCall(DMDestroy(&plexA));
2825:   PetscFunctionReturn(PETSC_SUCCESS);
2826: }

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

2831:   Input Parameters:
2832: + dm      - The mesh
2833: . X       - Global input vector
2834: . label   - The boundary `DMLabel`
2835: . numVals - The number of label values to use, or `PETSC_DETERMINE` for all values
2836: . vals    - The label values to use, or NULL for all values
2837: . funcs   - The functions to integrate along the boundary for each field
2838: - user    - The user context

2840:   Output Parameter:
2841: . integral - Integral for each field

2843:   Level: developer

2845: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeIntegralFEM()`, `DMPlexComputeBdResidualFEM()`
2846: @*/
2847: PetscErrorCode DMPlexComputeBdIntegral(DM dm, Vec X, DMLabel label, PetscInt numVals, const PetscInt vals[], void (**funcs)(PetscInt, PetscInt, PetscInt, const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], PetscReal, const PetscReal[], const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]), PetscScalar *integral, void *user)
2848: {
2849:   Vec          locX;
2850:   PetscSection section;
2851:   DMLabel      depthLabel;
2852:   IS           facetIS;
2853:   PetscInt     dim, Nf, f, v;

2855:   PetscFunctionBegin;
2859:   if (vals) PetscAssertPointer(vals, 5);
2860:   PetscAssertPointer(integral, 7);
2861:   PetscCall(PetscLogEventBegin(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2862:   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
2863:   PetscCall(DMGetDimension(dm, &dim));
2864:   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
2865:   PetscCall(DMGetLocalSection(dm, &section));
2866:   PetscCall(PetscSectionGetNumFields(section, &Nf));
2867:   /* Get local solution with boundary values */
2868:   PetscCall(DMGetLocalVector(dm, &locX));
2869:   PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL));
2870:   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX));
2871:   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX));
2872:   /* Loop over label values */
2873:   PetscCall(PetscArrayzero(integral, Nf));
2874:   for (v = 0; v < numVals; ++v) {
2875:     IS           pointIS;
2876:     PetscInt     numFaces, face;
2877:     PetscScalar *fintegral;

2879:     PetscCall(DMLabelGetStratumIS(label, vals[v], &pointIS));
2880:     if (!pointIS) continue; /* No points with that id on this process */
2881:     {
2882:       IS isectIS;

2884:       /* TODO: Special cases of ISIntersect where it is quick to check a priori if one is a superset of the other */
2885:       PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS));
2886:       PetscCall(ISDestroy(&pointIS));
2887:       pointIS = isectIS;
2888:     }
2889:     PetscCall(ISGetLocalSize(pointIS, &numFaces));
2890:     PetscCall(PetscCalloc1(numFaces * Nf, &fintegral));
2891:     PetscCall(DMPlexComputeBdIntegral_Internal(dm, locX, pointIS, funcs, fintegral, user));
2892:     /* Sum point contributions into integral */
2893:     for (f = 0; f < Nf; ++f)
2894:       for (face = 0; face < numFaces; ++face) integral[f] += fintegral[face * Nf + f];
2895:     PetscCall(PetscFree(fintegral));
2896:     PetscCall(ISDestroy(&pointIS));
2897:   }
2898:   PetscCall(DMRestoreLocalVector(dm, &locX));
2899:   PetscCall(ISDestroy(&facetIS));
2900:   PetscCall(PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2901:   PetscFunctionReturn(PETSC_SUCCESS);
2902: }

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

2907:   Input Parameters:
2908: + dmc       - The coarse mesh
2909: . dmf       - The fine mesh
2910: . isRefined - Flag indicating regular refinement, rather than the same topology
2911: - user      - The user context

2913:   Output Parameter:
2914: . In - The interpolation matrix

2916:   Level: developer

2918: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorGeneral()`
2919: @*/
2920: PetscErrorCode DMPlexComputeInterpolatorNested(DM dmc, DM dmf, PetscBool isRefined, Mat In, void *user)
2921: {
2922:   DM_Plex     *mesh = (DM_Plex *)dmc->data;
2923:   const char  *name = "Interpolator";
2924:   PetscFE     *feRef;
2925:   PetscFV     *fvRef;
2926:   PetscSection fsection, fglobalSection;
2927:   PetscSection csection, cglobalSection;
2928:   PetscScalar *elemMat;
2929:   PetscInt     dim, Nf, f, fieldI, fieldJ, offsetI, offsetJ, cStart, cEnd, c;
2930:   PetscInt     cTotDim = 0, rTotDim = 0;

2932:   PetscFunctionBegin;
2933:   PetscCall(PetscLogEventBegin(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
2934:   PetscCall(DMGetDimension(dmf, &dim));
2935:   PetscCall(DMGetLocalSection(dmf, &fsection));
2936:   PetscCall(DMGetGlobalSection(dmf, &fglobalSection));
2937:   PetscCall(DMGetLocalSection(dmc, &csection));
2938:   PetscCall(DMGetGlobalSection(dmc, &cglobalSection));
2939:   PetscCall(PetscSectionGetNumFields(fsection, &Nf));
2940:   PetscCall(DMPlexGetSimplexOrBoxCells(dmc, 0, &cStart, &cEnd));
2941:   PetscCall(PetscCalloc2(Nf, &feRef, Nf, &fvRef));
2942:   for (f = 0; f < Nf; ++f) {
2943:     PetscObject  obj, objc;
2944:     PetscClassId id, idc;
2945:     PetscInt     rNb = 0, Nc = 0, cNb = 0;

2947:     PetscCall(DMGetField(dmf, f, NULL, &obj));
2948:     PetscCall(PetscObjectGetClassId(obj, &id));
2949:     if (id == PETSCFE_CLASSID) {
2950:       PetscFE fe = (PetscFE)obj;

2952:       if (isRefined) {
2953:         PetscCall(PetscFERefine(fe, &feRef[f]));
2954:       } else {
2955:         PetscCall(PetscObjectReference((PetscObject)fe));
2956:         feRef[f] = fe;
2957:       }
2958:       PetscCall(PetscFEGetDimension(feRef[f], &rNb));
2959:       PetscCall(PetscFEGetNumComponents(fe, &Nc));
2960:     } else if (id == PETSCFV_CLASSID) {
2961:       PetscFV        fv = (PetscFV)obj;
2962:       PetscDualSpace Q;

2964:       if (isRefined) {
2965:         PetscCall(PetscFVRefine(fv, &fvRef[f]));
2966:       } else {
2967:         PetscCall(PetscObjectReference((PetscObject)fv));
2968:         fvRef[f] = fv;
2969:       }
2970:       PetscCall(PetscFVGetDualSpace(fvRef[f], &Q));
2971:       PetscCall(PetscDualSpaceGetDimension(Q, &rNb));
2972:       PetscCall(PetscFVGetDualSpace(fv, &Q));
2973:       PetscCall(PetscFVGetNumComponents(fv, &Nc));
2974:     }
2975:     PetscCall(DMGetField(dmc, f, NULL, &objc));
2976:     PetscCall(PetscObjectGetClassId(objc, &idc));
2977:     if (idc == PETSCFE_CLASSID) {
2978:       PetscFE fe = (PetscFE)objc;

2980:       PetscCall(PetscFEGetDimension(fe, &cNb));
2981:     } else if (id == PETSCFV_CLASSID) {
2982:       PetscFV        fv = (PetscFV)obj;
2983:       PetscDualSpace Q;

2985:       PetscCall(PetscFVGetDualSpace(fv, &Q));
2986:       PetscCall(PetscDualSpaceGetDimension(Q, &cNb));
2987:     }
2988:     rTotDim += rNb;
2989:     cTotDim += cNb;
2990:   }
2991:   PetscCall(PetscMalloc1(rTotDim * cTotDim, &elemMat));
2992:   PetscCall(PetscArrayzero(elemMat, rTotDim * cTotDim));
2993:   for (fieldI = 0, offsetI = 0; fieldI < Nf; ++fieldI) {
2994:     PetscDualSpace   Qref;
2995:     PetscQuadrature  f;
2996:     const PetscReal *qpoints, *qweights;
2997:     PetscReal       *points;
2998:     PetscInt         npoints = 0, Nc, Np, fpdim, i, k, p, d;

3000:     /* Compose points from all dual basis functionals */
3001:     if (feRef[fieldI]) {
3002:       PetscCall(PetscFEGetDualSpace(feRef[fieldI], &Qref));
3003:       PetscCall(PetscFEGetNumComponents(feRef[fieldI], &Nc));
3004:     } else {
3005:       PetscCall(PetscFVGetDualSpace(fvRef[fieldI], &Qref));
3006:       PetscCall(PetscFVGetNumComponents(fvRef[fieldI], &Nc));
3007:     }
3008:     PetscCall(PetscDualSpaceGetDimension(Qref, &fpdim));
3009:     for (i = 0; i < fpdim; ++i) {
3010:       PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
3011:       PetscCall(PetscQuadratureGetData(f, NULL, NULL, &Np, NULL, NULL));
3012:       npoints += Np;
3013:     }
3014:     PetscCall(PetscMalloc1(npoints * dim, &points));
3015:     for (i = 0, k = 0; i < fpdim; ++i) {
3016:       PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
3017:       PetscCall(PetscQuadratureGetData(f, NULL, NULL, &Np, &qpoints, NULL));
3018:       for (p = 0; p < Np; ++p, ++k)
3019:         for (d = 0; d < dim; ++d) points[k * dim + d] = qpoints[p * dim + d];
3020:     }

3022:     for (fieldJ = 0, offsetJ = 0; fieldJ < Nf; ++fieldJ) {
3023:       PetscObject  obj;
3024:       PetscClassId id;
3025:       PetscInt     NcJ = 0, cpdim = 0, j, qNc;

3027:       PetscCall(DMGetField(dmc, fieldJ, NULL, &obj));
3028:       PetscCall(PetscObjectGetClassId(obj, &id));
3029:       if (id == PETSCFE_CLASSID) {
3030:         PetscFE         fe = (PetscFE)obj;
3031:         PetscTabulation T  = NULL;

3033:         /* Evaluate basis at points */
3034:         PetscCall(PetscFEGetNumComponents(fe, &NcJ));
3035:         PetscCall(PetscFEGetDimension(fe, &cpdim));
3036:         /* For now, fields only interpolate themselves */
3037:         if (fieldI == fieldJ) {
3038:           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);
3039:           PetscCall(PetscFECreateTabulation(fe, 1, npoints, points, 0, &T));
3040:           for (i = 0, k = 0; i < fpdim; ++i) {
3041:             PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
3042:             PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, NULL, &qweights));
3043:             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);
3044:             for (p = 0; p < Np; ++p, ++k) {
3045:               for (j = 0; j < cpdim; ++j) {
3046:                 /*
3047:                    cTotDim:            Total columns in element interpolation matrix, sum of number of dual basis functionals in each field
3048:                    offsetI, offsetJ:   Offsets into the larger element interpolation matrix for different fields
3049:                    fpdim, i, cpdim, j: Dofs for fine and coarse grids, correspond to dual space basis functionals
3050:                    qNC, Nc, Ncj, c:    Number of components in this field
3051:                    Np, p:              Number of quad points in the fine grid functional i
3052:                    k:                  i*Np + p, overall point number for the interpolation
3053:                 */
3054:                 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];
3055:               }
3056:             }
3057:           }
3058:           PetscCall(PetscTabulationDestroy(&T));
3059:         }
3060:       } else if (id == PETSCFV_CLASSID) {
3061:         PetscFV fv = (PetscFV)obj;

3063:         /* Evaluate constant function at points */
3064:         PetscCall(PetscFVGetNumComponents(fv, &NcJ));
3065:         cpdim = 1;
3066:         /* For now, fields only interpolate themselves */
3067:         if (fieldI == fieldJ) {
3068:           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);
3069:           for (i = 0, k = 0; i < fpdim; ++i) {
3070:             PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
3071:             PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, NULL, &qweights));
3072:             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);
3073:             for (p = 0; p < Np; ++p, ++k) {
3074:               for (j = 0; j < cpdim; ++j) {
3075:                 for (c = 0; c < Nc; ++c) elemMat[(offsetI + i) * cTotDim + offsetJ + j] += 1.0 * qweights[p * qNc + c];
3076:               }
3077:             }
3078:           }
3079:         }
3080:       }
3081:       offsetJ += cpdim;
3082:     }
3083:     offsetI += fpdim;
3084:     PetscCall(PetscFree(points));
3085:   }
3086:   if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(0, name, rTotDim, cTotDim, elemMat));
3087:   /* Preallocate matrix */
3088:   {
3089:     Mat          preallocator;
3090:     PetscScalar *vals;
3091:     PetscInt    *cellCIndices, *cellFIndices;
3092:     PetscInt     locRows, locCols, cell;

3094:     PetscCall(MatGetLocalSize(In, &locRows, &locCols));
3095:     PetscCall(MatCreate(PetscObjectComm((PetscObject)In), &preallocator));
3096:     PetscCall(MatSetType(preallocator, MATPREALLOCATOR));
3097:     PetscCall(MatSetSizes(preallocator, locRows, locCols, PETSC_DETERMINE, PETSC_DETERMINE));
3098:     PetscCall(MatSetUp(preallocator));
3099:     PetscCall(PetscCalloc3(rTotDim * cTotDim, &vals, cTotDim, &cellCIndices, rTotDim, &cellFIndices));
3100:     for (cell = cStart; cell < cEnd; ++cell) {
3101:       if (isRefined) {
3102:         PetscCall(DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, cell, cellCIndices, cellFIndices));
3103:         PetscCall(MatSetValues(preallocator, rTotDim, cellFIndices, cTotDim, cellCIndices, vals, INSERT_VALUES));
3104:       } else {
3105:         PetscCall(DMPlexMatSetClosureGeneral(dmf, fsection, fglobalSection, PETSC_FALSE, dmc, csection, cglobalSection, PETSC_FALSE, preallocator, cell, vals, INSERT_VALUES));
3106:       }
3107:     }
3108:     PetscCall(PetscFree3(vals, cellCIndices, cellFIndices));
3109:     PetscCall(MatAssemblyBegin(preallocator, MAT_FINAL_ASSEMBLY));
3110:     PetscCall(MatAssemblyEnd(preallocator, MAT_FINAL_ASSEMBLY));
3111:     PetscCall(MatPreallocatorPreallocate(preallocator, PETSC_TRUE, In));
3112:     PetscCall(MatDestroy(&preallocator));
3113:   }
3114:   /* Fill matrix */
3115:   PetscCall(MatZeroEntries(In));
3116:   for (c = cStart; c < cEnd; ++c) {
3117:     if (isRefined) {
3118:       PetscCall(DMPlexMatSetClosureRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, In, c, elemMat, INSERT_VALUES));
3119:     } else {
3120:       PetscCall(DMPlexMatSetClosureGeneral(dmf, fsection, fglobalSection, PETSC_FALSE, dmc, csection, cglobalSection, PETSC_FALSE, In, c, elemMat, INSERT_VALUES));
3121:     }
3122:   }
3123:   for (f = 0; f < Nf; ++f) PetscCall(PetscFEDestroy(&feRef[f]));
3124:   PetscCall(PetscFree2(feRef, fvRef));
3125:   PetscCall(PetscFree(elemMat));
3126:   PetscCall(MatAssemblyBegin(In, MAT_FINAL_ASSEMBLY));
3127:   PetscCall(MatAssemblyEnd(In, MAT_FINAL_ASSEMBLY));
3128:   if (mesh->printFEM > 1) {
3129:     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)In), "%s:\n", name));
3130:     PetscCall(MatFilter(In, 1.0e-10, PETSC_FALSE, PETSC_FALSE));
3131:     PetscCall(MatView(In, NULL));
3132:   }
3133:   PetscCall(PetscLogEventEnd(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
3134:   PetscFunctionReturn(PETSC_SUCCESS);
3135: }

3137: PetscErrorCode DMPlexComputeMassMatrixNested(DM dmc, DM dmf, Mat mass, void *user)
3138: {
3139:   SETERRQ(PetscObjectComm((PetscObject)dmc), PETSC_ERR_SUP, "Laziness");
3140: }

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

3145:   Input Parameters:
3146: + dmf  - The fine mesh
3147: . dmc  - The coarse mesh
3148: - user - The user context

3150:   Output Parameter:
3151: . In - The interpolation matrix

3153:   Level: developer

3155: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorNested()`
3156: @*/
3157: PetscErrorCode DMPlexComputeInterpolatorGeneral(DM dmc, DM dmf, Mat In, void *user)
3158: {
3159:   DM_Plex     *mesh = (DM_Plex *)dmf->data;
3160:   const char  *name = "Interpolator";
3161:   PetscDS      prob;
3162:   Mat          interp;
3163:   PetscSection fsection, globalFSection;
3164:   PetscSection csection, globalCSection;
3165:   PetscInt     locRows, locCols;
3166:   PetscReal   *x, *v0, *J, *invJ, detJ;
3167:   PetscReal   *v0c, *Jc, *invJc, detJc;
3168:   PetscScalar *elemMat;
3169:   PetscInt     dim, Nf, field, totDim, cStart, cEnd, cell, ccell, s;

3171:   PetscFunctionBegin;
3172:   PetscCall(PetscLogEventBegin(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
3173:   PetscCall(DMGetCoordinateDim(dmc, &dim));
3174:   PetscCall(DMGetDS(dmc, &prob));
3175:   PetscCall(PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL));
3176:   PetscCall(PetscDSGetNumFields(prob, &Nf));
3177:   PetscCall(PetscMalloc3(dim, &v0, dim * dim, &J, dim * dim, &invJ));
3178:   PetscCall(PetscMalloc3(dim, &v0c, dim * dim, &Jc, dim * dim, &invJc));
3179:   PetscCall(DMGetLocalSection(dmf, &fsection));
3180:   PetscCall(DMGetGlobalSection(dmf, &globalFSection));
3181:   PetscCall(DMGetLocalSection(dmc, &csection));
3182:   PetscCall(DMGetGlobalSection(dmc, &globalCSection));
3183:   PetscCall(DMPlexGetSimplexOrBoxCells(dmf, 0, &cStart, &cEnd));
3184:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
3185:   PetscCall(PetscMalloc1(totDim, &elemMat));

3187:   PetscCall(MatGetLocalSize(In, &locRows, &locCols));
3188:   PetscCall(MatCreate(PetscObjectComm((PetscObject)In), &interp));
3189:   PetscCall(MatSetType(interp, MATPREALLOCATOR));
3190:   PetscCall(MatSetSizes(interp, locRows, locCols, PETSC_DETERMINE, PETSC_DETERMINE));
3191:   PetscCall(MatSetUp(interp));
3192:   for (s = 0; s < 2; ++s) {
3193:     for (field = 0; field < Nf; ++field) {
3194:       PetscObject      obj;
3195:       PetscClassId     id;
3196:       PetscDualSpace   Q = NULL;
3197:       PetscTabulation  T = NULL;
3198:       PetscQuadrature  f;
3199:       const PetscReal *qpoints, *qweights;
3200:       PetscInt         Nc, qNc, Np, fpdim, off, i, d;

3202:       PetscCall(PetscDSGetFieldOffset(prob, field, &off));
3203:       PetscCall(PetscDSGetDiscretization(prob, field, &obj));
3204:       PetscCall(PetscObjectGetClassId(obj, &id));
3205:       if (id == PETSCFE_CLASSID) {
3206:         PetscFE fe = (PetscFE)obj;

3208:         PetscCall(PetscFEGetDualSpace(fe, &Q));
3209:         PetscCall(PetscFEGetNumComponents(fe, &Nc));
3210:         if (s) PetscCall(PetscFECreateTabulation(fe, 1, 1, x, 0, &T));
3211:       } else if (id == PETSCFV_CLASSID) {
3212:         PetscFV fv = (PetscFV)obj;

3214:         PetscCall(PetscFVGetDualSpace(fv, &Q));
3215:         Nc = 1;
3216:       } else SETERRQ(PetscObjectComm((PetscObject)dmc), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
3217:       PetscCall(PetscDualSpaceGetDimension(Q, &fpdim));
3218:       /* For each fine grid cell */
3219:       for (cell = cStart; cell < cEnd; ++cell) {
3220:         PetscInt *findices, *cindices;
3221:         PetscInt  numFIndices, numCIndices;

3223:         PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3224:         PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ));
3225:         PetscCheck(numFIndices == totDim, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fine indices %" PetscInt_FMT " != %" PetscInt_FMT " dual basis vecs", numFIndices, totDim);
3226:         for (i = 0; i < fpdim; ++i) {
3227:           Vec                pointVec;
3228:           PetscScalar       *pV;
3229:           PetscSF            coarseCellSF = NULL;
3230:           const PetscSFNode *coarseCells;
3231:           PetscInt           numCoarseCells, cpdim, row = findices[i + off], q, c, j;

3233:           /* Get points from the dual basis functional quadrature */
3234:           PetscCall(PetscDualSpaceGetFunctional(Q, i, &f));
3235:           PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, &qpoints, &qweights));
3236:           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);
3237:           PetscCall(VecCreateSeq(PETSC_COMM_SELF, Np * dim, &pointVec));
3238:           PetscCall(VecSetBlockSize(pointVec, dim));
3239:           PetscCall(VecGetArray(pointVec, &pV));
3240:           for (q = 0; q < Np; ++q) {
3241:             const PetscReal xi0[3] = {-1., -1., -1.};

3243:             /* Transform point to real space */
3244:             CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x);
3245:             for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d];
3246:           }
3247:           PetscCall(VecRestoreArray(pointVec, &pV));
3248:           /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
3249:           /* OPT: Read this out from preallocation information */
3250:           PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF));
3251:           /* Update preallocation info */
3252:           PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells));
3253:           PetscCheck(numCoarseCells == Np, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located");
3254:           PetscCall(VecGetArray(pointVec, &pV));
3255:           for (ccell = 0; ccell < numCoarseCells; ++ccell) {
3256:             PetscReal       pVReal[3];
3257:             const PetscReal xi0[3] = {-1., -1., -1.};

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

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

3269:               if (id == PETSCFE_CLASSID) {
3270:                 /* Evaluate coarse basis on contained point */
3271:                 PetscCall(PetscFEComputeTabulation((PetscFE)obj, 1, x, 0, T));
3272:                 PetscCall(PetscArrayzero(elemMat, cpdim));
3273:                 /* Get elemMat entries by multiplying by weight */
3274:                 for (j = 0; j < cpdim; ++j) {
3275:                   for (c = 0; c < Nc; ++c) elemMat[j] += T->T[0][j * Nc + c] * qweights[ccell * qNc + c];
3276:                 }
3277:               } else {
3278:                 for (j = 0; j < cpdim; ++j) {
3279:                   for (c = 0; c < Nc; ++c) elemMat[j] += 1.0 * qweights[ccell * qNc + c];
3280:                 }
3281:               }
3282:               if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat));
3283:             }
3284:             /* Update interpolator */
3285:             PetscCheck(numCIndices == totDim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, totDim);
3286:             PetscCall(MatSetValues(interp, 1, &row, cpdim, &cindices[off], elemMat, INSERT_VALUES));
3287:             PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3288:           }
3289:           PetscCall(VecRestoreArray(pointVec, &pV));
3290:           PetscCall(PetscSFDestroy(&coarseCellSF));
3291:           PetscCall(VecDestroy(&pointVec));
3292:         }
3293:         PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3294:       }
3295:       if (s && id == PETSCFE_CLASSID) PetscCall(PetscTabulationDestroy(&T));
3296:     }
3297:     if (!s) {
3298:       PetscCall(MatAssemblyBegin(interp, MAT_FINAL_ASSEMBLY));
3299:       PetscCall(MatAssemblyEnd(interp, MAT_FINAL_ASSEMBLY));
3300:       PetscCall(MatPreallocatorPreallocate(interp, PETSC_TRUE, In));
3301:       PetscCall(MatDestroy(&interp));
3302:       interp = In;
3303:     }
3304:   }
3305:   PetscCall(PetscFree3(v0, J, invJ));
3306:   PetscCall(PetscFree3(v0c, Jc, invJc));
3307:   PetscCall(PetscFree(elemMat));
3308:   PetscCall(MatAssemblyBegin(In, MAT_FINAL_ASSEMBLY));
3309:   PetscCall(MatAssemblyEnd(In, MAT_FINAL_ASSEMBLY));
3310:   PetscCall(PetscLogEventEnd(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
3311:   PetscFunctionReturn(PETSC_SUCCESS);
3312: }

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

3317:   Input Parameters:
3318: + dmf  - The fine mesh
3319: . dmc  - The coarse mesh
3320: - user - The user context

3322:   Output Parameter:
3323: . mass - The mass matrix

3325:   Level: developer

3327: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeMassMatrixNested()`, `DMPlexComputeInterpolatorNested()`, `DMPlexComputeInterpolatorGeneral()`
3328: @*/
3329: PetscErrorCode DMPlexComputeMassMatrixGeneral(DM dmc, DM dmf, Mat mass, void *user)
3330: {
3331:   DM_Plex     *mesh = (DM_Plex *)dmf->data;
3332:   const char  *name = "Mass Matrix";
3333:   PetscDS      prob;
3334:   PetscSection fsection, csection, globalFSection, globalCSection;
3335:   PetscHSetIJ  ht;
3336:   PetscLayout  rLayout;
3337:   PetscInt    *dnz, *onz;
3338:   PetscInt     locRows, rStart, rEnd;
3339:   PetscReal   *x, *v0, *J, *invJ, detJ;
3340:   PetscReal   *v0c, *Jc, *invJc, detJc;
3341:   PetscScalar *elemMat;
3342:   PetscInt     dim, Nf, field, totDim, cStart, cEnd, cell, ccell;

3344:   PetscFunctionBegin;
3345:   PetscCall(DMGetCoordinateDim(dmc, &dim));
3346:   PetscCall(DMGetDS(dmc, &prob));
3347:   PetscCall(PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL));
3348:   PetscCall(PetscDSGetNumFields(prob, &Nf));
3349:   PetscCall(PetscMalloc3(dim, &v0, dim * dim, &J, dim * dim, &invJ));
3350:   PetscCall(PetscMalloc3(dim, &v0c, dim * dim, &Jc, dim * dim, &invJc));
3351:   PetscCall(DMGetLocalSection(dmf, &fsection));
3352:   PetscCall(DMGetGlobalSection(dmf, &globalFSection));
3353:   PetscCall(DMGetLocalSection(dmc, &csection));
3354:   PetscCall(DMGetGlobalSection(dmc, &globalCSection));
3355:   PetscCall(DMPlexGetHeightStratum(dmf, 0, &cStart, &cEnd));
3356:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
3357:   PetscCall(PetscMalloc1(totDim, &elemMat));

3359:   PetscCall(MatGetLocalSize(mass, &locRows, NULL));
3360:   PetscCall(PetscLayoutCreate(PetscObjectComm((PetscObject)mass), &rLayout));
3361:   PetscCall(PetscLayoutSetLocalSize(rLayout, locRows));
3362:   PetscCall(PetscLayoutSetBlockSize(rLayout, 1));
3363:   PetscCall(PetscLayoutSetUp(rLayout));
3364:   PetscCall(PetscLayoutGetRange(rLayout, &rStart, &rEnd));
3365:   PetscCall(PetscLayoutDestroy(&rLayout));
3366:   PetscCall(PetscCalloc2(locRows, &dnz, locRows, &onz));
3367:   PetscCall(PetscHSetIJCreate(&ht));
3368:   for (field = 0; field < Nf; ++field) {
3369:     PetscObject      obj;
3370:     PetscClassId     id;
3371:     PetscQuadrature  quad;
3372:     const PetscReal *qpoints;
3373:     PetscInt         Nq, Nc, i, d;

3375:     PetscCall(PetscDSGetDiscretization(prob, field, &obj));
3376:     PetscCall(PetscObjectGetClassId(obj, &id));
3377:     if (id == PETSCFE_CLASSID) PetscCall(PetscFEGetQuadrature((PetscFE)obj, &quad));
3378:     else PetscCall(PetscFVGetQuadrature((PetscFV)obj, &quad));
3379:     PetscCall(PetscQuadratureGetData(quad, NULL, &Nc, &Nq, &qpoints, NULL));
3380:     /* For each fine grid cell */
3381:     for (cell = cStart; cell < cEnd; ++cell) {
3382:       Vec                pointVec;
3383:       PetscScalar       *pV;
3384:       PetscSF            coarseCellSF = NULL;
3385:       const PetscSFNode *coarseCells;
3386:       PetscInt           numCoarseCells, q, c;
3387:       PetscInt          *findices, *cindices;
3388:       PetscInt           numFIndices, numCIndices;

3390:       PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3391:       PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ));
3392:       /* Get points from the quadrature */
3393:       PetscCall(VecCreateSeq(PETSC_COMM_SELF, Nq * dim, &pointVec));
3394:       PetscCall(VecSetBlockSize(pointVec, dim));
3395:       PetscCall(VecGetArray(pointVec, &pV));
3396:       for (q = 0; q < Nq; ++q) {
3397:         const PetscReal xi0[3] = {-1., -1., -1.};

3399:         /* Transform point to real space */
3400:         CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x);
3401:         for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d];
3402:       }
3403:       PetscCall(VecRestoreArray(pointVec, &pV));
3404:       /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
3405:       PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF));
3406:       PetscCall(PetscSFViewFromOptions(coarseCellSF, NULL, "-interp_sf_view"));
3407:       /* Update preallocation info */
3408:       PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells));
3409:       PetscCheck(numCoarseCells == Nq, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located");
3410:       {
3411:         PetscHashIJKey key;
3412:         PetscBool      missing;

3414:         for (i = 0; i < numFIndices; ++i) {
3415:           key.i = findices[i];
3416:           if (key.i >= 0) {
3417:             /* Get indices for coarse elements */
3418:             for (ccell = 0; ccell < numCoarseCells; ++ccell) {
3419:               PetscCall(DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3420:               for (c = 0; c < numCIndices; ++c) {
3421:                 key.j = cindices[c];
3422:                 if (key.j < 0) continue;
3423:                 PetscCall(PetscHSetIJQueryAdd(ht, key, &missing));
3424:                 if (missing) {
3425:                   if ((key.j >= rStart) && (key.j < rEnd)) ++dnz[key.i - rStart];
3426:                   else ++onz[key.i - rStart];
3427:                 }
3428:               }
3429:               PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3430:             }
3431:           }
3432:         }
3433:       }
3434:       PetscCall(PetscSFDestroy(&coarseCellSF));
3435:       PetscCall(VecDestroy(&pointVec));
3436:       PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3437:     }
3438:   }
3439:   PetscCall(PetscHSetIJDestroy(&ht));
3440:   PetscCall(MatXAIJSetPreallocation(mass, 1, dnz, onz, NULL, NULL));
3441:   PetscCall(MatSetOption(mass, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_TRUE));
3442:   PetscCall(PetscFree2(dnz, onz));
3443:   for (field = 0; field < Nf; ++field) {
3444:     PetscObject      obj;
3445:     PetscClassId     id;
3446:     PetscTabulation  T, Tfine;
3447:     PetscQuadrature  quad;
3448:     const PetscReal *qpoints, *qweights;
3449:     PetscInt         Nq, Nc, i, d;

3451:     PetscCall(PetscDSGetDiscretization(prob, field, &obj));
3452:     PetscCall(PetscObjectGetClassId(obj, &id));
3453:     if (id == PETSCFE_CLASSID) {
3454:       PetscCall(PetscFEGetQuadrature((PetscFE)obj, &quad));
3455:       PetscCall(PetscFEGetCellTabulation((PetscFE)obj, 1, &Tfine));
3456:       PetscCall(PetscFECreateTabulation((PetscFE)obj, 1, 1, x, 0, &T));
3457:     } else {
3458:       PetscCall(PetscFVGetQuadrature((PetscFV)obj, &quad));
3459:     }
3460:     PetscCall(PetscQuadratureGetData(quad, NULL, &Nc, &Nq, &qpoints, &qweights));
3461:     /* For each fine grid cell */
3462:     for (cell = cStart; cell < cEnd; ++cell) {
3463:       Vec                pointVec;
3464:       PetscScalar       *pV;
3465:       PetscSF            coarseCellSF = NULL;
3466:       const PetscSFNode *coarseCells;
3467:       PetscInt           numCoarseCells, cpdim, q, c, j;
3468:       PetscInt          *findices, *cindices;
3469:       PetscInt           numFIndices, numCIndices;

3471:       PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3472:       PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ));
3473:       /* Get points from the quadrature */
3474:       PetscCall(VecCreateSeq(PETSC_COMM_SELF, Nq * dim, &pointVec));
3475:       PetscCall(VecSetBlockSize(pointVec, dim));
3476:       PetscCall(VecGetArray(pointVec, &pV));
3477:       for (q = 0; q < Nq; ++q) {
3478:         const PetscReal xi0[3] = {-1., -1., -1.};

3480:         /* Transform point to real space */
3481:         CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x);
3482:         for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d];
3483:       }
3484:       PetscCall(VecRestoreArray(pointVec, &pV));
3485:       /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
3486:       PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF));
3487:       /* Update matrix */
3488:       PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells));
3489:       PetscCheck(numCoarseCells == Nq, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located");
3490:       PetscCall(VecGetArray(pointVec, &pV));
3491:       for (ccell = 0; ccell < numCoarseCells; ++ccell) {
3492:         PetscReal       pVReal[3];
3493:         const PetscReal xi0[3] = {-1., -1., -1.};

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

3501:         if (id == PETSCFE_CLASSID) {
3502:           PetscFE fe = (PetscFE)obj;

3504:           /* Evaluate coarse basis on contained point */
3505:           PetscCall(PetscFEGetDimension(fe, &cpdim));
3506:           PetscCall(PetscFEComputeTabulation(fe, 1, x, 0, T));
3507:           /* Get elemMat entries by multiplying by weight */
3508:           for (i = 0; i < numFIndices; ++i) {
3509:             PetscCall(PetscArrayzero(elemMat, cpdim));
3510:             for (j = 0; j < cpdim; ++j) {
3511:               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;
3512:             }
3513:             /* Update interpolator */
3514:             if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat));
3515:             PetscCheck(numCIndices == cpdim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, cpdim);
3516:             PetscCall(MatSetValues(mass, 1, &findices[i], numCIndices, cindices, elemMat, ADD_VALUES));
3517:           }
3518:         } else {
3519:           cpdim = 1;
3520:           for (i = 0; i < numFIndices; ++i) {
3521:             PetscCall(PetscArrayzero(elemMat, cpdim));
3522:             for (j = 0; j < cpdim; ++j) {
3523:               for (c = 0; c < Nc; ++c) elemMat[j] += 1.0 * 1.0 * qweights[ccell * Nc + c] * detJ;
3524:             }
3525:             /* Update interpolator */
3526:             if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat));
3527:             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));
3528:             PetscCheck(numCIndices == cpdim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, cpdim);
3529:             PetscCall(MatSetValues(mass, 1, &findices[i], numCIndices, cindices, elemMat, ADD_VALUES));
3530:           }
3531:         }
3532:         PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3533:       }
3534:       PetscCall(VecRestoreArray(pointVec, &pV));
3535:       PetscCall(PetscSFDestroy(&coarseCellSF));
3536:       PetscCall(VecDestroy(&pointVec));
3537:       PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3538:     }
3539:     if (id == PETSCFE_CLASSID) PetscCall(PetscTabulationDestroy(&T));
3540:   }
3541:   PetscCall(PetscFree3(v0, J, invJ));
3542:   PetscCall(PetscFree3(v0c, Jc, invJc));
3543:   PetscCall(PetscFree(elemMat));
3544:   PetscCall(MatAssemblyBegin(mass, MAT_FINAL_ASSEMBLY));
3545:   PetscCall(MatAssemblyEnd(mass, MAT_FINAL_ASSEMBLY));
3546:   PetscFunctionReturn(PETSC_SUCCESS);
3547: }

3549: /*@
3550:   DMPlexComputeInjectorFEM - Compute a mapping from coarse unknowns to fine unknowns

3552:   Input Parameters:
3553: + dmc  - The coarse mesh
3554: . dmf  - The fine mesh
3555: - user - The user context

3557:   Output Parameter:
3558: . sc - The mapping

3560:   Level: developer

3562: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorNested()`
3563: @*/
3564: PetscErrorCode DMPlexComputeInjectorFEM(DM dmc, DM dmf, VecScatter *sc, void *user)
3565: {
3566:   PetscDS      prob;
3567:   PetscFE     *feRef;
3568:   PetscFV     *fvRef;
3569:   Vec          fv, cv;
3570:   IS           fis, cis;
3571:   PetscSection fsection, fglobalSection, csection, cglobalSection;
3572:   PetscInt    *cmap, *cellCIndices, *cellFIndices, *cindices, *findices;
3573:   PetscInt     cTotDim, fTotDim = 0, Nf, f, field, cStart, cEnd, c, dim, d, startC, endC, offsetC, offsetF, m;
3574:   PetscBool   *needAvg;

3576:   PetscFunctionBegin;
3577:   PetscCall(PetscLogEventBegin(DMPLEX_InjectorFEM, dmc, dmf, 0, 0));
3578:   PetscCall(DMGetDimension(dmf, &dim));
3579:   PetscCall(DMGetLocalSection(dmf, &fsection));
3580:   PetscCall(DMGetGlobalSection(dmf, &fglobalSection));
3581:   PetscCall(DMGetLocalSection(dmc, &csection));
3582:   PetscCall(DMGetGlobalSection(dmc, &cglobalSection));
3583:   PetscCall(PetscSectionGetNumFields(fsection, &Nf));
3584:   PetscCall(DMPlexGetSimplexOrBoxCells(dmc, 0, &cStart, &cEnd));
3585:   PetscCall(DMGetDS(dmc, &prob));
3586:   PetscCall(PetscCalloc3(Nf, &feRef, Nf, &fvRef, Nf, &needAvg));
3587:   for (f = 0; f < Nf; ++f) {
3588:     PetscObject  obj;
3589:     PetscClassId id;
3590:     PetscInt     fNb = 0, Nc = 0;

3592:     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
3593:     PetscCall(PetscObjectGetClassId(obj, &id));
3594:     if (id == PETSCFE_CLASSID) {
3595:       PetscFE    fe = (PetscFE)obj;
3596:       PetscSpace sp;
3597:       PetscInt   maxDegree;

3599:       PetscCall(PetscFERefine(fe, &feRef[f]));
3600:       PetscCall(PetscFEGetDimension(feRef[f], &fNb));
3601:       PetscCall(PetscFEGetNumComponents(fe, &Nc));
3602:       PetscCall(PetscFEGetBasisSpace(fe, &sp));
3603:       PetscCall(PetscSpaceGetDegree(sp, NULL, &maxDegree));
3604:       if (!maxDegree) needAvg[f] = PETSC_TRUE;
3605:     } else if (id == PETSCFV_CLASSID) {
3606:       PetscFV        fv = (PetscFV)obj;
3607:       PetscDualSpace Q;

3609:       PetscCall(PetscFVRefine(fv, &fvRef[f]));
3610:       PetscCall(PetscFVGetDualSpace(fvRef[f], &Q));
3611:       PetscCall(PetscDualSpaceGetDimension(Q, &fNb));
3612:       PetscCall(PetscFVGetNumComponents(fv, &Nc));
3613:       needAvg[f] = PETSC_TRUE;
3614:     }
3615:     fTotDim += fNb;
3616:   }
3617:   PetscCall(PetscDSGetTotalDimension(prob, &cTotDim));
3618:   PetscCall(PetscMalloc1(cTotDim, &cmap));
3619:   for (field = 0, offsetC = 0, offsetF = 0; field < Nf; ++field) {
3620:     PetscFE        feC;
3621:     PetscFV        fvC;
3622:     PetscDualSpace QF, QC;
3623:     PetscInt       order = -1, NcF, NcC, fpdim, cpdim;

3625:     if (feRef[field]) {
3626:       PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&feC));
3627:       PetscCall(PetscFEGetNumComponents(feC, &NcC));
3628:       PetscCall(PetscFEGetNumComponents(feRef[field], &NcF));
3629:       PetscCall(PetscFEGetDualSpace(feRef[field], &QF));
3630:       PetscCall(PetscDualSpaceGetOrder(QF, &order));
3631:       PetscCall(PetscDualSpaceGetDimension(QF, &fpdim));
3632:       PetscCall(PetscFEGetDualSpace(feC, &QC));
3633:       PetscCall(PetscDualSpaceGetDimension(QC, &cpdim));
3634:     } else {
3635:       PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fvC));
3636:       PetscCall(PetscFVGetNumComponents(fvC, &NcC));
3637:       PetscCall(PetscFVGetNumComponents(fvRef[field], &NcF));
3638:       PetscCall(PetscFVGetDualSpace(fvRef[field], &QF));
3639:       PetscCall(PetscDualSpaceGetDimension(QF, &fpdim));
3640:       PetscCall(PetscFVGetDualSpace(fvC, &QC));
3641:       PetscCall(PetscDualSpaceGetDimension(QC, &cpdim));
3642:     }
3643:     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);
3644:     for (c = 0; c < cpdim; ++c) {
3645:       PetscQuadrature  cfunc;
3646:       const PetscReal *cqpoints, *cqweights;
3647:       PetscInt         NqcC, NpC;
3648:       PetscBool        found = PETSC_FALSE;

3650:       PetscCall(PetscDualSpaceGetFunctional(QC, c, &cfunc));
3651:       PetscCall(PetscQuadratureGetData(cfunc, NULL, &NqcC, &NpC, &cqpoints, &cqweights));
3652:       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);
3653:       PetscCheck(NpC == 1 || !feRef[field], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Do not know how to do injection for moments");
3654:       for (f = 0; f < fpdim; ++f) {
3655:         PetscQuadrature  ffunc;
3656:         const PetscReal *fqpoints, *fqweights;
3657:         PetscReal        sum = 0.0;
3658:         PetscInt         NqcF, NpF;

3660:         PetscCall(PetscDualSpaceGetFunctional(QF, f, &ffunc));
3661:         PetscCall(PetscQuadratureGetData(ffunc, NULL, &NqcF, &NpF, &fqpoints, &fqweights));
3662:         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);
3663:         if (NpC != NpF) continue;
3664:         for (d = 0; d < dim; ++d) sum += PetscAbsReal(cqpoints[d] - fqpoints[d]);
3665:         if (sum > 1.0e-9) continue;
3666:         for (d = 0; d < NcC; ++d) sum += PetscAbsReal(cqweights[d] * fqweights[d]);
3667:         if (sum < 1.0e-9) continue;
3668:         cmap[offsetC + c] = offsetF + f;
3669:         found             = PETSC_TRUE;
3670:         break;
3671:       }
3672:       if (!found) {
3673:         /* TODO We really want the average here, but some asshole put VecScatter in the interface */
3674:         if (fvRef[field] || (feRef[field] && order == 0)) {
3675:           cmap[offsetC + c] = offsetF + 0;
3676:         } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Could not locate matching functional for injection");
3677:       }
3678:     }
3679:     offsetC += cpdim;
3680:     offsetF += fpdim;
3681:   }
3682:   for (f = 0; f < Nf; ++f) {
3683:     PetscCall(PetscFEDestroy(&feRef[f]));
3684:     PetscCall(PetscFVDestroy(&fvRef[f]));
3685:   }
3686:   PetscCall(PetscFree3(feRef, fvRef, needAvg));

3688:   PetscCall(DMGetGlobalVector(dmf, &fv));
3689:   PetscCall(DMGetGlobalVector(dmc, &cv));
3690:   PetscCall(VecGetOwnershipRange(cv, &startC, &endC));
3691:   PetscCall(PetscSectionGetConstrainedStorageSize(cglobalSection, &m));
3692:   PetscCall(PetscMalloc2(cTotDim, &cellCIndices, fTotDim, &cellFIndices));
3693:   PetscCall(PetscMalloc1(m, &cindices));
3694:   PetscCall(PetscMalloc1(m, &findices));
3695:   for (d = 0; d < m; ++d) cindices[d] = findices[d] = -1;
3696:   for (c = cStart; c < cEnd; ++c) {
3697:     PetscCall(DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, c, cellCIndices, cellFIndices));
3698:     for (d = 0; d < cTotDim; ++d) {
3699:       if ((cellCIndices[d] < startC) || (cellCIndices[d] >= endC)) continue;
3700:       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]]);
3701:       cindices[cellCIndices[d] - startC] = cellCIndices[d];
3702:       findices[cellCIndices[d] - startC] = cellFIndices[cmap[d]];
3703:     }
3704:   }
3705:   PetscCall(PetscFree(cmap));
3706:   PetscCall(PetscFree2(cellCIndices, cellFIndices));

3708:   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, m, cindices, PETSC_OWN_POINTER, &cis));
3709:   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, m, findices, PETSC_OWN_POINTER, &fis));
3710:   PetscCall(VecScatterCreate(cv, cis, fv, fis, sc));
3711:   PetscCall(ISDestroy(&cis));
3712:   PetscCall(ISDestroy(&fis));
3713:   PetscCall(DMRestoreGlobalVector(dmf, &fv));
3714:   PetscCall(DMRestoreGlobalVector(dmc, &cv));
3715:   PetscCall(PetscLogEventEnd(DMPLEX_InjectorFEM, dmc, dmf, 0, 0));
3716:   PetscFunctionReturn(PETSC_SUCCESS);
3717: }

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

3722:   Input Parameters:
3723: + dm     - The `DM`
3724: . cellIS - The cells to include
3725: . locX   - A local vector with the solution fields
3726: . locX_t - A local vector with solution field time derivatives, or `NULL`
3727: - locA   - A local vector with auxiliary fields, or `NULL`

3729:   Output Parameters:
3730: + u   - The field coefficients
3731: . u_t - The fields derivative coefficients
3732: - a   - The auxiliary field coefficients

3734:   Level: developer

3736: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
3737: @*/
3738: PetscErrorCode DMPlexGetCellFields(DM dm, IS cellIS, Vec locX, PeOp Vec locX_t, PeOp Vec locA, PetscScalar *u[], PetscScalar *u_t[], PetscScalar *a[])
3739: {
3740:   DM              plex, plexA = NULL;
3741:   DMEnclosureType encAux;
3742:   PetscSection    section, sectionAux;
3743:   PetscDS         prob;
3744:   const PetscInt *cells;
3745:   PetscInt        cStart, cEnd, numCells, totDim, totDimAux, c;

3747:   PetscFunctionBegin;
3752:   PetscAssertPointer(u, 6);
3753:   PetscAssertPointer(u_t, 7);
3754:   PetscAssertPointer(a, 8);
3755:   PetscCall(DMPlexConvertPlex(dm, &plex, PETSC_FALSE));
3756:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
3757:   PetscCall(DMGetLocalSection(dm, &section));
3758:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL));
3759:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
3760:   if (locA) {
3761:     DM      dmAux;
3762:     PetscDS probAux;

3764:     PetscCall(VecGetDM(locA, &dmAux));
3765:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
3766:     PetscCall(DMPlexConvertPlex(dmAux, &plexA, PETSC_FALSE));
3767:     PetscCall(DMGetLocalSection(dmAux, &sectionAux));
3768:     PetscCall(DMGetDS(dmAux, &probAux));
3769:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
3770:   }
3771:   numCells = cEnd - cStart;
3772:   PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u));
3773:   if (locX_t) PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u_t));
3774:   else *u_t = NULL;
3775:   if (locA) PetscCall(DMGetWorkArray(dm, numCells * totDimAux, MPIU_SCALAR, a));
3776:   else *a = NULL;
3777:   for (c = cStart; c < cEnd; ++c) {
3778:     const PetscInt cell = cells ? cells[c] : c;
3779:     const PetscInt cind = c - cStart;
3780:     PetscScalar   *x = NULL, *x_t = NULL, *ul = *u, *ul_t = *u_t, *al = *a;
3781:     PetscInt       i;

3783:     PetscCall(DMPlexVecGetClosure(plex, section, locX, cell, NULL, &x));
3784:     for (i = 0; i < totDim; ++i) ul[cind * totDim + i] = x[i];
3785:     PetscCall(DMPlexVecRestoreClosure(plex, section, locX, cell, NULL, &x));
3786:     if (locX_t) {
3787:       PetscCall(DMPlexVecGetClosure(plex, section, locX_t, cell, NULL, &x_t));
3788:       for (i = 0; i < totDim; ++i) ul_t[cind * totDim + i] = x_t[i];
3789:       PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, cell, NULL, &x_t));
3790:     }
3791:     if (locA) {
3792:       PetscInt subcell;
3793:       PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, cell, &subcell));
3794:       PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subcell, NULL, &x));
3795:       for (i = 0; i < totDimAux; ++i) al[cind * totDimAux + i] = x[i];
3796:       PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subcell, NULL, &x));
3797:     }
3798:   }
3799:   PetscCall(DMDestroy(&plex));
3800:   if (locA) PetscCall(DMDestroy(&plexA));
3801:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
3802:   PetscFunctionReturn(PETSC_SUCCESS);
3803: }

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

3808:   Input Parameters:
3809: + dm     - The `DM`
3810: . cellIS - The cells to include
3811: . locX   - A local vector with the solution fields
3812: . locX_t - A local vector with solution field time derivatives, or `NULL`
3813: - locA   - A local vector with auxiliary fields, or `NULL`

3815:   Output Parameters:
3816: + u   - The field coefficients
3817: . u_t - The fields derivative coefficients
3818: - a   - The auxiliary field coefficients

3820:   Level: developer

3822: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
3823: @*/
3824: PetscErrorCode DMPlexRestoreCellFields(DM dm, IS cellIS, Vec locX, PeOp Vec locX_t, PeOp Vec locA, PetscScalar *u[], PetscScalar *u_t[], PetscScalar *a[])
3825: {
3826:   PetscFunctionBegin;
3827:   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u));
3828:   if (locX_t) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u_t));
3829:   if (locA) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, a));
3830:   PetscFunctionReturn(PETSC_SUCCESS);
3831: }

3833: static PetscErrorCode DMPlexGetHybridCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
3834: {
3835:   DM              plex, plexA = NULL;
3836:   DMEnclosureType encAux;
3837:   PetscSection    section, sectionAux;
3838:   PetscDS         ds, dsIn;
3839:   const PetscInt *cells;
3840:   PetscInt        cStart, cEnd, numCells, c, totDim, totDimAux, Nf, f;

3842:   PetscFunctionBegin;
3848:   PetscAssertPointer(u, 6);
3849:   PetscAssertPointer(u_t, 7);
3850:   PetscAssertPointer(a, 8);
3851:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
3852:   numCells = cEnd - cStart;
3853:   PetscCall(DMPlexConvertPlex(dm, &plex, PETSC_FALSE));
3854:   PetscCall(DMGetLocalSection(dm, &section));
3855:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn));
3856:   PetscCall(PetscDSGetNumFields(dsIn, &Nf));
3857:   PetscCall(PetscDSGetTotalDimension(dsIn, &totDim));
3858:   if (locA) {
3859:     DM      dmAux;
3860:     PetscDS probAux;

3862:     PetscCall(VecGetDM(locA, &dmAux));
3863:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
3864:     PetscCall(DMPlexConvertPlex(dmAux, &plexA, PETSC_FALSE));
3865:     PetscCall(DMGetLocalSection(dmAux, &sectionAux));
3866:     PetscCall(DMGetDS(dmAux, &probAux));
3867:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
3868:   }
3869:   PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u));
3870:   if (locX_t) PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u_t));
3871:   else {
3872:     *u_t = NULL;
3873:   }
3874:   if (locA) PetscCall(DMGetWorkArray(dm, numCells * totDimAux, MPIU_SCALAR, a));
3875:   else {
3876:     *a = NULL;
3877:   }
3878:   // Loop over cohesive cells
3879:   for (c = cStart; c < cEnd; ++c) {
3880:     const PetscInt  cell = cells ? cells[c] : c;
3881:     const PetscInt  cind = c - cStart;
3882:     PetscScalar    *xf = NULL, *xc = NULL, *x = NULL, *xf_t = NULL, *xc_t = NULL;
3883:     PetscScalar    *ul = &(*u)[cind * totDim], *ul_t = PetscSafePointerPlusOffset(*u_t, cind * totDim);
3884:     const PetscInt *cone, *ornt;
3885:     PetscInt        Nx = 0, Nxf, s;

3887:     PetscCall(DMPlexGetCone(dm, cell, &cone));
3888:     PetscCall(DMPlexGetConeOrientation(dm, cell, &ornt));
3889:     // Put in cohesive unknowns
3890:     PetscCall(DMPlexVecGetClosure(plex, section, locX, cell, &Nxf, &xf));
3891:     if (locX_t) PetscCall(DMPlexVecGetClosure(plex, section, locX_t, cell, NULL, &xf_t));
3892:     for (f = 0; f < Nf; ++f) {
3893:       PetscInt  fdofIn, foff, foffIn;
3894:       PetscBool cohesive;

3896:       PetscCall(PetscDSGetCohesive(dsIn, f, &cohesive));
3897:       if (!cohesive) continue;
3898:       PetscCall(PetscDSGetFieldSize(dsIn, f, &fdofIn));
3899:       PetscCall(PetscDSGetFieldOffsetCohesive(ds, f, &foff));
3900:       PetscCall(PetscDSGetFieldOffsetCohesive(dsIn, f, &foffIn));
3901:       for (PetscInt i = 0; i < fdofIn; ++i) ul[foffIn + i] = xf[foff + i];
3902:       if (locX_t)
3903:         for (PetscInt i = 0; i < fdofIn; ++i) ul_t[foffIn + i] = xf_t[foff + i];
3904:       Nx += fdofIn;
3905:     }
3906:     PetscCall(DMPlexVecRestoreClosure(plex, section, locX, cell, &Nxf, &xf));
3907:     if (locX_t) PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, cell, NULL, &xf_t));
3908:     // Loop over sides of surface
3909:     for (s = 0; s < 2; ++s) {
3910:       const PetscInt *support;
3911:       const PetscInt  face = cone[s];
3912:       PetscDS         dsC;
3913:       PetscInt        ssize, ncell, Nxc;

3915:       // I don't think I need the face to have 0 orientation in the hybrid cell
3916:       //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]);
3917:       PetscCall(DMPlexGetSupport(dm, face, &support));
3918:       PetscCall(DMPlexGetSupportSize(dm, face, &ssize));
3919:       if (support[0] == cell) ncell = support[1];
3920:       else if (support[1] == cell) ncell = support[0];
3921:       else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", face, cell);
3922:       // Get closure of both face and cell, stick in cell for normal fields and face for cohesive fields
3923:       PetscCall(DMGetCellDS(dm, ncell, &dsC, NULL));
3924:       PetscCall(DMPlexVecGetClosure(plex, section, locX, ncell, &Nxc, &xc));
3925:       if (locX_t) PetscCall(DMPlexVecGetClosure(plex, section, locX_t, ncell, NULL, &xc_t));
3926:       for (f = 0; f < Nf; ++f) {
3927:         PetscInt  fdofIn, foffIn, foff;
3928:         PetscBool cohesive;

3930:         PetscCall(PetscDSGetCohesive(dsIn, f, &cohesive));
3931:         if (cohesive) continue;
3932:         PetscCall(PetscDSGetFieldSize(dsIn, f, &fdofIn));
3933:         PetscCall(PetscDSGetFieldOffset(dsC, f, &foff));
3934:         PetscCall(PetscDSGetFieldOffsetCohesive(dsIn, f, &foffIn));
3935:         for (PetscInt i = 0; i < fdofIn; ++i) ul[foffIn + s * fdofIn + i] = xc[foff + i];
3936:         if (locX_t)
3937:           for (PetscInt i = 0; i < fdofIn; ++i) ul_t[foffIn + s * fdofIn + i] = xc_t[foff + i];
3938:         Nx += fdofIn;
3939:       }
3940:       PetscCall(DMPlexVecRestoreClosure(plex, section, locX, ncell, &Nxc, &xc));
3941:       if (locX_t) PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, ncell, NULL, &xc_t));
3942:     }
3943:     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);

3945:     if (locA) {
3946:       PetscScalar *al = &(*a)[cind * totDimAux];
3947:       PetscInt     subcell;

3949:       PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, cell, &subcell));
3950:       PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subcell, &Nx, &x));
3951:       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);
3952:       for (PetscInt i = 0; i < totDimAux; ++i) al[i] = x[i];
3953:       PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subcell, &Nx, &x));
3954:     }
3955:   }
3956:   PetscCall(DMDestroy(&plex));
3957:   PetscCall(DMDestroy(&plexA));
3958:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
3959:   PetscFunctionReturn(PETSC_SUCCESS);
3960: }

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

3965:   Input Parameters:
3966: + dm      - The full domain DM
3967: . dmX     - An array of DM for the field, say an auxiliary DM, indexed by s
3968: . dsX     - An array of PetscDS for the field, indexed by s
3969: . cellIS  - The interface cells for which we want values
3970: . locX    - An array of local vectors with the field values, indexed by s
3971: - useCell - Flag to have values come from neighboring cell rather than endcap face

3973:   Output Parameter:
3974: . x       - An array of field values, indexed by s

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

3979:   Level: advanced

3981: .seealso: `DMPlexRestoreHybridFields()`, `DMGetWorkArray()`
3982: */
3983: static PetscErrorCode DMPlexGetHybridFields(DM dm, DM dmX[], PetscDS dsX[], IS cellIS, Vec locX[], PetscBool useCell, PetscScalar *x[])
3984: {
3985:   DM              plexX[2];
3986:   DMEnclosureType encX[2];
3987:   PetscSection    sectionX[2];
3988:   const PetscInt *cells;
3989:   PetscInt        cStart, cEnd, numCells, c, s, totDimX[2];

3991:   PetscFunctionBegin;
3992:   PetscAssertPointer(locX, 5);
3993:   if (!locX[0] || !locX[1]) PetscFunctionReturn(PETSC_SUCCESS);
3994:   PetscAssertPointer(dmX, 2);
3995:   PetscAssertPointer(dsX, 3);
3997:   PetscAssertPointer(x, 7);
3998:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
3999:   numCells = cEnd - cStart;
4000:   for (s = 0; s < 2; ++s) {
4004:     PetscCall(DMPlexConvertPlex(dmX[s], &plexX[s], PETSC_FALSE));
4005:     PetscCall(DMGetEnclosureRelation(dmX[s], dm, &encX[s]));
4006:     PetscCall(DMGetLocalSection(dmX[s], &sectionX[s]));
4007:     PetscCall(PetscDSGetTotalDimension(dsX[s], &totDimX[s]));
4008:     PetscCall(DMGetWorkArray(dmX[s], numCells * totDimX[s], MPIU_SCALAR, &x[s]));
4009:   }
4010:   for (c = cStart; c < cEnd; ++c) {
4011:     const PetscInt  cell = cells ? cells[c] : c;
4012:     const PetscInt  cind = c - cStart;
4013:     const PetscInt *cone, *ornt;

4015:     PetscCall(DMPlexGetCone(dm, cell, &cone));
4016:     PetscCall(DMPlexGetConeOrientation(dm, cell, &ornt));
4017:     //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]);
4018:     for (s = 0; s < 2; ++s) {
4019:       const PetscInt tdX     = totDimX[s];
4020:       PetscScalar   *closure = NULL, *xl = &x[s][cind * tdX];
4021:       PetscInt       face = cone[s], point = face, subpoint, Nx, i;

4023:       if (useCell) {
4024:         const PetscInt *support;
4025:         PetscInt        ssize;

4027:         PetscCall(DMPlexGetSupport(dm, face, &support));
4028:         PetscCall(DMPlexGetSupportSize(dm, face, &ssize));
4029:         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);
4030:         if (support[0] == cell) point = support[1];
4031:         else if (support[1] == cell) point = support[0];
4032:         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", face, cell);
4033:       }
4034:       PetscCall(DMGetEnclosurePoint(plexX[s], dm, encX[s], point, &subpoint));
4035:       PetscCall(DMPlexVecGetOrientedClosure_Internal(plexX[s], sectionX[s], PETSC_FALSE, locX[s], subpoint, ornt[s], &Nx, &closure));
4036:       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);
4037:       for (i = 0; i < Nx; ++i) xl[i] = closure[i];
4038:       PetscCall(DMPlexVecRestoreClosure(plexX[s], sectionX[s], locX[s], subpoint, &Nx, &closure));
4039:     }
4040:   }
4041:   for (s = 0; s < 2; ++s) PetscCall(DMDestroy(&plexX[s]));
4042:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
4043:   PetscFunctionReturn(PETSC_SUCCESS);
4044: }

4046: static PetscErrorCode DMPlexRestoreHybridFields(DM dm, DM dmX[], PetscDS dsX[], IS cellIS, Vec locX[], PetscBool useCell, PetscScalar *x[])
4047: {
4048:   PetscFunctionBegin;
4049:   if (!locX[0] || !locX[1]) PetscFunctionReturn(PETSC_SUCCESS);
4050:   PetscCall(DMRestoreWorkArray(dmX[0], 0, MPIU_SCALAR, &x[0]));
4051:   PetscCall(DMRestoreWorkArray(dmX[1], 0, MPIU_SCALAR, &x[1]));
4052:   PetscFunctionReturn(PETSC_SUCCESS);
4053: }

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

4058:   Input Parameters:
4059: + dm           - The `DM`
4060: . fStart       - The first face to include
4061: . fEnd         - The first face to exclude
4062: . locX         - A local vector with the solution fields
4063: . locX_t       - A local vector with solution field time derivatives, or `NULL`
4064: . faceGeometry - A local vector with face geometry
4065: . cellGeometry - A local vector with cell geometry
4066: - locGrad      - A local vector with field gradients, or `NULL`

4068:   Output Parameters:
4069: + Nface - The number of faces with field values
4070: . uL    - The field values at the left side of the face
4071: - uR    - The field values at the right side of the face

4073:   Level: developer

4075: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellFields()`
4076: @*/
4077: PetscErrorCode DMPlexGetFaceFields(DM dm, PetscInt fStart, PetscInt fEnd, Vec locX, PeOp Vec locX_t, Vec faceGeometry, Vec cellGeometry, PeOp Vec locGrad, PetscInt *Nface, PetscScalar *uL[], PetscScalar *uR[])
4078: {
4079:   DM                 dmFace, dmCell, dmGrad = NULL;
4080:   PetscSection       section;
4081:   PetscDS            prob;
4082:   DMLabel            ghostLabel;
4083:   const PetscScalar *facegeom, *cellgeom, *x, *lgrad;
4084:   PetscBool         *isFE;
4085:   PetscInt           dim, Nf, f, Nc, numFaces = fEnd - fStart, iface, face;

4087:   PetscFunctionBegin;
4094:   PetscAssertPointer(uL, 10);
4095:   PetscAssertPointer(uR, 11);
4096:   PetscCall(DMGetDimension(dm, &dim));
4097:   PetscCall(DMGetDS(dm, &prob));
4098:   PetscCall(DMGetLocalSection(dm, &section));
4099:   PetscCall(PetscDSGetNumFields(prob, &Nf));
4100:   PetscCall(PetscDSGetTotalComponents(prob, &Nc));
4101:   PetscCall(PetscMalloc1(Nf, &isFE));
4102:   for (f = 0; f < Nf; ++f) {
4103:     PetscObject  obj;
4104:     PetscClassId id;

4106:     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4107:     PetscCall(PetscObjectGetClassId(obj, &id));
4108:     if (id == PETSCFE_CLASSID) {
4109:       isFE[f] = PETSC_TRUE;
4110:     } else if (id == PETSCFV_CLASSID) {
4111:       isFE[f] = PETSC_FALSE;
4112:     } else {
4113:       isFE[f] = PETSC_FALSE;
4114:     }
4115:   }
4116:   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
4117:   PetscCall(VecGetArrayRead(locX, &x));
4118:   PetscCall(VecGetDM(faceGeometry, &dmFace));
4119:   PetscCall(VecGetArrayRead(faceGeometry, &facegeom));
4120:   PetscCall(VecGetDM(cellGeometry, &dmCell));
4121:   PetscCall(VecGetArrayRead(cellGeometry, &cellgeom));
4122:   if (locGrad) {
4123:     PetscCall(VecGetDM(locGrad, &dmGrad));
4124:     PetscCall(VecGetArrayRead(locGrad, &lgrad));
4125:   }
4126:   PetscCall(DMGetWorkArray(dm, numFaces * Nc, MPIU_SCALAR, uL));
4127:   PetscCall(DMGetWorkArray(dm, numFaces * Nc, MPIU_SCALAR, uR));
4128:   /* Right now just eat the extra work for FE (could make a cell loop) */
4129:   for (face = fStart, iface = 0; face < fEnd; ++face) {
4130:     const PetscInt  *cells;
4131:     PetscFVFaceGeom *fg;
4132:     PetscFVCellGeom *cgL, *cgR;
4133:     PetscScalar     *xL, *xR, *gL, *gR;
4134:     PetscScalar     *uLl = *uL, *uRl = *uR;
4135:     PetscInt         ghost, nsupp, nchild;

4137:     PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
4138:     PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
4139:     PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
4140:     if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
4141:     PetscCall(DMPlexPointLocalRead(dmFace, face, facegeom, &fg));
4142:     PetscCall(DMPlexGetSupport(dm, face, &cells));
4143:     PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL));
4144:     PetscCall(DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR));
4145:     for (f = 0; f < Nf; ++f) {
4146:       PetscInt off;

4148:       PetscCall(PetscDSGetComponentOffset(prob, f, &off));
4149:       if (isFE[f]) {
4150:         const PetscInt *cone;
4151:         PetscInt        comp, coneSizeL, coneSizeR, faceLocL, faceLocR, ldof, rdof, d;

4153:         xL = xR = NULL;
4154:         PetscCall(PetscSectionGetFieldComponents(section, f, &comp));
4155:         PetscCall(DMPlexVecGetClosure(dm, section, locX, cells[0], &ldof, &xL));
4156:         PetscCall(DMPlexVecGetClosure(dm, section, locX, cells[1], &rdof, &xR));
4157:         PetscCall(DMPlexGetCone(dm, cells[0], &cone));
4158:         PetscCall(DMPlexGetConeSize(dm, cells[0], &coneSizeL));
4159:         for (faceLocL = 0; faceLocL < coneSizeL; ++faceLocL)
4160:           if (cone[faceLocL] == face) break;
4161:         PetscCall(DMPlexGetCone(dm, cells[1], &cone));
4162:         PetscCall(DMPlexGetConeSize(dm, cells[1], &coneSizeR));
4163:         for (faceLocR = 0; faceLocR < coneSizeR; ++faceLocR)
4164:           if (cone[faceLocR] == face) break;
4165:         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]);
4166:         /* Check that FEM field has values in the right cell (sometimes its an FV ghost cell) */
4167:         /* TODO: this is a hack that might not be right for nonconforming */
4168:         if (faceLocL < coneSizeL) {
4169:           PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocL, xL, &uLl[iface * Nc + off]));
4170:           if (rdof == ldof && faceLocR < coneSizeR) PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface * Nc + off]));
4171:           else {
4172:             for (d = 0; d < comp; ++d) uRl[iface * Nc + off + d] = uLl[iface * Nc + off + d];
4173:           }
4174:         } else {
4175:           PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface * Nc + off]));
4176:           PetscCall(PetscSectionGetFieldComponents(section, f, &comp));
4177:           for (d = 0; d < comp; ++d) uLl[iface * Nc + off + d] = uRl[iface * Nc + off + d];
4178:         }
4179:         PetscCall(DMPlexVecRestoreClosure(dm, section, locX, cells[0], &ldof, &xL));
4180:         PetscCall(DMPlexVecRestoreClosure(dm, section, locX, cells[1], &rdof, &xR));
4181:       } else {
4182:         PetscFV  fv;
4183:         PetscInt numComp, c;

4185:         PetscCall(PetscDSGetDiscretization(prob, f, (PetscObject *)&fv));
4186:         PetscCall(PetscFVGetNumComponents(fv, &numComp));
4187:         PetscCall(DMPlexPointLocalFieldRead(dm, cells[0], f, x, &xL));
4188:         PetscCall(DMPlexPointLocalFieldRead(dm, cells[1], f, x, &xR));
4189:         if (dmGrad) {
4190:           PetscReal dxL[3], dxR[3];

4192:           PetscCall(DMPlexPointLocalRead(dmGrad, cells[0], lgrad, &gL));
4193:           PetscCall(DMPlexPointLocalRead(dmGrad, cells[1], lgrad, &gR));
4194:           DMPlex_WaxpyD_Internal(dim, -1, cgL->centroid, fg->centroid, dxL);
4195:           DMPlex_WaxpyD_Internal(dim, -1, cgR->centroid, fg->centroid, dxR);
4196:           for (c = 0; c < numComp; ++c) {
4197:             uLl[iface * Nc + off + c] = xL[c] + DMPlex_DotD_Internal(dim, &gL[c * dim], dxL);
4198:             uRl[iface * Nc + off + c] = xR[c] + DMPlex_DotD_Internal(dim, &gR[c * dim], dxR);
4199:           }
4200:         } else {
4201:           for (c = 0; c < numComp; ++c) {
4202:             uLl[iface * Nc + off + c] = xL[c];
4203:             uRl[iface * Nc + off + c] = xR[c];
4204:           }
4205:         }
4206:       }
4207:     }
4208:     ++iface;
4209:   }
4210:   *Nface = iface;
4211:   PetscCall(VecRestoreArrayRead(locX, &x));
4212:   PetscCall(VecRestoreArrayRead(faceGeometry, &facegeom));
4213:   PetscCall(VecRestoreArrayRead(cellGeometry, &cellgeom));
4214:   if (locGrad) PetscCall(VecRestoreArrayRead(locGrad, &lgrad));
4215:   PetscCall(PetscFree(isFE));
4216:   PetscFunctionReturn(PETSC_SUCCESS);
4217: }

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

4222:   Input Parameters:
4223: + dm           - The `DM`
4224: . fStart       - The first face to include
4225: . fEnd         - The first face to exclude
4226: . locX         - A local vector with the solution fields
4227: . locX_t       - A local vector with solution field time derivatives, or `NULL`
4228: . faceGeometry - A local vector with face geometry
4229: . cellGeometry - A local vector with cell geometry
4230: - locGrad      - A local vector with field gradients, or `NULL`

4232:   Output Parameters:
4233: + Nface - The number of faces with field values
4234: . uL    - The field values at the left side of the face
4235: - uR    - The field values at the right side of the face

4237:   Level: developer

4239: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
4240: @*/
4241: PetscErrorCode DMPlexRestoreFaceFields(DM dm, PetscInt fStart, PetscInt fEnd, Vec locX, PeOp Vec locX_t, Vec faceGeometry, Vec cellGeometry, PeOp Vec locGrad, PetscInt *Nface, PetscScalar *uL[], PetscScalar *uR[])
4242: {
4243:   PetscFunctionBegin;
4244:   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uL));
4245:   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uR));
4246:   PetscFunctionReturn(PETSC_SUCCESS);
4247: }

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

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

4259:   Output Parameters:
4260: + Nface - The number of faces with field values
4261: . fgeom - The face centroid and normals
4262: - vol   - The cell volumes

4264:   Level: developer

4266: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellFields()`
4267: @*/
4268: PetscErrorCode DMPlexGetFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom *fgeom[], PetscReal *vol[])
4269: {
4270:   DM                 dmFace, dmCell;
4271:   DMLabel            ghostLabel;
4272:   const PetscScalar *facegeom, *cellgeom;
4273:   PetscInt           dim, numFaces = fEnd - fStart, iface, face;

4275:   PetscFunctionBegin;
4279:   PetscAssertPointer(fgeom, 7);
4280:   PetscAssertPointer(vol, 8);
4281:   PetscCall(DMGetDimension(dm, &dim));
4282:   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
4283:   PetscCall(VecGetDM(faceGeometry, &dmFace));
4284:   PetscCall(VecGetArrayRead(faceGeometry, &facegeom));
4285:   PetscCall(VecGetDM(cellGeometry, &dmCell));
4286:   PetscCall(VecGetArrayRead(cellGeometry, &cellgeom));
4287:   PetscCall(PetscMalloc1(numFaces, fgeom));
4288:   PetscCall(DMGetWorkArray(dm, numFaces * 2, MPIU_SCALAR, vol));
4289:   for (face = fStart, iface = 0; face < fEnd; ++face) {
4290:     const PetscInt  *cells;
4291:     PetscFVFaceGeom *fg;
4292:     PetscFVCellGeom *cgL, *cgR;
4293:     PetscFVFaceGeom *fgeoml = *fgeom;
4294:     PetscReal       *voll   = *vol;
4295:     PetscInt         ghost, d, nchild, nsupp;

4297:     PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
4298:     PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
4299:     PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
4300:     if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
4301:     PetscCall(DMPlexPointLocalRead(dmFace, face, facegeom, &fg));
4302:     PetscCall(DMPlexGetSupport(dm, face, &cells));
4303:     PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL));
4304:     PetscCall(DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR));
4305:     for (d = 0; d < dim; ++d) {
4306:       fgeoml[iface].centroid[d] = fg->centroid[d];
4307:       fgeoml[iface].normal[d]   = fg->normal[d];
4308:     }
4309:     voll[iface * 2 + 0] = cgL->volume;
4310:     voll[iface * 2 + 1] = cgR->volume;
4311:     ++iface;
4312:   }
4313:   *Nface = iface;
4314:   PetscCall(VecRestoreArrayRead(faceGeometry, &facegeom));
4315:   PetscCall(VecRestoreArrayRead(cellGeometry, &cellgeom));
4316:   PetscFunctionReturn(PETSC_SUCCESS);
4317: }

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

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

4329:   Output Parameters:
4330: + Nface - The number of faces with field values
4331: . fgeom - The face centroid and normals
4332: - vol   - The cell volumes

4334:   Level: developer

4336: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
4337: @*/
4338: PetscErrorCode DMPlexRestoreFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom *fgeom[], PetscReal *vol[])
4339: {
4340:   PetscFunctionBegin;
4341:   PetscCall(PetscFree(*fgeom));
4342:   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_REAL, vol));
4343:   PetscFunctionReturn(PETSC_SUCCESS);
4344: }

4346: PetscErrorCode DMSNESGetFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscFEGeomMode mode, PetscFEGeom **geom)
4347: {
4348:   char           composeStr[33] = {0};
4349:   PetscObjectId  id;
4350:   PetscContainer container;

4352:   PetscFunctionBegin;
4353:   PetscCall(PetscObjectGetId((PetscObject)quad, &id));
4354:   PetscCall(PetscSNPrintf(composeStr, 32, "DMSNESGetFEGeom_%" PetscInt64_FMT "\n", id));
4355:   PetscCall(PetscObjectQuery((PetscObject)pointIS, composeStr, (PetscObject *)&container));
4356:   if (container) {
4357:     PetscCall(PetscContainerGetPointer(container, (void **)geom));
4358:   } else {
4359:     PetscCall(DMFieldCreateFEGeom(coordField, pointIS, quad, mode, geom));
4360:     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &container));
4361:     PetscCall(PetscContainerSetPointer(container, (void *)*geom));
4362:     PetscCall(PetscContainerSetCtxDestroy(container, PetscContainerCtxDestroy_PetscFEGeom));
4363:     PetscCall(PetscObjectCompose((PetscObject)pointIS, composeStr, (PetscObject)container));
4364:     PetscCall(PetscContainerDestroy(&container));
4365:   }
4366:   PetscFunctionReturn(PETSC_SUCCESS);
4367: }

4369: PetscErrorCode DMSNESRestoreFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
4370: {
4371:   PetscFunctionBegin;
4372:   *geom = NULL;
4373:   PetscFunctionReturn(PETSC_SUCCESS);
4374: }

4376: PetscErrorCode DMPlexComputeResidual_Patch_Internal(DM dm, PetscSection section, IS cellIS, PetscReal t, Vec locX, Vec locX_t, Vec locF, void *user)
4377: {
4378:   DM_Plex        *mesh       = (DM_Plex *)dm->data;
4379:   const char     *name       = "Residual";
4380:   DM              dmAux      = NULL;
4381:   DMLabel         ghostLabel = NULL;
4382:   PetscDS         prob       = NULL;
4383:   PetscDS         probAux    = NULL;
4384:   PetscBool       useFEM     = PETSC_FALSE;
4385:   PetscBool       isImplicit = (locX_t || t == PETSC_MIN_REAL) ? PETSC_TRUE : PETSC_FALSE;
4386:   DMField         coordField = NULL;
4387:   Vec             locA;
4388:   PetscScalar    *u = NULL, *u_t, *a, *uL = NULL, *uR = NULL;
4389:   IS              chunkIS;
4390:   const PetscInt *cells;
4391:   PetscInt        cStart, cEnd, numCells;
4392:   PetscInt        Nf, f, totDim, totDimAux, numChunks, cellChunkSize, chunk, fStart, fEnd;
4393:   PetscInt        maxDegree = PETSC_INT_MAX;
4394:   PetscFormKey    key;
4395:   PetscQuadrature affineQuad = NULL, *quads = NULL;
4396:   PetscFEGeom    *affineGeom = NULL, **geoms = NULL;

4398:   PetscFunctionBegin;
4399:   PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
4400:   /* FEM+FVM */
4401:   /* 1: Get sizes from dm and dmAux */
4402:   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
4403:   PetscCall(DMGetDS(dm, &prob));
4404:   PetscCall(PetscDSGetNumFields(prob, &Nf));
4405:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
4406:   PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA));
4407:   if (locA) {
4408:     PetscCall(VecGetDM(locA, &dmAux));
4409:     PetscCall(DMGetDS(dmAux, &probAux));
4410:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
4411:   }
4412:   /* 2: Get geometric data */
4413:   for (f = 0; f < Nf; ++f) {
4414:     PetscObject  obj;
4415:     PetscClassId id;
4416:     PetscBool    fimp;

4418:     PetscCall(PetscDSGetImplicit(prob, f, &fimp));
4419:     if (isImplicit != fimp) continue;
4420:     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4421:     PetscCall(PetscObjectGetClassId(obj, &id));
4422:     if (id == PETSCFE_CLASSID) useFEM = PETSC_TRUE;
4423:     PetscCheck(id != PETSCFV_CLASSID, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Use of FVM with PCPATCH not yet implemented");
4424:   }
4425:   if (useFEM) {
4426:     PetscCall(DMGetCoordinateField(dm, &coordField));
4427:     PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
4428:     if (maxDegree <= 1) {
4429:       PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad));
4430:       if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, cellIS, affineQuad, PETSC_FEGEOM_BASIC, &affineGeom));
4431:     } else {
4432:       PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
4433:       for (f = 0; f < Nf; ++f) {
4434:         PetscObject  obj;
4435:         PetscClassId id;
4436:         PetscBool    fimp;

4438:         PetscCall(PetscDSGetImplicit(prob, f, &fimp));
4439:         if (isImplicit != fimp) continue;
4440:         PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4441:         PetscCall(PetscObjectGetClassId(obj, &id));
4442:         if (id == PETSCFE_CLASSID) {
4443:           PetscFE fe = (PetscFE)obj;

4445:           PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
4446:           PetscCall(PetscObjectReference((PetscObject)quads[f]));
4447:           PetscCall(DMSNESGetFEGeom(coordField, cellIS, quads[f], PETSC_FEGEOM_BASIC, &geoms[f]));
4448:         }
4449:       }
4450:     }
4451:   }
4452:   /* Loop over chunks */
4453:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
4454:   PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
4455:   if (useFEM) PetscCall(ISCreate(PETSC_COMM_SELF, &chunkIS));
4456:   numCells      = cEnd - cStart;
4457:   numChunks     = 1;
4458:   cellChunkSize = numCells / numChunks;
4459:   numChunks     = PetscMin(1, numCells);
4460:   key.label     = NULL;
4461:   key.value     = 0;
4462:   key.part      = 0;
4463:   for (chunk = 0; chunk < numChunks; ++chunk) {
4464:     PetscScalar     *elemVec, *fluxL = NULL, *fluxR = NULL;
4465:     PetscReal       *vol   = NULL;
4466:     PetscFVFaceGeom *fgeom = NULL;
4467:     PetscInt         cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
4468:     PetscInt         numFaces = 0;

4470:     /* Extract field coefficients */
4471:     if (useFEM) {
4472:       PetscCall(ISGetPointSubrange(chunkIS, cS, cE, cells));
4473:       PetscCall(DMPlexGetCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
4474:       PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
4475:       PetscCall(PetscArrayzero(elemVec, numCells * totDim));
4476:     }
4477:     /* TODO We will interlace both our field coefficients (u, u_t, uL, uR, etc.) and our output (elemVec, fL, fR). I think this works */
4478:     /* Loop over fields */
4479:     for (f = 0; f < Nf; ++f) {
4480:       PetscObject  obj;
4481:       PetscClassId id;
4482:       PetscBool    fimp;
4483:       PetscInt     numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;

4485:       key.field = f;
4486:       PetscCall(PetscDSGetImplicit(prob, f, &fimp));
4487:       if (isImplicit != fimp) continue;
4488:       PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4489:       PetscCall(PetscObjectGetClassId(obj, &id));
4490:       if (id == PETSCFE_CLASSID) {
4491:         PetscFE         fe        = (PetscFE)obj;
4492:         PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[f];
4493:         PetscFEGeom    *chunkGeom = NULL;
4494:         PetscQuadrature quad      = affineQuad ? affineQuad : quads[f];
4495:         PetscInt        Nq, Nb;

4497:         PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
4498:         PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
4499:         PetscCall(PetscFEGetDimension(fe, &Nb));
4500:         blockSize = Nb;
4501:         batchSize = numBlocks * blockSize;
4502:         PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
4503:         numChunks = numCells / (numBatches * batchSize);
4504:         Ne        = numChunks * numBatches * batchSize;
4505:         Nr        = numCells % (numBatches * batchSize);
4506:         offset    = numCells - Nr;
4507:         /* Integrate FE residual to get elemVec (need fields at quadrature points) */
4508:         /*   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) */
4509:         PetscCall(PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom));
4510:         PetscCall(PetscFEIntegrateResidual(prob, key, Ne, chunkGeom, u, u_t, probAux, a, t, elemVec));
4511:         PetscCall(PetscFEGeomGetChunk(geom, offset, numCells, &chunkGeom));
4512:         PetscCall(PetscFEIntegrateResidual(prob, key, Nr, chunkGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), probAux, &a[offset * totDimAux], t, &elemVec[offset * totDim]));
4513:         PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &chunkGeom));
4514:       } else if (id == PETSCFV_CLASSID) {
4515:         PetscFV fv = (PetscFV)obj;

4517:         Ne = numFaces;
4518:         /* Riemann solve over faces (need fields at face centroids) */
4519:         /*   We need to evaluate FE fields at those coordinates */
4520:         PetscCall(PetscFVIntegrateRHSFunction(fv, prob, f, Ne, fgeom, vol, uL, uR, fluxL, fluxR));
4521:       } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
4522:     }
4523:     /* Loop over domain */
4524:     if (useFEM) {
4525:       /* Add elemVec to locX */
4526:       for (c = cS; c < cE; ++c) {
4527:         const PetscInt cell = cells ? cells[c] : c;
4528:         const PetscInt cind = c - cStart;

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

4534:           PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
4535:           if (ghostVal > 0) continue;
4536:         }
4537:         PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind * totDim], ADD_ALL_VALUES));
4538:       }
4539:     }
4540:     /* Handle time derivative */
4541:     if (locX_t) {
4542:       PetscScalar *x_t, *fa;

4544:       PetscCall(VecGetArray(locF, &fa));
4545:       PetscCall(VecGetArray(locX_t, &x_t));
4546:       for (f = 0; f < Nf; ++f) {
4547:         PetscFV      fv;
4548:         PetscObject  obj;
4549:         PetscClassId id;
4550:         PetscInt     pdim, d;

4552:         PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4553:         PetscCall(PetscObjectGetClassId(obj, &id));
4554:         if (id != PETSCFV_CLASSID) continue;
4555:         fv = (PetscFV)obj;
4556:         PetscCall(PetscFVGetNumComponents(fv, &pdim));
4557:         for (c = cS; c < cE; ++c) {
4558:           const PetscInt cell = cells ? cells[c] : c;
4559:           PetscScalar   *u_t, *r;

4561:           if (ghostLabel) {
4562:             PetscInt ghostVal;

4564:             PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
4565:             if (ghostVal > 0) continue;
4566:           }
4567:           PetscCall(DMPlexPointLocalFieldRead(dm, cell, f, x_t, &u_t));
4568:           PetscCall(DMPlexPointLocalFieldRef(dm, cell, f, fa, &r));
4569:           for (d = 0; d < pdim; ++d) r[d] += u_t[d];
4570:         }
4571:       }
4572:       PetscCall(VecRestoreArray(locX_t, &x_t));
4573:       PetscCall(VecRestoreArray(locF, &fa));
4574:     }
4575:     if (useFEM) {
4576:       PetscCall(DMPlexRestoreCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
4577:       PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
4578:     }
4579:   }
4580:   if (useFEM) PetscCall(ISDestroy(&chunkIS));
4581:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
4582:   /* TODO Could include boundary residual here (see DMPlexComputeResidualByKey) */
4583:   if (useFEM) {
4584:     if (maxDegree <= 1) {
4585:       PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
4586:       PetscCall(PetscQuadratureDestroy(&affineQuad));
4587:     } else {
4588:       for (f = 0; f < Nf; ++f) {
4589:         PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
4590:         PetscCall(PetscQuadratureDestroy(&quads[f]));
4591:       }
4592:       PetscCall(PetscFree2(quads, geoms));
4593:     }
4594:   }
4595:   PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
4596:   PetscFunctionReturn(PETSC_SUCCESS);
4597: }

4599: /*
4600:   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

4602:   X   - The local solution vector
4603:   X_t - The local solution time derivative vector, or NULL
4604: */
4605: 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)
4606: {
4607:   DM_Plex        *mesh = (DM_Plex *)dm->data;
4608:   const char     *name = "Jacobian", *nameP = "JacobianPre";
4609:   DM              dmAux = NULL;
4610:   PetscDS         prob, probAux = NULL;
4611:   PetscSection    sectionAux = NULL;
4612:   Vec             A;
4613:   DMField         coordField;
4614:   PetscFEGeom    *cgeomFEM;
4615:   PetscQuadrature qGeom = NULL;
4616:   Mat             J = Jac, JP = JacP;
4617:   PetscScalar    *work, *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL, *elemMatP = NULL, *elemMatD = NULL;
4618:   PetscBool       hasJac, hasPrec, hasDyn, assembleJac, *isFE, hasFV = PETSC_FALSE;
4619:   const PetscInt *cells;
4620:   PetscFormKey    key;
4621:   PetscInt        Nf, fieldI, fieldJ, maxDegree, numCells, cStart, cEnd, numChunks, chunkSize, chunk, totDim, totDimAux = 0, sz, wsz, off = 0, offCell = 0;

4623:   PetscFunctionBegin;
4624:   PetscCall(ISGetLocalSize(cellIS, &numCells));
4625:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
4626:   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
4627:   PetscCall(DMGetDS(dm, &prob));
4628:   PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &A));
4629:   if (A) {
4630:     PetscCall(VecGetDM(A, &dmAux));
4631:     PetscCall(DMGetLocalSection(dmAux, &sectionAux));
4632:     PetscCall(DMGetDS(dmAux, &probAux));
4633:   }
4634:   /* Get flags */
4635:   PetscCall(PetscDSGetNumFields(prob, &Nf));
4636:   PetscCall(DMGetWorkArray(dm, Nf, MPIU_BOOL, &isFE));
4637:   for (fieldI = 0; fieldI < Nf; ++fieldI) {
4638:     PetscObject  disc;
4639:     PetscClassId id;
4640:     PetscCall(PetscDSGetDiscretization(prob, fieldI, &disc));
4641:     PetscCall(PetscObjectGetClassId(disc, &id));
4642:     if (id == PETSCFE_CLASSID) {
4643:       isFE[fieldI] = PETSC_TRUE;
4644:     } else if (id == PETSCFV_CLASSID) {
4645:       hasFV        = PETSC_TRUE;
4646:       isFE[fieldI] = PETSC_FALSE;
4647:     }
4648:   }
4649:   PetscCall(PetscDSHasJacobian(prob, &hasJac));
4650:   PetscCall(PetscDSHasJacobianPreconditioner(prob, &hasPrec));
4651:   PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
4652:   assembleJac = hasJac && hasPrec && (Jac != JacP) ? PETSC_TRUE : PETSC_FALSE;
4653:   hasDyn      = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
4654:   if (hasFV) PetscCall(MatSetOption(JP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE)); /* No allocated space for FV stuff, so ignore the zero entries */
4655:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
4656:   if (probAux) PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
4657:   /* Compute batch sizes */
4658:   if (isFE[0]) {
4659:     PetscFE         fe;
4660:     PetscQuadrature q;
4661:     PetscInt        numQuadPoints, numBatches, batchSize, numBlocks, blockSize, Nb;

4663:     PetscCall(PetscDSGetDiscretization(prob, 0, (PetscObject *)&fe));
4664:     PetscCall(PetscFEGetQuadrature(fe, &q));
4665:     PetscCall(PetscQuadratureGetData(q, NULL, NULL, &numQuadPoints, NULL, NULL));
4666:     PetscCall(PetscFEGetDimension(fe, &Nb));
4667:     PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
4668:     blockSize = Nb * numQuadPoints;
4669:     batchSize = numBlocks * blockSize;
4670:     chunkSize = numBatches * batchSize;
4671:     numChunks = numCells / chunkSize + numCells % chunkSize;
4672:     PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
4673:   } else {
4674:     chunkSize = numCells;
4675:     numChunks = 1;
4676:   }
4677:   /* Get work space */
4678:   wsz = (((X ? 1 : 0) + (X_t ? 1 : 0) + (dmAux ? 1 : 0)) * totDim + ((hasJac ? 1 : 0) + (hasPrec ? 1 : 0) + (hasDyn ? 1 : 0)) * totDim * totDim) * chunkSize;
4679:   PetscCall(DMGetWorkArray(dm, wsz, MPIU_SCALAR, &work));
4680:   PetscCall(PetscArrayzero(work, wsz));
4681:   off      = 0;
4682:   u        = X ? (sz = chunkSize * totDim, off += sz, work + off - sz) : NULL;
4683:   u_t      = X_t ? (sz = chunkSize * totDim, off += sz, work + off - sz) : NULL;
4684:   a        = dmAux ? (sz = chunkSize * totDimAux, off += sz, work + off - sz) : NULL;
4685:   elemMat  = hasJac ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL;
4686:   elemMatP = hasPrec ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL;
4687:   elemMatD = hasDyn ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL;
4688:   PetscCheck(off == wsz, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Error is workspace size %" PetscInt_FMT " should be %" PetscInt_FMT, off, wsz);
4689:   /* Setup geometry */
4690:   PetscCall(DMGetCoordinateField(dm, &coordField));
4691:   PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
4692:   if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
4693:   if (!qGeom) {
4694:     PetscFE fe;

4696:     PetscCall(PetscDSGetDiscretization(prob, 0, (PetscObject *)&fe));
4697:     PetscCall(PetscFEGetQuadrature(fe, &qGeom));
4698:     PetscCall(PetscObjectReference((PetscObject)qGeom));
4699:   }
4700:   PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FEGEOM_BASIC, &cgeomFEM));
4701:   /* Compute volume integrals */
4702:   if (assembleJac) PetscCall(MatZeroEntries(J));
4703:   PetscCall(MatZeroEntries(JP));
4704:   key.label = NULL;
4705:   key.value = 0;
4706:   key.part  = 0;
4707:   for (chunk = 0; chunk < numChunks; ++chunk, offCell += chunkSize) {
4708:     const PetscInt Ncell = PetscMin(chunkSize, numCells - offCell);
4709:     PetscInt       c;

4711:     /* Extract values */
4712:     for (c = 0; c < Ncell; ++c) {
4713:       const PetscInt cell = cells ? cells[c + offCell] : c + offCell;
4714:       PetscScalar   *x = NULL, *x_t = NULL;
4715:       PetscInt       i;

4717:       if (X) {
4718:         PetscCall(DMPlexVecGetClosure(dm, section, X, cell, NULL, &x));
4719:         for (i = 0; i < totDim; ++i) u[c * totDim + i] = x[i];
4720:         PetscCall(DMPlexVecRestoreClosure(dm, section, X, cell, NULL, &x));
4721:       }
4722:       if (X_t) {
4723:         PetscCall(DMPlexVecGetClosure(dm, section, X_t, cell, NULL, &x_t));
4724:         for (i = 0; i < totDim; ++i) u_t[c * totDim + i] = x_t[i];
4725:         PetscCall(DMPlexVecRestoreClosure(dm, section, X_t, cell, NULL, &x_t));
4726:       }
4727:       if (dmAux) {
4728:         PetscCall(DMPlexVecGetClosure(dmAux, sectionAux, A, cell, NULL, &x));
4729:         for (i = 0; i < totDimAux; ++i) a[c * totDimAux + i] = x[i];
4730:         PetscCall(DMPlexVecRestoreClosure(dmAux, sectionAux, A, cell, NULL, &x));
4731:       }
4732:     }
4733:     for (fieldI = 0; fieldI < Nf; ++fieldI) {
4734:       PetscFE fe;
4735:       PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
4736:       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
4737:         key.field = fieldI * Nf + fieldJ;
4738:         if (hasJac) PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMat));
4739:         if (hasPrec) PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatP));
4740:         if (hasDyn) PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatD));
4741:       }
4742:       /* For finite volume, add the identity */
4743:       if (!isFE[fieldI]) {
4744:         PetscFV  fv;
4745:         PetscInt eOffset = 0, Nc, fc, foff;

4747:         PetscCall(PetscDSGetFieldOffset(prob, fieldI, &foff));
4748:         PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fv));
4749:         PetscCall(PetscFVGetNumComponents(fv, &Nc));
4750:         for (c = 0; c < chunkSize; ++c, eOffset += totDim * totDim) {
4751:           for (fc = 0; fc < Nc; ++fc) {
4752:             const PetscInt i = foff + fc;
4753:             if (hasJac) elemMat[eOffset + i * totDim + i] = 1.0;
4754:             if (hasPrec) elemMatP[eOffset + i * totDim + i] = 1.0;
4755:           }
4756:         }
4757:       }
4758:     }
4759:     /*   Add contribution from X_t */
4760:     if (hasDyn) {
4761:       for (c = 0; c < chunkSize * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
4762:     }
4763:     /* Insert values into matrix */
4764:     for (c = 0; c < Ncell; ++c) {
4765:       const PetscInt cell = cells ? cells[c + offCell] : c + offCell;
4766:       if (mesh->printFEM > 1) {
4767:         if (hasJac) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[(c - cStart) * totDim * totDim]));
4768:         if (hasPrec) PetscCall(DMPrintCellMatrix(cell, nameP, totDim, totDim, &elemMatP[(c - cStart) * totDim * totDim]));
4769:       }
4770:       if (assembleJac) PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, Jac, cell, &elemMat[(c - cStart) * totDim * totDim], ADD_VALUES));
4771:       PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, JP, cell, &elemMat[(c - cStart) * totDim * totDim], ADD_VALUES));
4772:     }
4773:   }
4774:   /* Cleanup */
4775:   PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
4776:   PetscCall(PetscQuadratureDestroy(&qGeom));
4777:   if (hasFV) PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE));
4778:   PetscCall(DMRestoreWorkArray(dm, Nf, MPIU_BOOL, &isFE));
4779:   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));
4780:   /* Compute boundary integrals */
4781:   /* PetscCall(DMPlexComputeBdJacobian_Internal(dm, X, X_t, t, X_tShift, Jac, JacP, ctx)); */
4782:   /* Assemble matrix */
4783:   if (assembleJac) {
4784:     PetscCall(MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY));
4785:     PetscCall(MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY));
4786:   }
4787:   PetscCall(MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY));
4788:   PetscCall(MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY));
4789:   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
4790:   PetscFunctionReturn(PETSC_SUCCESS);
4791: }

4793: /* FEM Assembly Function */

4795: static PetscErrorCode DMConvertPlex_Internal(DM dm, DM *plex, PetscBool copy)
4796: {
4797:   PetscBool isPlex;

4799:   PetscFunctionBegin;
4800:   PetscCall(PetscObjectTypeCompare((PetscObject)dm, DMPLEX, &isPlex));
4801:   if (isPlex) {
4802:     *plex = dm;
4803:     PetscCall(PetscObjectReference((PetscObject)dm));
4804:   } else {
4805:     PetscCall(PetscObjectQuery((PetscObject)dm, "dm_plex", (PetscObject *)plex));
4806:     if (!*plex) {
4807:       PetscCall(DMConvert(dm, DMPLEX, plex));
4808:       PetscCall(PetscObjectCompose((PetscObject)dm, "dm_plex", (PetscObject)*plex));
4809:     } else {
4810:       PetscCall(PetscObjectReference((PetscObject)*plex));
4811:     }
4812:     if (copy) PetscCall(DMCopyAuxiliaryVec(dm, *plex));
4813:   }
4814:   PetscFunctionReturn(PETSC_SUCCESS);
4815: }

4817: /*@
4818:   DMPlexGetGeometryFVM - Return precomputed geometric data

4820:   Collective

4822:   Input Parameter:
4823: . dm - The `DM`

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

4830:   Level: developer

4832: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMTSSetRHSFunctionLocal()`
4833: @*/
4834: PetscErrorCode DMPlexGetGeometryFVM(DM dm, Vec *facegeom, Vec *cellgeom, PeOp PetscReal *minRadius)
4835: {
4836:   DM plex;

4838:   PetscFunctionBegin;
4840:   PetscCall(DMConvertPlex_Internal(dm, &plex, PETSC_TRUE));
4841:   PetscCall(DMPlexGetDataFVM(plex, NULL, cellgeom, facegeom, NULL));
4842:   if (minRadius) PetscCall(DMPlexGetMinRadius(plex, minRadius));
4843:   PetscCall(DMDestroy(&plex));
4844:   PetscFunctionReturn(PETSC_SUCCESS);
4845: }

4847: /*@
4848:   DMPlexGetGradientDM - Return gradient data layout

4850:   Collective

4852:   Input Parameters:
4853: + dm - The `DM`
4854: - fv - The `PetscFV`

4856:   Output Parameter:
4857: . dmGrad - The layout for gradient values

4859:   Level: developer

4861: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetGeometryFVM()`
4862: @*/
4863: PetscErrorCode DMPlexGetGradientDM(DM dm, PetscFV fv, DM *dmGrad)
4864: {
4865:   DM        plex;
4866:   PetscBool computeGradients;

4868:   PetscFunctionBegin;
4871:   PetscAssertPointer(dmGrad, 3);
4872:   PetscCall(PetscFVGetComputeGradients(fv, &computeGradients));
4873:   if (!computeGradients) {
4874:     *dmGrad = NULL;
4875:     PetscFunctionReturn(PETSC_SUCCESS);
4876:   }
4877:   PetscCall(DMConvertPlex_Internal(dm, &plex, PETSC_TRUE));
4878:   PetscCall(DMPlexGetDataFVM(plex, fv, NULL, NULL, dmGrad));
4879:   PetscCall(DMDestroy(&plex));
4880:   PetscFunctionReturn(PETSC_SUCCESS);
4881: }

4883: static PetscErrorCode DMPlexComputeBdResidual_Single_Internal(DM dm, PetscReal t, PetscWeakForm wf, PetscFormKey key, Vec locX, Vec locX_t, Vec locF, DMField coordField, IS facetIS)
4884: {
4885:   DM_Plex        *mesh = (DM_Plex *)dm->data;
4886:   DM              plex = NULL, plexA = NULL;
4887:   const char     *name = "BdResidual";
4888:   DMEnclosureType encAux;
4889:   PetscDS         prob, probAux       = NULL;
4890:   PetscSection    section, sectionAux = NULL;
4891:   Vec             locA = NULL;
4892:   PetscScalar    *u = NULL, *u_t = NULL, *a = NULL, *elemVec = NULL;
4893:   PetscInt        totDim, totDimAux = 0;

4895:   PetscFunctionBegin;
4896:   PetscCall(DMConvert(dm, DMPLEX, &plex));
4897:   PetscCall(DMGetLocalSection(dm, &section));
4898:   PetscCall(DMGetDS(dm, &prob));
4899:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
4900:   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &locA));
4901:   if (locA) {
4902:     DM dmAux;

4904:     PetscCall(VecGetDM(locA, &dmAux));
4905:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
4906:     PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
4907:     PetscCall(DMGetDS(plexA, &probAux));
4908:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
4909:     PetscCall(DMGetLocalSection(plexA, &sectionAux));
4910:   }
4911:   {
4912:     PetscFEGeom    *fgeom;
4913:     PetscInt        maxDegree;
4914:     PetscQuadrature qGeom = NULL;
4915:     IS              pointIS;
4916:     const PetscInt *points;
4917:     PetscInt        numFaces, face, Nq;

4919:     PetscCall(DMLabelGetStratumIS(key.label, key.value, &pointIS));
4920:     if (!pointIS) goto end; /* No points with that id on this process */
4921:     {
4922:       IS isectIS;

4924:       /* TODO: Special cases of ISIntersect where it is quick to check a priori if one is a superset of the other */
4925:       PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS));
4926:       PetscCall(ISDestroy(&pointIS));
4927:       pointIS = isectIS;
4928:     }
4929:     PetscCall(ISGetLocalSize(pointIS, &numFaces));
4930:     PetscCall(ISGetIndices(pointIS, &points));
4931:     PetscCall(PetscMalloc4(numFaces * totDim, &u, (locX_t ? (size_t)numFaces * totDim : 0), &u_t, numFaces * totDim, &elemVec, (locA ? (size_t)numFaces * totDimAux : 0), &a));
4932:     PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
4933:     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom));
4934:     if (!qGeom) {
4935:       PetscFE fe;

4937:       PetscCall(PetscDSGetDiscretization(prob, key.field, (PetscObject *)&fe));
4938:       PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
4939:       PetscCall(PetscObjectReference((PetscObject)qGeom));
4940:     }
4941:     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
4942:     PetscCall(DMSNESGetFEGeom(coordField, pointIS, qGeom, PETSC_FEGEOM_BOUNDARY, &fgeom));
4943:     for (face = 0; face < numFaces; ++face) {
4944:       const PetscInt point = points[face], *support;
4945:       PetscScalar   *x     = NULL;
4946:       PetscInt       i;

4948:       PetscCall(DMPlexGetSupport(dm, point, &support));
4949:       PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
4950:       for (i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
4951:       PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
4952:       if (locX_t) {
4953:         PetscCall(DMPlexVecGetClosure(plex, section, locX_t, support[0], NULL, &x));
4954:         for (i = 0; i < totDim; ++i) u_t[face * totDim + i] = x[i];
4955:         PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, support[0], NULL, &x));
4956:       }
4957:       if (locA) {
4958:         PetscInt subp;

4960:         PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
4961:         PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
4962:         for (i = 0; i < totDimAux; ++i) a[face * totDimAux + i] = x[i];
4963:         PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
4964:       }
4965:     }
4966:     PetscCall(PetscArrayzero(elemVec, numFaces * totDim));
4967:     {
4968:       PetscFE      fe;
4969:       PetscInt     Nb;
4970:       PetscFEGeom *chunkGeom = NULL;
4971:       /* Conforming batches */
4972:       PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
4973:       /* Remainder */
4974:       PetscInt Nr, offset;

4976:       PetscCall(PetscDSGetDiscretization(prob, key.field, (PetscObject *)&fe));
4977:       PetscCall(PetscFEGetDimension(fe, &Nb));
4978:       PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
4979:       /* TODO: documentation is unclear about what is going on with these numbers: how should Nb / Nq factor in ? */
4980:       blockSize = Nb;
4981:       batchSize = numBlocks * blockSize;
4982:       PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
4983:       numChunks = numFaces / (numBatches * batchSize);
4984:       Ne        = numChunks * numBatches * batchSize;
4985:       Nr        = numFaces % (numBatches * batchSize);
4986:       offset    = numFaces - Nr;
4987:       PetscCall(PetscFEGeomGetChunk(fgeom, 0, offset, &chunkGeom));
4988:       PetscCall(PetscFEIntegrateBdResidual(prob, wf, key, Ne, chunkGeom, u, u_t, probAux, a, t, elemVec));
4989:       PetscCall(PetscFEGeomRestoreChunk(fgeom, 0, offset, &chunkGeom));
4990:       PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom));
4991:       PetscCall(PetscFEIntegrateBdResidual(prob, wf, key, Nr, chunkGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), probAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, &elemVec[offset * totDim]));
4992:       PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom));
4993:     }
4994:     for (face = 0; face < numFaces; ++face) {
4995:       const PetscInt point = points[face], *support;

4997:       if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(point, name, totDim, &elemVec[face * totDim]));
4998:       PetscCall(DMPlexGetSupport(plex, point, &support));
4999:       PetscCall(DMPlexVecSetClosure(plex, NULL, locF, support[0], &elemVec[face * totDim], ADD_ALL_VALUES));
5000:     }
5001:     PetscCall(DMSNESRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
5002:     PetscCall(PetscQuadratureDestroy(&qGeom));
5003:     PetscCall(ISRestoreIndices(pointIS, &points));
5004:     PetscCall(ISDestroy(&pointIS));
5005:     PetscCall(PetscFree4(u, u_t, elemVec, a));
5006:   }
5007: end:
5008:   if (mesh->printFEM) {
5009:     PetscSection s;
5010:     Vec          locFbc;
5011:     PetscInt     pStart, pEnd, maxDof;
5012:     PetscScalar *zeroes;

5014:     PetscCall(DMGetLocalSection(dm, &s));
5015:     PetscCall(VecDuplicate(locF, &locFbc));
5016:     PetscCall(VecCopy(locF, locFbc));
5017:     PetscCall(PetscSectionGetChart(s, &pStart, &pEnd));
5018:     PetscCall(PetscSectionGetMaxDof(s, &maxDof));
5019:     PetscCall(PetscCalloc1(maxDof, &zeroes));
5020:     for (PetscInt p = pStart; p < pEnd; p++) PetscCall(VecSetValuesSection(locFbc, s, p, zeroes, INSERT_BC_VALUES));
5021:     PetscCall(PetscFree(zeroes));
5022:     PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc));
5023:     PetscCall(VecDestroy(&locFbc));
5024:   }
5025:   PetscCall(DMDestroy(&plex));
5026:   PetscCall(DMDestroy(&plexA));
5027:   PetscFunctionReturn(PETSC_SUCCESS);
5028: }

5030: PetscErrorCode DMPlexComputeBdResidualSingle(DM dm, PetscReal t, PetscWeakForm wf, PetscFormKey key, Vec locX, Vec locX_t, Vec locF)
5031: {
5032:   DMField  coordField;
5033:   DMLabel  depthLabel;
5034:   IS       facetIS;
5035:   PetscInt dim;

5037:   PetscFunctionBegin;
5038:   PetscCall(DMGetDimension(dm, &dim));
5039:   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5040:   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
5041:   PetscCall(DMGetCoordinateField(dm, &coordField));
5042:   PetscCall(DMPlexComputeBdResidual_Single_Internal(dm, t, wf, key, locX, locX_t, locF, coordField, facetIS));
5043:   PetscCall(ISDestroy(&facetIS));
5044:   PetscFunctionReturn(PETSC_SUCCESS);
5045: }

5047: static PetscErrorCode DMPlexComputeBdResidual_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
5048: {
5049:   PetscDS  prob;
5050:   PetscInt numBd, bd;
5051:   DMField  coordField = NULL;
5052:   IS       facetIS    = NULL;
5053:   DMLabel  depthLabel;
5054:   PetscInt dim;

5056:   PetscFunctionBegin;
5057:   PetscCall(DMGetDS(dm, &prob));
5058:   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5059:   PetscCall(DMGetDimension(dm, &dim));
5060:   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
5061:   PetscCall(PetscDSGetNumBoundary(prob, &numBd));
5062:   for (bd = 0; bd < numBd; ++bd) {
5063:     PetscWeakForm           wf;
5064:     DMBoundaryConditionType type;
5065:     DMLabel                 label;
5066:     const PetscInt         *values;
5067:     PetscInt                field, numValues, v;
5068:     PetscObject             obj;
5069:     PetscClassId            id;
5070:     PetscFormKey            key;

5072:     PetscCall(PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &field, NULL, NULL, NULL, NULL, NULL));
5073:     if (type & DM_BC_ESSENTIAL) continue;
5074:     PetscCall(PetscDSGetDiscretization(prob, field, &obj));
5075:     PetscCall(PetscObjectGetClassId(obj, &id));
5076:     if (id != PETSCFE_CLASSID) continue;
5077:     if (!facetIS) {
5078:       DMLabel  depthLabel;
5079:       PetscInt dim;

5081:       PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5082:       PetscCall(DMGetDimension(dm, &dim));
5083:       PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
5084:     }
5085:     PetscCall(DMGetCoordinateField(dm, &coordField));
5086:     for (v = 0; v < numValues; ++v) {
5087:       key.label = label;
5088:       key.value = values[v];
5089:       key.field = field;
5090:       key.part  = 0;
5091:       PetscCall(DMPlexComputeBdResidual_Single_Internal(dm, t, wf, key, locX, locX_t, locF, coordField, facetIS));
5092:     }
5093:   }
5094:   PetscCall(ISDestroy(&facetIS));
5095:   PetscFunctionReturn(PETSC_SUCCESS);
5096: }

5098: /*@
5099:   DMPlexComputeResidualByKey - Compute the local residual for terms matching the input key

5101:   Collective

5103:   Input Parameters:
5104: + dm     - The output `DM`
5105: . key    - The `PetscFormKey` indicating what should be integrated
5106: . cellIS - The `IS` giving a set of cells to integrate over
5107: . time   - The time, or `PETSC_MIN_REAL` to include implicit terms in a time-independent problems
5108: . locX   - The local solution
5109: . locX_t - The time derivative of the local solution, or `NULL` for time-independent problems
5110: . t      - The time
5111: - user   - An optional user context, passed to the pointwise functions

5113:   Output Parameter:
5114: . locF - The local residual

5116:   Level: developer

5118: .seealso: `DMPlexComputeJacobianByKey()`, `DMPlexComputeResidualHybridByKey()`, `DMPlexComputeJacobianHybridByKey()`, `PetscFormKey`
5119: @*/
5120: PetscErrorCode DMPlexComputeResidualByKey(DM dm, PetscFormKey key, IS cellIS, PetscReal time, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
5121: {
5122:   DM_Plex        *mesh       = (DM_Plex *)dm->data;
5123:   const char     *name       = "Residual";
5124:   DM              dmAux      = NULL;
5125:   DM              dmGrad     = NULL;
5126:   DMLabel         ghostLabel = NULL;
5127:   PetscDS         ds         = NULL;
5128:   PetscDS         dsAux      = NULL;
5129:   PetscSection    section    = NULL;
5130:   PetscBool       useFEM     = PETSC_FALSE;
5131:   PetscBool       useFVM     = PETSC_FALSE;
5132:   PetscBool       isImplicit = (locX_t || time == PETSC_MIN_REAL) ? PETSC_TRUE : PETSC_FALSE;
5133:   PetscFV         fvm        = NULL;
5134:   DMField         coordField = NULL;
5135:   Vec             locA, cellGeometryFVM = NULL, faceGeometryFVM = NULL, locGrad = NULL;
5136:   PetscScalar    *u = NULL, *u_t, *a, *uL, *uR;
5137:   IS              chunkIS;
5138:   const PetscInt *cells;
5139:   PetscInt        cStart, cEnd, numCells;
5140:   PetscInt        Nf, f, totDim, totDimAux, numChunks, cellChunkSize, faceChunkSize, chunk, fStart, fEnd;
5141:   PetscInt        maxDegree  = PETSC_INT_MAX;
5142:   PetscQuadrature affineQuad = NULL, *quads = NULL;
5143:   PetscFEGeom    *affineGeom = NULL, **geoms = NULL;

5145:   PetscFunctionBegin;
5146:   PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
5147:   if (!cellIS) goto end;
5148:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
5149:   if (cStart >= cEnd) goto end;
5150:   /* TODO The places where we have to use isFE are probably the member functions for the PetscDisc class */
5151:   /* TODO The FVM geometry is over-manipulated. Make the precalc functions return exactly what we need */
5152:   /* FEM+FVM */
5153:   PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
5154:   /* 1: Get sizes from dm and dmAux */
5155:   PetscCall(DMGetLocalSection(dm, &section));
5156:   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
5157:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, NULL));
5158:   PetscCall(PetscDSGetNumFields(ds, &Nf));
5159:   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
5160:   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &locA));
5161:   if (locA) {
5162:     PetscInt subcell;
5163:     PetscCall(VecGetDM(locA, &dmAux));
5164:     PetscCall(DMGetEnclosurePoint(dmAux, dm, DM_ENC_UNKNOWN, cells ? cells[cStart] : cStart, &subcell));
5165:     PetscCall(DMGetCellDS(dmAux, subcell, &dsAux, NULL));
5166:     PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
5167:   }
5168:   /* 2: Get geometric data */
5169:   for (f = 0; f < Nf; ++f) {
5170:     PetscObject  obj;
5171:     PetscClassId id;
5172:     PetscBool    fimp;

5174:     PetscCall(PetscDSGetImplicit(ds, f, &fimp));
5175:     if (isImplicit != fimp) continue;
5176:     PetscCall(PetscDSGetDiscretization(ds, f, &obj));
5177:     PetscCall(PetscObjectGetClassId(obj, &id));
5178:     if (id == PETSCFE_CLASSID) useFEM = PETSC_TRUE;
5179:     if (id == PETSCFV_CLASSID) {
5180:       useFVM = PETSC_TRUE;
5181:       fvm    = (PetscFV)obj;
5182:     }
5183:   }
5184:   if (useFEM) {
5185:     PetscCall(DMGetCoordinateField(dm, &coordField));
5186:     PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
5187:     if (maxDegree <= 1) {
5188:       PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad));
5189:       if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, cellIS, affineQuad, PETSC_FEGEOM_BASIC, &affineGeom));
5190:     } else {
5191:       PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
5192:       for (f = 0; f < Nf; ++f) {
5193:         PetscObject  obj;
5194:         PetscClassId id;
5195:         PetscBool    fimp;

5197:         PetscCall(PetscDSGetImplicit(ds, f, &fimp));
5198:         if (isImplicit != fimp) continue;
5199:         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
5200:         PetscCall(PetscObjectGetClassId(obj, &id));
5201:         if (id == PETSCFE_CLASSID) {
5202:           PetscFE fe = (PetscFE)obj;

5204:           PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
5205:           PetscCall(PetscObjectReference((PetscObject)quads[f]));
5206:           PetscCall(DMSNESGetFEGeom(coordField, cellIS, quads[f], PETSC_FEGEOM_BASIC, &geoms[f]));
5207:         }
5208:       }
5209:     }
5210:   }
5211:   // Handle non-essential (e.g. outflow) boundary values
5212:   if (useFVM) {
5213:     PetscCall(DMPlexInsertBoundaryValuesFVM(dm, fvm, locX, time, &locGrad));
5214:     PetscCall(DMPlexGetGeometryFVM(dm, &faceGeometryFVM, &cellGeometryFVM, NULL));
5215:     PetscCall(DMPlexGetGradientDM(dm, fvm, &dmGrad));
5216:   }
5217:   /* Loop over chunks */
5218:   if (useFEM) PetscCall(ISCreate(PETSC_COMM_SELF, &chunkIS));
5219:   numCells      = cEnd - cStart;
5220:   numChunks     = 1;
5221:   cellChunkSize = numCells / numChunks;
5222:   faceChunkSize = (fEnd - fStart) / numChunks;
5223:   numChunks     = PetscMin(1, numCells);
5224:   for (chunk = 0; chunk < numChunks; ++chunk) {
5225:     PetscScalar     *elemVec, *fluxL, *fluxR;
5226:     PetscReal       *vol;
5227:     PetscFVFaceGeom *fgeom;
5228:     PetscInt         cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
5229:     PetscInt         fS = fStart + chunk * faceChunkSize, fE = PetscMin(fS + faceChunkSize, fEnd), numFaces = 0, face;

5231:     /* Extract field coefficients */
5232:     if (useFEM) {
5233:       PetscCall(ISGetPointSubrange(chunkIS, cS, cE, cells));
5234:       PetscCall(DMPlexGetCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
5235:       PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
5236:       PetscCall(PetscArrayzero(elemVec, numCells * totDim));
5237:     }
5238:     if (useFVM) {
5239:       PetscCall(DMPlexGetFaceFields(dm, fS, fE, locX, locX_t, faceGeometryFVM, cellGeometryFVM, locGrad, &numFaces, &uL, &uR));
5240:       PetscCall(DMPlexGetFaceGeometry(dm, fS, fE, faceGeometryFVM, cellGeometryFVM, &numFaces, &fgeom, &vol));
5241:       PetscCall(DMGetWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxL));
5242:       PetscCall(DMGetWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxR));
5243:       PetscCall(PetscArrayzero(fluxL, numFaces * totDim));
5244:       PetscCall(PetscArrayzero(fluxR, numFaces * totDim));
5245:     }
5246:     /* TODO We will interlace both our field coefficients (u, u_t, uL, uR, etc.) and our output (elemVec, fL, fR). I think this works */
5247:     /* Loop over fields */
5248:     for (f = 0; f < Nf; ++f) {
5249:       PetscObject  obj;
5250:       PetscClassId id;
5251:       PetscBool    fimp;
5252:       PetscInt     numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;

5254:       key.field = f;
5255:       PetscCall(PetscDSGetImplicit(ds, f, &fimp));
5256:       if (isImplicit != fimp) continue;
5257:       PetscCall(PetscDSGetDiscretization(ds, f, &obj));
5258:       PetscCall(PetscObjectGetClassId(obj, &id));
5259:       if (id == PETSCFE_CLASSID) {
5260:         PetscFE         fe        = (PetscFE)obj;
5261:         PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[f];
5262:         PetscFEGeom    *chunkGeom = NULL;
5263:         PetscQuadrature quad      = affineQuad ? affineQuad : quads[f];
5264:         PetscInt        Nq, Nb;

5266:         PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5267:         PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
5268:         PetscCall(PetscFEGetDimension(fe, &Nb));
5269:         blockSize = Nb;
5270:         batchSize = numBlocks * blockSize;
5271:         PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5272:         numChunks = numCells / (numBatches * batchSize);
5273:         Ne        = numChunks * numBatches * batchSize;
5274:         Nr        = numCells % (numBatches * batchSize);
5275:         offset    = numCells - Nr;
5276:         /* Integrate FE residual to get elemVec (need fields at quadrature points) */
5277:         /*   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) */
5278:         PetscCall(PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom));
5279:         PetscCall(PetscFEIntegrateResidual(ds, key, Ne, chunkGeom, u, u_t, dsAux, a, t, elemVec));
5280:         PetscCall(PetscFEGeomGetChunk(geom, offset, numCells, &chunkGeom));
5281:         PetscCall(PetscFEIntegrateResidual(ds, key, Nr, chunkGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), dsAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, &elemVec[offset * totDim]));
5282:         PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &chunkGeom));
5283:       } else if (id == PETSCFV_CLASSID) {
5284:         PetscFV fv = (PetscFV)obj;

5286:         Ne = numFaces;
5287:         /* Riemann solve over faces (need fields at face centroids) */
5288:         /*   We need to evaluate FE fields at those coordinates */
5289:         PetscCall(PetscFVIntegrateRHSFunction(fv, ds, f, Ne, fgeom, vol, uL, uR, fluxL, fluxR));
5290:       } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
5291:     }
5292:     /* Loop over domain */
5293:     if (useFEM) {
5294:       /* Add elemVec to locX */
5295:       for (c = cS; c < cE; ++c) {
5296:         const PetscInt cell = cells ? cells[c] : c;
5297:         const PetscInt cind = c - cStart;

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

5303:           PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
5304:           if (ghostVal > 0) continue;
5305:         }
5306:         PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind * totDim], ADD_ALL_VALUES));
5307:       }
5308:     }
5309:     if (useFVM) {
5310:       PetscScalar *fa;
5311:       PetscInt     iface;

5313:       PetscCall(VecGetArray(locF, &fa));
5314:       for (f = 0; f < Nf; ++f) {
5315:         PetscFV      fv;
5316:         PetscObject  obj;
5317:         PetscClassId id;
5318:         PetscInt     cdim, foff, pdim;

5320:         PetscCall(DMGetCoordinateDim(dm, &cdim));
5321:         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
5322:         PetscCall(PetscDSGetFieldOffset(ds, f, &foff));
5323:         PetscCall(PetscObjectGetClassId(obj, &id));
5324:         if (id != PETSCFV_CLASSID) continue;
5325:         fv = (PetscFV)obj;
5326:         PetscCall(PetscFVGetNumComponents(fv, &pdim));
5327:         /* Accumulate fluxes to cells */
5328:         for (face = fS, iface = 0; face < fE; ++face) {
5329:           const PetscInt *scells;
5330:           PetscScalar    *fL = NULL, *fR = NULL;
5331:           PetscInt        ghost, d, nsupp, nchild;

5333:           PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
5334:           PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
5335:           PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
5336:           if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
5337:           PetscCall(DMPlexGetSupport(dm, face, &scells));
5338:           PetscCall(DMLabelGetValue(ghostLabel, scells[0], &ghost));
5339:           if (ghost <= 0) PetscCall(DMPlexPointLocalFieldRef(dm, scells[0], f, fa, &fL));
5340:           PetscCall(DMLabelGetValue(ghostLabel, scells[1], &ghost));
5341:           if (ghost <= 0) PetscCall(DMPlexPointLocalFieldRef(dm, scells[1], f, fa, &fR));
5342:           if (mesh->printFVM > 1) {
5343:             PetscCall(DMPrintCellVectorReal(face, "Residual: normal", cdim, fgeom[iface].normal));
5344:             PetscCall(DMPrintCellVector(face, "Residual: left state", pdim, &uL[iface * totDim + foff]));
5345:             PetscCall(DMPrintCellVector(face, "Residual: right state", pdim, &uR[iface * totDim + foff]));
5346:             PetscCall(DMPrintCellVector(face, "Residual: left flux", pdim, &fluxL[iface * totDim + foff]));
5347:             PetscCall(DMPrintCellVector(face, "Residual: right flux", pdim, &fluxR[iface * totDim + foff]));
5348:           }
5349:           for (d = 0; d < pdim; ++d) {
5350:             if (fL) fL[d] -= fluxL[iface * totDim + foff + d];
5351:             if (fR) fR[d] += fluxR[iface * totDim + foff + d];
5352:           }
5353:           ++iface;
5354:         }
5355:       }
5356:       PetscCall(VecRestoreArray(locF, &fa));
5357:     }
5358:     /* Handle time derivative */
5359:     if (locX_t) {
5360:       PetscScalar *x_t, *fa;

5362:       PetscCall(VecGetArray(locF, &fa));
5363:       PetscCall(VecGetArray(locX_t, &x_t));
5364:       for (f = 0; f < Nf; ++f) {
5365:         PetscFV      fv;
5366:         PetscObject  obj;
5367:         PetscClassId id;
5368:         PetscInt     pdim, d;

5370:         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
5371:         PetscCall(PetscObjectGetClassId(obj, &id));
5372:         if (id != PETSCFV_CLASSID) continue;
5373:         fv = (PetscFV)obj;
5374:         PetscCall(PetscFVGetNumComponents(fv, &pdim));
5375:         for (c = cS; c < cE; ++c) {
5376:           const PetscInt cell = cells ? cells[c] : c;
5377:           PetscScalar   *u_t, *r;

5379:           if (ghostLabel) {
5380:             PetscInt ghostVal;

5382:             PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
5383:             if (ghostVal > 0) continue;
5384:           }
5385:           PetscCall(DMPlexPointLocalFieldRead(dm, cell, f, x_t, &u_t));
5386:           PetscCall(DMPlexPointLocalFieldRef(dm, cell, f, fa, &r));
5387:           for (d = 0; d < pdim; ++d) r[d] += u_t[d];
5388:         }
5389:       }
5390:       PetscCall(VecRestoreArray(locX_t, &x_t));
5391:       PetscCall(VecRestoreArray(locF, &fa));
5392:     }
5393:     if (useFEM) {
5394:       PetscCall(DMPlexRestoreCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
5395:       PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
5396:     }
5397:     if (useFVM) {
5398:       PetscCall(DMPlexRestoreFaceFields(dm, fS, fE, locX, locX_t, faceGeometryFVM, cellGeometryFVM, locGrad, &numFaces, &uL, &uR));
5399:       PetscCall(DMPlexRestoreFaceGeometry(dm, fS, fE, faceGeometryFVM, cellGeometryFVM, &numFaces, &fgeom, &vol));
5400:       PetscCall(DMRestoreWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxL));
5401:       PetscCall(DMRestoreWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxR));
5402:       if (dmGrad) PetscCall(DMRestoreLocalVector(dmGrad, &locGrad));
5403:     }
5404:   }
5405:   if (useFEM) PetscCall(ISDestroy(&chunkIS));
5406:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));

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

5411:     if (maxDegree <= 1) {
5412:       PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
5413:       PetscCall(PetscQuadratureDestroy(&affineQuad));
5414:     } else {
5415:       for (f = 0; f < Nf; ++f) {
5416:         PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
5417:         PetscCall(PetscQuadratureDestroy(&quads[f]));
5418:       }
5419:       PetscCall(PetscFree2(quads, geoms));
5420:     }
5421:   }

5423:   /* FEM */
5424:   /* 1: Get sizes from dm and dmAux */
5425:   /* 2: Get geometric data */
5426:   /* 3: Handle boundary values */
5427:   /* 4: Loop over domain */
5428:   /*   Extract coefficients */
5429:   /* Loop over fields */
5430:   /*   Set tiling for FE*/
5431:   /*   Integrate FE residual to get elemVec */
5432:   /*     Loop over subdomain */
5433:   /*       Loop over quad points */
5434:   /*         Transform coords to real space */
5435:   /*         Evaluate field and aux fields at point */
5436:   /*         Evaluate residual at point */
5437:   /*         Transform residual to real space */
5438:   /*       Add residual to elemVec */
5439:   /* Loop over domain */
5440:   /*   Add elemVec to locX */

5442:   /* FVM */
5443:   /* Get geometric data */
5444:   /* If using gradients */
5445:   /*   Compute gradient data */
5446:   /*   Loop over domain faces */
5447:   /*     Count computational faces */
5448:   /*     Reconstruct cell gradient */
5449:   /*   Loop over domain cells */
5450:   /*     Limit cell gradients */
5451:   /* Handle boundary values */
5452:   /* Loop over domain faces */
5453:   /*   Read out field, centroid, normal, volume for each side of face */
5454:   /* Riemann solve over faces */
5455:   /* Loop over domain faces */
5456:   /*   Accumulate fluxes to cells */
5457:   /* TODO Change printFEM to printDisc here */
5458:   if (mesh->printFEM) {
5459:     Vec          locFbc;
5460:     PetscInt     pStart, pEnd, p, maxDof;
5461:     PetscScalar *zeroes;

5463:     PetscCall(VecDuplicate(locF, &locFbc));
5464:     PetscCall(VecCopy(locF, locFbc));
5465:     PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5466:     PetscCall(PetscSectionGetMaxDof(section, &maxDof));
5467:     PetscCall(PetscCalloc1(maxDof, &zeroes));
5468:     for (p = pStart; p < pEnd; p++) PetscCall(VecSetValuesSection(locFbc, section, p, zeroes, INSERT_BC_VALUES));
5469:     PetscCall(PetscFree(zeroes));
5470:     PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc));
5471:     PetscCall(VecDestroy(&locFbc));
5472:   }
5473: end:
5474:   PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
5475:   PetscFunctionReturn(PETSC_SUCCESS);
5476: }

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

5481:   Collective

5483:   Input Parameters:
5484: + dm     - The output `DM`
5485: . key    - The `PetscFormKey` array (left cell, right cell, cohesive cell) indicating what should be integrated
5486: . cellIS - The `IS` give a set of cells to integrate over
5487: . time   - The time, or `PETSC_MIN_REAL` to include implicit terms in a time-independent problems
5488: . locX   - The local solution
5489: . locX_t - The time derivative of the local solution, or `NULL` for time-independent problems
5490: . t      - The time
5491: - user   - An optional user context, passed to the pointwise functions

5493:   Output Parameter:
5494: . locF - The local residual

5496:   Level: developer

5498: .seealso: `DMPlexComputeResidualByKey()`, `DMPlexComputeJacobianByKey()`, `DMPlexComputeJacobianHybridByKey()`, `PetscFormKey`
5499: @*/
5500: PetscErrorCode DMPlexComputeResidualHybridByKey(DM dm, PetscFormKey key[], IS cellIS, PetscReal time, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
5501: {
5502:   DM_Plex        *mesh       = (DM_Plex *)dm->data;
5503:   const char     *name       = "Hybrid Residual";
5504:   DM              dmAux[3]   = {NULL, NULL, NULL};
5505:   DMLabel         ghostLabel = NULL;
5506:   PetscDS         ds         = NULL;
5507:   PetscDS         dsIn       = NULL;
5508:   PetscDS         dsAux[3]   = {NULL, NULL, NULL};
5509:   Vec             locA[3]    = {NULL, NULL, NULL};
5510:   DM              dmScale[3] = {NULL, NULL, NULL};
5511:   PetscDS         dsScale[3] = {NULL, NULL, NULL};
5512:   Vec             locS[3]    = {NULL, NULL, NULL};
5513:   PetscSection    section    = NULL;
5514:   DMField         coordField = NULL;
5515:   PetscScalar    *a[3]       = {NULL, NULL, NULL};
5516:   PetscScalar    *s[3]       = {NULL, NULL, NULL};
5517:   PetscScalar    *u          = NULL, *u_t;
5518:   PetscScalar    *elemVecNeg, *elemVecPos, *elemVecCoh;
5519:   IS              chunkISF, chunkISN;
5520:   const PetscInt *cells;
5521:   PetscInt       *faces, *neighbors;
5522:   PetscInt        cStart, cEnd, numCells;
5523:   PetscInt        Nf, f, totDim, totDimIn, totDimAux[3], totDimScale[3], numChunks, cellChunkSize, chunk;
5524:   PetscInt        maxDegree   = PETSC_INT_MAX;
5525:   PetscQuadrature affineQuadF = NULL, *quadsF = NULL;
5526:   PetscFEGeom    *affineGeomF = NULL, **geomsF = NULL;
5527:   PetscQuadrature affineQuadN = NULL, *quadsN = NULL;
5528:   PetscFEGeom    *affineGeomN = NULL, **geomsN = NULL;

5530:   PetscFunctionBegin;
5531:   PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
5532:   if (!cellIS) goto end;
5533:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
5534:   PetscCall(ISGetLocalSize(cellIS, &numCells));
5535:   if (cStart >= cEnd) goto end;
5536:   if ((key[0].label == key[1].label) && (key[0].value == key[1].value) && (key[0].part == key[1].part)) {
5537:     const char *name;
5538:     PetscCall(PetscObjectGetName((PetscObject)key[0].label, &name));
5539:     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);
5540:   }
5541:   /* TODO The places where we have to use isFE are probably the member functions for the PetscDisc class */
5542:   /* FEM */
5543:   /* 1: Get sizes from dm and dmAux */
5544:   PetscCall(DMGetLocalSection(dm, &section));
5545:   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
5546:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn));
5547:   PetscCall(PetscDSGetNumFields(ds, &Nf));
5548:   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
5549:   PetscCall(PetscDSGetTotalDimension(dsIn, &totDimIn));
5550:   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, key[2].value, key[2].part, &locA[2]));
5551:   if (locA[2]) {
5552:     const PetscInt cellStart = cells ? cells[cStart] : cStart;

5554:     PetscCall(VecGetDM(locA[2], &dmAux[2]));
5555:     PetscCall(DMGetCellDS(dmAux[2], cellStart, &dsAux[2], NULL));
5556:     PetscCall(PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]));
5557:     {
5558:       const PetscInt *cone;
5559:       PetscInt        c;

5561:       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
5562:       for (c = 0; c < 2; ++c) {
5563:         const PetscInt *support;
5564:         PetscInt        ssize, s;

5566:         PetscCall(DMPlexGetSupport(dm, cone[c], &support));
5567:         PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
5568:         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);
5569:         if (support[0] == cellStart) s = 1;
5570:         else if (support[1] == cellStart) s = 0;
5571:         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
5572:         PetscCall(DMGetAuxiliaryVec(dm, key[c].label, key[c].value, key[c].part, &locA[c]));
5573:         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);
5574:         if (locA[c]) PetscCall(VecGetDM(locA[c], &dmAux[c]));
5575:         else dmAux[c] = dmAux[2];
5576:         PetscCall(DMGetCellDS(dmAux[c], support[s], &dsAux[c], NULL));
5577:         PetscCall(PetscDSGetTotalDimension(dsAux[c], &totDimAux[c]));
5578:       }
5579:     }
5580:   }
5581:   /* Handle mass matrix scaling
5582:        The field in key[2] is the field to be scaled, and the scaling field is the first in the dsScale */
5583:   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, -key[2].value, key[2].part, &locS[2]));
5584:   if (locS[2]) {
5585:     const PetscInt cellStart = cells ? cells[cStart] : cStart;
5586:     PetscInt       Nb, Nbs;

5588:     PetscCall(VecGetDM(locS[2], &dmScale[2]));
5589:     PetscCall(DMGetCellDS(dmScale[2], cellStart, &dsScale[2], NULL));
5590:     PetscCall(PetscDSGetTotalDimension(dsScale[2], &totDimScale[2]));
5591:     // BRAD: This is not set correctly
5592:     key[2].field = 2;
5593:     PetscCall(PetscDSGetFieldSize(ds, key[2].field, &Nb));
5594:     PetscCall(PetscDSGetFieldSize(dsScale[2], 0, &Nbs));
5595:     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);
5596:     {
5597:       const PetscInt *cone;
5598:       PetscInt        c;

5600:       locS[1] = locS[0] = locS[2];
5601:       dmScale[1] = dmScale[0] = dmScale[2];
5602:       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
5603:       for (c = 0; c < 2; ++c) {
5604:         const PetscInt *support;
5605:         PetscInt        ssize, s;

5607:         PetscCall(DMPlexGetSupport(dm, cone[c], &support));
5608:         PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
5609:         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);
5610:         if (support[0] == cellStart) s = 1;
5611:         else if (support[1] == cellStart) s = 0;
5612:         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
5613:         PetscCall(DMGetCellDS(dmScale[c], support[s], &dsScale[c], NULL));
5614:         PetscCall(PetscDSGetTotalDimension(dsScale[c], &totDimScale[c]));
5615:       }
5616:     }
5617:   }
5618:   /* 2: Setup geometric data */
5619:   PetscCall(DMGetCoordinateField(dm, &coordField));
5620:   PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
5621:   if (maxDegree > 1) {
5622:     PetscCall(PetscCalloc4(Nf, &quadsF, Nf, &geomsF, Nf, &quadsN, Nf, &geomsN));
5623:     for (f = 0; f < Nf; ++f) {
5624:       PetscFE   fe;
5625:       PetscBool isCohesiveField;

5627:       PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
5628:       if (fe) {
5629:         PetscCall(PetscFEGetQuadrature(fe, &quadsF[f]));
5630:         PetscCall(PetscObjectReference((PetscObject)quadsF[f]));
5631:       }
5632:       PetscCall(PetscDSGetDiscretization(dsIn, f, (PetscObject *)&fe));
5633:       PetscCall(PetscDSGetCohesive(dsIn, f, &isCohesiveField));
5634:       if (fe) {
5635:         if (isCohesiveField) {
5636:           for (PetscInt g = 0; g < Nf; ++g) {
5637:             PetscCall(PetscDSGetDiscretization(dsIn, g, (PetscObject *)&fe));
5638:             PetscCall(PetscDSGetCohesive(dsIn, g, &isCohesiveField));
5639:             if (!isCohesiveField) break;
5640:           }
5641:         }
5642:         PetscCall(PetscFEGetQuadrature(fe, &quadsN[f]));
5643:         PetscCall(PetscObjectReference((PetscObject)quadsN[f]));
5644:       }
5645:     }
5646:   }
5647:   /* Loop over chunks */
5648:   cellChunkSize = numCells;
5649:   numChunks     = !numCells ? 0 : PetscCeilReal(((PetscReal)numCells) / cellChunkSize);
5650:   PetscCall(PetscCalloc2(2 * cellChunkSize, &faces, 2 * cellChunkSize, &neighbors));
5651:   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 2 * cellChunkSize, faces, PETSC_USE_POINTER, &chunkISF));
5652:   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 2 * cellChunkSize, neighbors, PETSC_USE_POINTER, &chunkISN));
5653:   /* Extract field coefficients */
5654:   /* NOTE This needs the end cap faces to have identical orientations */
5655:   PetscCall(DMPlexGetHybridCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
5656:   PetscCall(DMPlexGetHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
5657:   PetscCall(DMPlexGetHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_TRUE, s));
5658:   PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecNeg));
5659:   PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecPos));
5660:   PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecCoh));
5661:   for (chunk = 0; chunk < numChunks; ++chunk) {
5662:     PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;

5664:     PetscCall(PetscArrayzero(elemVecNeg, cellChunkSize * totDim));
5665:     PetscCall(PetscArrayzero(elemVecPos, cellChunkSize * totDim));
5666:     PetscCall(PetscArrayzero(elemVecCoh, cellChunkSize * totDim));
5667:     /* Get faces and neighbors */
5668:     for (c = cS; c < cE; ++c) {
5669:       const PetscInt  cell = cells ? cells[c] : c;
5670:       const PetscInt *cone, *support;
5671:       PetscCall(DMPlexGetCone(dm, cell, &cone));
5672:       faces[(c - cS) * 2 + 0] = cone[0];
5673:       faces[(c - cS) * 2 + 1] = cone[1];
5674:       PetscCall(DMPlexGetSupport(dm, cone[0], &support));
5675:       neighbors[(c - cS) * 2 + 0] = support[0] == cell ? support[1] : support[0];
5676:       PetscCall(DMPlexGetSupport(dm, cone[1], &support));
5677:       neighbors[(c - cS) * 2 + 1] = support[0] == cell ? support[1] : support[0];
5678:     }
5679:     PetscCall(ISGeneralSetIndices(chunkISF, 2 * cellChunkSize, faces, PETSC_USE_POINTER));
5680:     PetscCall(ISGeneralSetIndices(chunkISN, 2 * cellChunkSize, neighbors, PETSC_USE_POINTER));
5681:     /* Get geometric data */
5682:     if (maxDegree <= 1) {
5683:       if (!affineQuadF) PetscCall(DMFieldCreateDefaultQuadrature(coordField, chunkISF, &affineQuadF));
5684:       if (affineQuadF) PetscCall(DMSNESGetFEGeom(coordField, chunkISF, affineQuadF, PETSC_FEGEOM_COHESIVE, &affineGeomF));
5685:       if (!affineQuadN) {
5686:         PetscInt dim;
5687:         PetscCall(PetscQuadratureGetData(affineQuadF, &dim, NULL, NULL, NULL, NULL));
5688:         PetscCall(DMFieldCreateDefaultFaceQuadrature(coordField, chunkISN, &affineQuadN));
5689:         PetscCall(PetscQuadratureSetData(affineQuadN, dim + 1, PETSC_DECIDE, PETSC_DECIDE, NULL, NULL));
5690:       }
5691:       if (affineQuadN) PetscCall(DMSNESGetFEGeom(coordField, chunkISN, affineQuadN, PETSC_FEGEOM_BASIC, &affineGeomN));
5692:     } else {
5693:       for (f = 0; f < Nf; ++f) {
5694:         if (quadsF[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkISF, quadsF[f], PETSC_FEGEOM_COHESIVE, &geomsF[f]));
5695:         if (quadsN[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkISN, quadsN[f], PETSC_FEGEOM_BASIC, &geomsN[f]));
5696:       }
5697:     }
5698:     /* Loop over fields */
5699:     for (f = 0; f < Nf; ++f) {
5700:       PetscFE         fe;
5701:       PetscFEGeom    *geomF      = affineGeomF ? affineGeomF : geomsF[f];
5702:       PetscFEGeom    *chunkGeomF = NULL, *remGeomF = NULL;
5703:       PetscFEGeom    *geomN      = affineGeomN ? affineGeomN : geomsN[f];
5704:       PetscFEGeom    *chunkGeomN = NULL, *remGeomN = NULL;
5705:       PetscQuadrature quadF = affineQuadF ? affineQuadF : quadsF[f];
5706:       PetscInt        numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb;
5707:       PetscBool       isCohesiveField;

5709:       PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
5710:       if (!fe) continue;
5711:       PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5712:       PetscCall(PetscQuadratureGetData(quadF, NULL, NULL, &Nq, NULL, NULL));
5713:       PetscCall(PetscFEGetDimension(fe, &Nb));
5714:       blockSize = Nb;
5715:       batchSize = numBlocks * blockSize;
5716:       PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5717:       numChunks = numCells / (numBatches * batchSize);
5718:       Ne        = numChunks * numBatches * batchSize;
5719:       Nr        = numCells % (numBatches * batchSize);
5720:       offset    = numCells - Nr;
5721:       PetscCall(PetscFEGeomGetChunk(geomF, 0, offset * 2, &chunkGeomF));
5722:       PetscCall(PetscFEGeomGetChunk(geomF, offset * 2, numCells * 2, &remGeomF));
5723:       PetscCall(PetscFEGeomGetChunk(geomN, 0, offset * 2, &chunkGeomN));
5724:       PetscCall(PetscFEGeomGetChunk(geomN, offset * 2, numCells * 2, &remGeomN));
5725:       PetscCall(PetscDSGetCohesive(ds, f, &isCohesiveField));
5726:       // TODO Do I need to set isCohesive on the chunks?
5727:       key[0].field = f;
5728:       key[1].field = f;
5729:       key[2].field = f;
5730:       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[0], 0, Ne, chunkGeomF, chunkGeomN, u, u_t, dsAux[0], a[0], t, elemVecNeg));
5731:       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[0], 0, Nr, remGeomF, remGeomN, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[0], PetscSafePointerPlusOffset(a[0], offset * totDimAux[0]), t, &elemVecNeg[offset * totDim]));
5732:       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[1], 1, Ne, chunkGeomF, chunkGeomN, u, u_t, dsAux[1], a[1], t, elemVecPos));
5733:       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[1], 1, Nr, remGeomF, remGeomN, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[1], PetscSafePointerPlusOffset(a[1], offset * totDimAux[1]), t, &elemVecPos[offset * totDim]));
5734:       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[2], 2, Ne, chunkGeomF, chunkGeomN, u, u_t, dsAux[2], a[2], t, elemVecCoh));
5735:       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[2], 2, Nr, remGeomF, remGeomN, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[2], PetscSafePointerPlusOffset(a[2], offset * totDimAux[2]), t, &elemVecCoh[offset * totDim]));
5736:       PetscCall(PetscFEGeomRestoreChunk(geomF, offset, numCells, &remGeomF));
5737:       PetscCall(PetscFEGeomRestoreChunk(geomF, 0, offset, &chunkGeomF));
5738:       PetscCall(PetscFEGeomRestoreChunk(geomN, offset, numCells, &remGeomN));
5739:       PetscCall(PetscFEGeomRestoreChunk(geomN, 0, offset, &chunkGeomN));
5740:     }
5741:     /* Add elemVec to locX */
5742:     for (c = cS; c < cE; ++c) {
5743:       const PetscInt cell = cells ? cells[c] : c;
5744:       const PetscInt cind = c - cStart;
5745:       PetscInt       i;

5747:       /* Scale element values */
5748:       if (locS[0]) {
5749:         PetscInt  Nb, off = cind * totDim, soff = cind * totDimScale[0];
5750:         PetscBool cohesive;

5752:         for (f = 0; f < Nf; ++f) {
5753:           PetscCall(PetscDSGetFieldSize(ds, f, &Nb));
5754:           PetscCall(PetscDSGetCohesive(ds, f, &cohesive));
5755:           if (f == key[2].field) {
5756:             PetscCheck(cohesive, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Scaling should not happen for face fields");
5757:             // No cohesive scaling field is currently input
5758:             for (i = 0; i < Nb; ++i) elemVecCoh[off + i] += s[0][soff + i] * elemVecNeg[off + i] + s[1][soff + i] * elemVecPos[off + i];
5759:             off += Nb;
5760:           } else {
5761:             const PetscInt N = cohesive ? Nb : Nb * 2;

5763:             for (i = 0; i < N; ++i) elemVecCoh[off + i] += elemVecNeg[off + i] + elemVecPos[off + i];
5764:             off += N;
5765:           }
5766:         }
5767:       } else {
5768:         for (i = cind * totDim; i < (cind + 1) * totDim; ++i) elemVecCoh[i] += elemVecNeg[i] + elemVecPos[i];
5769:       }
5770:       if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, name, totDim, &elemVecCoh[cind * totDim]));
5771:       if (ghostLabel) {
5772:         PetscInt ghostVal;

5774:         PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
5775:         if (ghostVal > 0) continue;
5776:       }
5777:       PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVecCoh[cind * totDim], ADD_ALL_VALUES));
5778:     }
5779:   }
5780:   PetscCall(DMPlexRestoreCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
5781:   PetscCall(DMPlexRestoreHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
5782:   PetscCall(DMPlexRestoreHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_TRUE, s));
5783:   PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecNeg));
5784:   PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecPos));
5785:   PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecCoh));
5786:   PetscCall(PetscFree2(faces, neighbors));
5787:   PetscCall(ISDestroy(&chunkISF));
5788:   PetscCall(ISDestroy(&chunkISN));
5789:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
5790:   if (maxDegree <= 1) {
5791:     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuadF, PETSC_FALSE, &affineGeomF));
5792:     PetscCall(PetscQuadratureDestroy(&affineQuadF));
5793:     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuadN, PETSC_FALSE, &affineGeomN));
5794:     PetscCall(PetscQuadratureDestroy(&affineQuadN));
5795:   } else {
5796:     for (f = 0; f < Nf; ++f) {
5797:       if (geomsF) PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quadsF[f], PETSC_FALSE, &geomsF[f]));
5798:       if (quadsF) PetscCall(PetscQuadratureDestroy(&quadsF[f]));
5799:       if (geomsN) PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quadsN[f], PETSC_FALSE, &geomsN[f]));
5800:       if (quadsN) PetscCall(PetscQuadratureDestroy(&quadsN[f]));
5801:     }
5802:     PetscCall(PetscFree4(quadsF, geomsF, quadsN, geomsN));
5803:   }
5804:   if (mesh->printFEM) {
5805:     Vec          locFbc;
5806:     PetscInt     pStart, pEnd, p, maxDof;
5807:     PetscScalar *zeroes;

5809:     PetscCall(VecDuplicate(locF, &locFbc));
5810:     PetscCall(VecCopy(locF, locFbc));
5811:     PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5812:     PetscCall(PetscSectionGetMaxDof(section, &maxDof));
5813:     PetscCall(PetscCalloc1(maxDof, &zeroes));
5814:     for (p = pStart; p < pEnd; p++) PetscCall(VecSetValuesSection(locFbc, section, p, zeroes, INSERT_BC_VALUES));
5815:     PetscCall(PetscFree(zeroes));
5816:     PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc));
5817:     PetscCall(VecDestroy(&locFbc));
5818:   }
5819: end:
5820:   PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
5821:   PetscFunctionReturn(PETSC_SUCCESS);
5822: }

5824: 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)
5825: {
5826:   DM_Plex        *mesh = (DM_Plex *)dm->data;
5827:   DM              plex = NULL, plexA = NULL, tdm;
5828:   DMEnclosureType encAux;
5829:   PetscDS         ds, dsAux           = NULL;
5830:   PetscSection    section, sectionAux = NULL;
5831:   PetscSection    globalSection;
5832:   Vec             locA = NULL, tv;
5833:   PetscScalar    *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL, *elemMatP = NULL;
5834:   PetscInt        v;
5835:   PetscInt        Nf, totDim, totDimAux = 0;
5836:   PetscBool       hasJac = PETSC_FALSE, hasPrec = PETSC_FALSE, transform;

5838:   PetscFunctionBegin;
5839:   PetscCall(DMHasBasisTransform(dm, &transform));
5840:   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
5841:   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
5842:   PetscCall(DMGetLocalSection(dm, &section));
5843:   PetscCall(DMGetDS(dm, &ds));
5844:   PetscCall(PetscDSGetNumFields(ds, &Nf));
5845:   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
5846:   PetscCall(PetscWeakFormHasBdJacobian(wf, &hasJac));
5847:   PetscCall(PetscWeakFormHasBdJacobianPreconditioner(wf, &hasPrec));
5848:   if (!hasJac && !hasPrec) PetscFunctionReturn(PETSC_SUCCESS);
5849:   PetscCall(DMConvert(dm, DMPLEX, &plex));
5850:   PetscCall(DMGetAuxiliaryVec(dm, label, values[0], 0, &locA));
5851:   if (locA) {
5852:     DM dmAux;

5854:     PetscCall(VecGetDM(locA, &dmAux));
5855:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
5856:     PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
5857:     PetscCall(DMGetDS(plexA, &dsAux));
5858:     PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
5859:     PetscCall(DMGetLocalSection(plexA, &sectionAux));
5860:   }

5862:   PetscCall(DMGetGlobalSection(dm, &globalSection));
5863:   for (v = 0; v < numValues; ++v) {
5864:     PetscFEGeom    *fgeom;
5865:     PetscInt        maxDegree;
5866:     PetscQuadrature qGeom = NULL;
5867:     IS              pointIS;
5868:     const PetscInt *points;
5869:     PetscFormKey    key;
5870:     PetscInt        numFaces, face, Nq;

5872:     key.label = label;
5873:     key.value = values[v];
5874:     key.part  = 0;
5875:     PetscCall(DMLabelGetStratumIS(label, values[v], &pointIS));
5876:     if (!pointIS) continue; /* No points with that id on this process */
5877:     {
5878:       IS isectIS;

5880:       /* TODO: Special cases of ISIntersect where it is quick to check a prior if one is a superset of the other */
5881:       PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS));
5882:       PetscCall(ISDestroy(&pointIS));
5883:       pointIS = isectIS;
5884:     }
5885:     PetscCall(ISGetLocalSize(pointIS, &numFaces));
5886:     PetscCall(ISGetIndices(pointIS, &points));
5887:     PetscCall(PetscMalloc5(numFaces * totDim, &u, (locX_t ? (size_t)numFaces * totDim : 0), &u_t, (hasJac ? (size_t)numFaces * totDim * totDim : 0), &elemMat, (hasPrec ? (size_t)numFaces * totDim * totDim : 0), &elemMatP, (locA ? (size_t)numFaces * totDimAux : 0), &a));
5888:     PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
5889:     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom));
5890:     if (!qGeom) {
5891:       PetscFE fe;

5893:       PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&fe));
5894:       PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
5895:       PetscCall(PetscObjectReference((PetscObject)qGeom));
5896:     }
5897:     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
5898:     PetscCall(DMSNESGetFEGeom(coordField, pointIS, qGeom, PETSC_FEGEOM_BOUNDARY, &fgeom));
5899:     for (face = 0; face < numFaces; ++face) {
5900:       const PetscInt point = points[face], *support;
5901:       PetscScalar   *x     = NULL;
5902:       PetscInt       i;

5904:       PetscCall(DMPlexGetSupport(dm, point, &support));
5905:       PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
5906:       for (i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
5907:       PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
5908:       if (locX_t) {
5909:         PetscCall(DMPlexVecGetClosure(plex, section, locX_t, support[0], NULL, &x));
5910:         for (i = 0; i < totDim; ++i) u_t[face * totDim + i] = x[i];
5911:         PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, support[0], NULL, &x));
5912:       }
5913:       if (locA) {
5914:         PetscInt subp;
5915:         PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
5916:         PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
5917:         for (i = 0; i < totDimAux; ++i) a[face * totDimAux + i] = x[i];
5918:         PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
5919:       }
5920:     }
5921:     if (elemMat) PetscCall(PetscArrayzero(elemMat, numFaces * totDim * totDim));
5922:     if (elemMatP) PetscCall(PetscArrayzero(elemMatP, numFaces * totDim * totDim));
5923:     {
5924:       PetscFE  fe;
5925:       PetscInt Nb;
5926:       /* Conforming batches */
5927:       PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
5928:       /* Remainder */
5929:       PetscFEGeom *chunkGeom = NULL;
5930:       PetscInt     fieldJ, Nr, offset;

5932:       PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&fe));
5933:       PetscCall(PetscFEGetDimension(fe, &Nb));
5934:       PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5935:       blockSize = Nb;
5936:       batchSize = numBlocks * blockSize;
5937:       PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5938:       numChunks = numFaces / (numBatches * batchSize);
5939:       Ne        = numChunks * numBatches * batchSize;
5940:       Nr        = numFaces % (numBatches * batchSize);
5941:       offset    = numFaces - Nr;
5942:       PetscCall(PetscFEGeomGetChunk(fgeom, 0, offset, &chunkGeom));
5943:       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5944:         key.field = fieldI * Nf + fieldJ;
5945:         if (hasJac) PetscCall(PetscFEIntegrateBdJacobian(ds, wf, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, dsAux, a, t, X_tShift, elemMat));
5946:         if (hasPrec) PetscCall(PetscFEIntegrateBdJacobian(ds, wf, PETSCFE_JACOBIAN_PRE, key, Ne, chunkGeom, u, u_t, dsAux, a, t, X_tShift, elemMatP));
5947:       }
5948:       PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom));
5949:       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5950:         key.field = fieldI * Nf + fieldJ;
5951:         if (hasJac)
5952:           PetscCall(PetscFEIntegrateBdJacobian(ds, wf, PETSCFE_JACOBIAN, key, Nr, chunkGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), dsAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, X_tShift, &elemMat[offset * totDim * totDim]));
5953:         if (hasPrec)
5954:           PetscCall(PetscFEIntegrateBdJacobian(ds, wf, PETSCFE_JACOBIAN_PRE, key, Nr, chunkGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), dsAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, X_tShift, &elemMatP[offset * totDim * totDim]));
5955:       }
5956:       PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom));
5957:     }
5958:     for (face = 0; face < numFaces; ++face) {
5959:       const PetscInt point = points[face], *support;

5961:       /* Transform to global basis before insertion in Jacobian */
5962:       PetscCall(DMPlexGetSupport(plex, point, &support));
5963:       if (hasJac && transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, support[0], PETSC_TRUE, totDim, &elemMat[face * totDim * totDim]));
5964:       if (hasPrec && transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, support[0], PETSC_TRUE, totDim, &elemMatP[face * totDim * totDim]));
5965:       if (hasPrec) {
5966:         if (hasJac) {
5967:           if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(point, "BdJacobian", totDim, totDim, &elemMat[face * totDim * totDim]));
5968:           PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, Jac, support[0], &elemMat[face * totDim * totDim], ADD_VALUES));
5969:         }
5970:         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(point, "BdJacobian", totDim, totDim, &elemMatP[face * totDim * totDim]));
5971:         PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, JacP, support[0], &elemMatP[face * totDim * totDim], ADD_VALUES));
5972:       } else {
5973:         if (hasJac) {
5974:           if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(point, "BdJacobian", totDim, totDim, &elemMat[face * totDim * totDim]));
5975:           PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, Jac, support[0], &elemMat[face * totDim * totDim], ADD_VALUES));
5976:         }
5977:       }
5978:     }
5979:     PetscCall(DMSNESRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
5980:     PetscCall(PetscQuadratureDestroy(&qGeom));
5981:     PetscCall(ISRestoreIndices(pointIS, &points));
5982:     PetscCall(ISDestroy(&pointIS));
5983:     PetscCall(PetscFree5(u, u_t, elemMat, elemMatP, a));
5984:   }
5985:   if (plex) PetscCall(DMDestroy(&plex));
5986:   if (plexA) PetscCall(DMDestroy(&plexA));
5987:   PetscFunctionReturn(PETSC_SUCCESS);
5988: }

5990: 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)
5991: {
5992:   DMField  coordField;
5993:   DMLabel  depthLabel;
5994:   IS       facetIS;
5995:   PetscInt dim;

5997:   PetscFunctionBegin;
5998:   PetscCall(DMGetDimension(dm, &dim));
5999:   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6000:   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
6001:   PetscCall(DMGetCoordinateField(dm, &coordField));
6002:   PetscCall(DMPlexComputeBdJacobian_Single_Internal(dm, t, wf, label, numValues, values, field, locX, locX_t, X_tShift, Jac, JacP, coordField, facetIS));
6003:   PetscCall(ISDestroy(&facetIS));
6004:   PetscFunctionReturn(PETSC_SUCCESS);
6005: }

6007: static PetscErrorCode DMPlexComputeBdJacobian_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, PetscReal X_tShift, Mat Jac, Mat JacP, void *user)
6008: {
6009:   PetscDS  prob;
6010:   PetscInt dim, numBd, bd;
6011:   DMLabel  depthLabel;
6012:   DMField  coordField = NULL;
6013:   IS       facetIS;

6015:   PetscFunctionBegin;
6016:   PetscCall(DMGetDS(dm, &prob));
6017:   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6018:   PetscCall(DMGetDimension(dm, &dim));
6019:   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
6020:   PetscCall(PetscDSGetNumBoundary(prob, &numBd));
6021:   PetscCall(DMGetCoordinateField(dm, &coordField));
6022:   for (bd = 0; bd < numBd; ++bd) {
6023:     PetscWeakForm           wf;
6024:     DMBoundaryConditionType type;
6025:     DMLabel                 label;
6026:     const PetscInt         *values;
6027:     PetscInt                fieldI, numValues;
6028:     PetscObject             obj;
6029:     PetscClassId            id;

6031:     PetscCall(PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &fieldI, NULL, NULL, NULL, NULL, NULL));
6032:     if (type & DM_BC_ESSENTIAL) continue;
6033:     PetscCall(PetscDSGetDiscretization(prob, fieldI, &obj));
6034:     PetscCall(PetscObjectGetClassId(obj, &id));
6035:     if (id != PETSCFE_CLASSID) continue;
6036:     PetscCall(DMPlexComputeBdJacobian_Single_Internal(dm, t, wf, label, numValues, values, fieldI, locX, locX_t, X_tShift, Jac, JacP, coordField, facetIS));
6037:   }
6038:   PetscCall(ISDestroy(&facetIS));
6039:   PetscFunctionReturn(PETSC_SUCCESS);
6040: }

6042: /*@
6043:   DMPlexComputeJacobianByKey - Compute the local Jacobian for terms matching the input key

6045:   Collective

6047:   Input Parameters:
6048: + dm       - The output `DM`
6049: . key      - The `PetscFormKey` indicating what should be integrated
6050: . cellIS   - The `IS` give a set of cells to integrate over
6051: . t        - The time
6052: . X_tShift - The multiplier for the Jacobian with respect to $X_t$
6053: . locX     - The local solution
6054: . locX_t   - The time derivative of the local solution, or `NULL` for time-independent problems
6055: - user     - An optional user context, passed to the pointwise functions

6057:   Output Parameters:
6058: + Jac  - The local Jacobian
6059: - JacP - The local Jacobian preconditioner

6061:   Level: developer

6063: .seealso: `DMPlexComputeResidualByKey()`, `DMPlexComputeResidualHybridByKey()`, `DMPlexComputeJacobianHybridByKey()`, `PetscFormKey`
6064: @*/
6065: PetscErrorCode DMPlexComputeJacobianByKey(DM dm, PetscFormKey key, IS cellIS, PetscReal t, PetscReal X_tShift, Vec locX, Vec locX_t, Mat Jac, Mat JacP, void *user)
6066: {
6067:   DM_Plex        *mesh  = (DM_Plex *)dm->data;
6068:   const char     *name  = "Jacobian";
6069:   DM              dmAux = NULL, plex, tdm;
6070:   DMEnclosureType encAux;
6071:   Vec             A, tv;
6072:   DMField         coordField;
6073:   PetscDS         prob, probAux = NULL;
6074:   PetscSection    section, globalSection, sectionAux;
6075:   PetscScalar    *elemMat, *elemMatP, *elemMatD, *u, *u_t, *a = NULL;
6076:   const PetscInt *cells;
6077:   PetscInt        Nf, fieldI, fieldJ;
6078:   PetscInt        totDim, totDimAux = 0, cStart, cEnd, numCells, c;
6079:   PetscBool       hasJac = PETSC_FALSE, hasPrec = PETSC_FALSE, hasDyn, hasFV = PETSC_FALSE, transform;

6081:   PetscFunctionBegin;
6082:   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
6083:   PetscCall(DMGetLocalSection(dm, &section));
6084:   PetscCall(DMGetGlobalSection(dm, &globalSection));
6085:   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &A));
6086:   if (A) {
6087:     PetscCall(VecGetDM(A, &dmAux));
6088:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
6089:     PetscCall(DMConvert(dmAux, DMPLEX, &plex));
6090:     PetscCall(DMGetLocalSection(plex, &sectionAux));
6091:     PetscCall(DMGetDS(dmAux, &probAux));
6092:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
6093:   }
6094:   PetscCall(DMGetCoordinateField(dm, &coordField));
6095:   if (!cellIS) goto end;
6096:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
6097:   PetscCall(ISGetLocalSize(cellIS, &numCells));
6098:   if (cStart >= cEnd) goto end;
6099:   PetscCall(DMHasBasisTransform(dm, &transform));
6100:   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
6101:   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
6102:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL));
6103:   PetscCall(PetscDSGetNumFields(prob, &Nf));
6104:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
6105:   PetscCall(PetscDSHasJacobian(prob, &hasJac));
6106:   PetscCall(PetscDSHasJacobianPreconditioner(prob, &hasPrec));
6107:   /* user passed in the same matrix, avoid double contributions and
6108:      only assemble the Jacobian */
6109:   if (hasJac && Jac == JacP) hasPrec = PETSC_FALSE;
6110:   PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
6111:   hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
6112:   PetscCall(PetscMalloc5(numCells * totDim, &u, (locX_t ? (size_t)numCells * totDim : 0), &u_t, (hasJac ? (size_t)numCells * totDim * totDim : 0), &elemMat, (hasPrec ? (size_t)numCells * totDim * totDim : 0), &elemMatP, (hasDyn ? (size_t)numCells * totDim * totDim : 0), &elemMatD));
6113:   if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
6114:   for (c = cStart; c < cEnd; ++c) {
6115:     const PetscInt cell = cells ? cells[c] : c;
6116:     const PetscInt cind = c - cStart;
6117:     PetscScalar   *x = NULL, *x_t = NULL;
6118:     PetscInt       i;

6120:     PetscCall(DMPlexVecGetClosure(dm, section, locX, cell, NULL, &x));
6121:     for (i = 0; i < totDim; ++i) u[cind * totDim + i] = x[i];
6122:     PetscCall(DMPlexVecRestoreClosure(dm, section, locX, cell, NULL, &x));
6123:     if (locX_t) {
6124:       PetscCall(DMPlexVecGetClosure(dm, section, locX_t, cell, NULL, &x_t));
6125:       for (i = 0; i < totDim; ++i) u_t[cind * totDim + i] = x_t[i];
6126:       PetscCall(DMPlexVecRestoreClosure(dm, section, locX_t, cell, NULL, &x_t));
6127:     }
6128:     if (dmAux) {
6129:       PetscInt subcell;
6130:       PetscCall(DMGetEnclosurePoint(dmAux, dm, encAux, cell, &subcell));
6131:       PetscCall(DMPlexVecGetClosure(plex, sectionAux, A, subcell, NULL, &x));
6132:       for (i = 0; i < totDimAux; ++i) a[cind * totDimAux + i] = x[i];
6133:       PetscCall(DMPlexVecRestoreClosure(plex, sectionAux, A, subcell, NULL, &x));
6134:     }
6135:   }
6136:   if (hasJac) PetscCall(PetscArrayzero(elemMat, numCells * totDim * totDim));
6137:   if (hasPrec) PetscCall(PetscArrayzero(elemMatP, numCells * totDim * totDim));
6138:   if (hasDyn) PetscCall(PetscArrayzero(elemMatD, numCells * totDim * totDim));
6139:   for (fieldI = 0; fieldI < Nf; ++fieldI) {
6140:     PetscClassId    id;
6141:     PetscFE         fe;
6142:     PetscQuadrature qGeom = NULL;
6143:     PetscInt        Nb;
6144:     /* Conforming batches */
6145:     PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
6146:     /* Remainder */
6147:     PetscInt     Nr, offset, Nq;
6148:     PetscInt     maxDegree;
6149:     PetscFEGeom *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL;

6151:     PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
6152:     PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
6153:     if (id == PETSCFV_CLASSID) {
6154:       hasFV = PETSC_TRUE;
6155:       continue;
6156:     }
6157:     PetscCall(PetscFEGetDimension(fe, &Nb));
6158:     PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
6159:     PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
6160:     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
6161:     if (!qGeom) {
6162:       PetscCall(PetscFEGetQuadrature(fe, &qGeom));
6163:       PetscCall(PetscObjectReference((PetscObject)qGeom));
6164:     }
6165:     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
6166:     PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FEGEOM_BASIC, &cgeomFEM));
6167:     blockSize = Nb;
6168:     batchSize = numBlocks * blockSize;
6169:     PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
6170:     numChunks = numCells / (numBatches * batchSize);
6171:     Ne        = numChunks * numBatches * batchSize;
6172:     Nr        = numCells % (numBatches * batchSize);
6173:     offset    = numCells - Nr;
6174:     PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
6175:     PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &remGeom));
6176:     for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
6177:       key.field = fieldI * Nf + fieldJ;
6178:       if (hasJac) {
6179:         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat));
6180:         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Nr, remGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), probAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, X_tShift, &elemMat[offset * totDim * totDim]));
6181:       }
6182:       if (hasPrec) {
6183:         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatP));
6184:         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, key, Nr, remGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), probAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, X_tShift, &elemMatP[offset * totDim * totDim]));
6185:       }
6186:       if (hasDyn) {
6187:         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatD));
6188:         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Nr, remGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), probAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, X_tShift, &elemMatD[offset * totDim * totDim]));
6189:       }
6190:     }
6191:     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &remGeom));
6192:     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, 0, offset, &chunkGeom));
6193:     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
6194:     PetscCall(PetscQuadratureDestroy(&qGeom));
6195:   }
6196:   /*   Add contribution from X_t */
6197:   if (hasDyn) {
6198:     for (c = 0; c < numCells * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
6199:   }
6200:   if (hasFV) {
6201:     PetscClassId id;
6202:     PetscFV      fv;
6203:     PetscInt     offsetI, NcI, NbI = 1, fc, f;

6205:     for (fieldI = 0; fieldI < Nf; ++fieldI) {
6206:       PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fv));
6207:       PetscCall(PetscDSGetFieldOffset(prob, fieldI, &offsetI));
6208:       PetscCall(PetscObjectGetClassId((PetscObject)fv, &id));
6209:       if (id != PETSCFV_CLASSID) continue;
6210:       /* Put in the weighted identity */
6211:       PetscCall(PetscFVGetNumComponents(fv, &NcI));
6212:       for (c = cStart; c < cEnd; ++c) {
6213:         const PetscInt cind    = c - cStart;
6214:         const PetscInt eOffset = cind * totDim * totDim;
6215:         PetscReal      vol;

6217:         PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
6218:         for (fc = 0; fc < NcI; ++fc) {
6219:           for (f = 0; f < NbI; ++f) {
6220:             const PetscInt i = offsetI + f * NcI + fc;
6221:             if (hasPrec) {
6222:               if (hasJac) elemMat[eOffset + i * totDim + i] = vol;
6223:               elemMatP[eOffset + i * totDim + i] = vol;
6224:             } else {
6225:               elemMat[eOffset + i * totDim + i] = vol;
6226:             }
6227:           }
6228:         }
6229:       }
6230:     }
6231:     /* No allocated space for FV stuff, so ignore the zero entries */
6232:     PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE));
6233:   }
6234:   /* Insert values into matrix */
6235:   for (c = cStart; c < cEnd; ++c) {
6236:     const PetscInt cell = cells ? cells[c] : c;
6237:     const PetscInt cind = c - cStart;

6239:     /* Transform to global basis before insertion in Jacobian */
6240:     if (transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, cell, PETSC_TRUE, totDim, &elemMat[cind * totDim * totDim]));
6241:     if (hasPrec) {
6242:       if (hasJac) {
6243:         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
6244:         PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, Jac, cell, &elemMat[cind * totDim * totDim], ADD_VALUES));
6245:       }
6246:       if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatP[cind * totDim * totDim]));
6247:       PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, JacP, cell, &elemMatP[cind * totDim * totDim], ADD_VALUES));
6248:     } else {
6249:       if (hasJac) {
6250:         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
6251:         PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, JacP, cell, &elemMat[cind * totDim * totDim], ADD_VALUES));
6252:       }
6253:     }
6254:   }
6255:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
6256:   if (hasFV) PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE));
6257:   PetscCall(PetscFree5(u, u_t, elemMat, elemMatP, elemMatD));
6258:   if (dmAux) PetscCall(PetscFree(a));
6259:   /* Compute boundary integrals */
6260:   PetscCall(DMPlexComputeBdJacobian_Internal(dm, locX, locX_t, t, X_tShift, Jac, JacP, user));
6261:   /* Assemble matrix */
6262: end: {
6263:   PetscBool assOp = hasJac && hasPrec ? PETSC_TRUE : PETSC_FALSE, gassOp;

6265:   if (dmAux) PetscCall(DMDestroy(&plex));
6266:   PetscCallMPI(MPIU_Allreduce(&assOp, &gassOp, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
6267:   if (hasJac && hasPrec) {
6268:     PetscCall(MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY));
6269:     PetscCall(MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY));
6270:   }
6271: }
6272:   PetscCall(MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY));
6273:   PetscCall(MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY));
6274:   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
6275:   PetscFunctionReturn(PETSC_SUCCESS);
6276: }

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

6281:   Collective

6283:   Input Parameters:
6284: + dm       - The output `DM`
6285: . key      - The `PetscFormKey` array (left cell, right cell, cohesive cell) indicating what should be integrated
6286: . cellIS   - The `IS` give a set of cells to integrate over
6287: . t        - The time
6288: . X_tShift - The multiplier for the Jacobian with respect to $X_t$
6289: . locX     - The local solution
6290: . locX_t   - The time derivative of the local solution, or `NULL` for time-independent problems
6291: - user     - An optional user context, passed to the pointwise functions

6293:   Output Parameters:
6294: + Jac  - The local Jacobian
6295: - JacP - The local Jacobian preconditioner

6297:   Level: developer

6299: .seealso: `DMPlexComputeResidualByKey()`, `DMPlexComputeJacobianByKey()`, `DMPlexComputeResidualHybridByKey()`, `PetscFormKey`
6300: @*/
6301: PetscErrorCode DMPlexComputeJacobianHybridByKey(DM dm, PetscFormKey key[], IS cellIS, PetscReal t, PetscReal X_tShift, Vec locX, Vec locX_t, Mat Jac, Mat JacP, void *user)
6302: {
6303:   DM_Plex        *mesh          = (DM_Plex *)dm->data;
6304:   const char     *name          = "Hybrid Jacobian";
6305:   DM              dmAux[3]      = {NULL, NULL, NULL};
6306:   DMLabel         ghostLabel    = NULL;
6307:   DM              plex          = NULL;
6308:   DM              plexA         = NULL;
6309:   PetscDS         ds            = NULL;
6310:   PetscDS         dsIn          = NULL;
6311:   PetscDS         dsAux[3]      = {NULL, NULL, NULL};
6312:   Vec             locA[3]       = {NULL, NULL, NULL};
6313:   DM              dmScale[3]    = {NULL, NULL, NULL};
6314:   PetscDS         dsScale[3]    = {NULL, NULL, NULL};
6315:   Vec             locS[3]       = {NULL, NULL, NULL};
6316:   PetscSection    section       = NULL;
6317:   PetscSection    sectionAux[3] = {NULL, NULL, NULL};
6318:   DMField         coordField    = NULL;
6319:   PetscScalar    *a[3]          = {NULL, NULL, NULL};
6320:   PetscScalar    *s[3]          = {NULL, NULL, NULL};
6321:   PetscScalar    *u             = NULL, *u_t;
6322:   PetscScalar    *elemMatNeg, *elemMatPos, *elemMatCoh;
6323:   PetscScalar    *elemMatNegP, *elemMatPosP, *elemMatCohP;
6324:   PetscSection    globalSection;
6325:   IS              chunkISF, chunkISN;
6326:   const PetscInt *cells;
6327:   PetscInt       *faces, *neighbors;
6328:   PetscInt        cStart, cEnd, numCells;
6329:   PetscInt        Nf, fieldI, fieldJ, totDim, totDimIn, totDimAux[3], totDimScale[3], numChunks, cellChunkSize, chunk;
6330:   PetscInt        maxDegree   = PETSC_INT_MAX;
6331:   PetscQuadrature affineQuadF = NULL, *quadsF = NULL;
6332:   PetscFEGeom    *affineGeomF = NULL, **geomsF = NULL;
6333:   PetscQuadrature affineQuadN = NULL;
6334:   PetscFEGeom    *affineGeomN = NULL;
6335:   PetscBool       hasBdJac, hasBdPrec;

6337:   PetscFunctionBegin;
6338:   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
6339:   if (!cellIS) goto end;
6340:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
6341:   PetscCall(ISGetLocalSize(cellIS, &numCells));
6342:   if (cStart >= cEnd) goto end;
6343:   if ((key[0].label == key[1].label) && (key[0].value == key[1].value) && (key[0].part == key[1].part)) {
6344:     const char *name;
6345:     PetscCall(PetscObjectGetName((PetscObject)key[0].label, &name));
6346:     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);
6347:   }
6348:   PetscCall(DMConvert(dm, DMPLEX, &plex));
6349:   PetscCall(DMGetLocalSection(dm, &section));
6350:   PetscCall(DMGetGlobalSection(dm, &globalSection));
6351:   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
6352:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn));
6353:   PetscCall(PetscDSGetNumFields(ds, &Nf));
6354:   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
6355:   PetscCall(PetscDSGetTotalDimension(dsIn, &totDimIn));
6356:   PetscCall(PetscDSHasBdJacobian(ds, &hasBdJac));
6357:   PetscCall(PetscDSHasBdJacobianPreconditioner(ds, &hasBdPrec));
6358:   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, key[2].value, key[2].part, &locA[2]));
6359:   if (locA[2]) {
6360:     const PetscInt cellStart = cells ? cells[cStart] : cStart;

6362:     PetscCall(VecGetDM(locA[2], &dmAux[2]));
6363:     PetscCall(DMConvert(dmAux[2], DMPLEX, &plexA));
6364:     PetscCall(DMGetLocalSection(dmAux[2], &sectionAux[2]));
6365:     PetscCall(DMGetCellDS(dmAux[2], cellStart, &dsAux[2], NULL));
6366:     PetscCall(PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]));
6367:     {
6368:       const PetscInt *cone;
6369:       PetscInt        c;

6371:       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
6372:       for (c = 0; c < 2; ++c) {
6373:         const PetscInt *support;
6374:         PetscInt        ssize, s;

6376:         PetscCall(DMPlexGetSupport(dm, cone[c], &support));
6377:         PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
6378:         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);
6379:         if (support[0] == cellStart) s = 1;
6380:         else if (support[1] == cellStart) s = 0;
6381:         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
6382:         PetscCall(DMGetAuxiliaryVec(dm, key[c].label, key[c].value, key[c].part, &locA[c]));
6383:         if (locA[c]) PetscCall(VecGetDM(locA[c], &dmAux[c]));
6384:         else dmAux[c] = dmAux[2];
6385:         PetscCall(DMGetCellDS(dmAux[c], support[s], &dsAux[c], NULL));
6386:         PetscCall(PetscDSGetTotalDimension(dsAux[c], &totDimAux[c]));
6387:       }
6388:     }
6389:   }
6390:   /* Handle mass matrix scaling
6391:        The field in key[2] is the field to be scaled, and the scaling field is the first in the dsScale */
6392:   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, -key[2].value, key[2].part, &locS[2]));
6393:   if (locS[2]) {
6394:     const PetscInt cellStart = cells ? cells[cStart] : cStart;
6395:     PetscInt       Nb, Nbs;

6397:     PetscCall(VecGetDM(locS[2], &dmScale[2]));
6398:     PetscCall(DMGetCellDS(dmScale[2], cells ? cells[cStart] : cStart, &dsScale[2], NULL));
6399:     PetscCall(PetscDSGetTotalDimension(dsScale[2], &totDimScale[2]));
6400:     // BRAD: This is not set correctly
6401:     key[2].field = 2;
6402:     PetscCall(PetscDSGetFieldSize(ds, key[2].field, &Nb));
6403:     PetscCall(PetscDSGetFieldSize(dsScale[2], 0, &Nbs));
6404:     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);
6405:     {
6406:       const PetscInt *cone;
6407:       PetscInt        c;

6409:       locS[1] = locS[0] = locS[2];
6410:       dmScale[1] = dmScale[0] = dmScale[2];
6411:       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
6412:       for (c = 0; c < 2; ++c) {
6413:         const PetscInt *support;
6414:         PetscInt        ssize, s;

6416:         PetscCall(DMPlexGetSupport(dm, cone[c], &support));
6417:         PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
6418:         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);
6419:         if (support[0] == cellStart) s = 1;
6420:         else if (support[1] == cellStart) s = 0;
6421:         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
6422:         PetscCall(DMGetCellDS(dmScale[c], support[s], &dsScale[c], NULL));
6423:         PetscCall(PetscDSGetTotalDimension(dsScale[c], &totDimScale[c]));
6424:       }
6425:     }
6426:   }
6427:   /* 2: Setup geometric data */
6428:   PetscCall(DMGetCoordinateField(dm, &coordField));
6429:   PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
6430:   if (maxDegree > 1) {
6431:     PetscInt f;
6432:     PetscCall(PetscCalloc2(Nf, &quadsF, Nf, &geomsF));
6433:     for (f = 0; f < Nf; ++f) {
6434:       PetscFE fe;

6436:       PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
6437:       if (fe) {
6438:         PetscCall(PetscFEGetQuadrature(fe, &quadsF[f]));
6439:         PetscCall(PetscObjectReference((PetscObject)quadsF[f]));
6440:       }
6441:     }
6442:   }
6443:   /* Loop over chunks */
6444:   cellChunkSize = numCells;
6445:   numChunks     = !numCells ? 0 : PetscCeilReal(((PetscReal)numCells) / cellChunkSize);
6446:   PetscCall(PetscCalloc2(2 * cellChunkSize, &faces, 2 * cellChunkSize, &neighbors));
6447:   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 2 * cellChunkSize, faces, PETSC_USE_POINTER, &chunkISF));
6448:   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 2 * cellChunkSize, neighbors, PETSC_USE_POINTER, &chunkISN));
6449:   /* Extract field coefficients */
6450:   /* NOTE This needs the end cap faces to have identical orientations */
6451:   PetscCall(DMPlexGetHybridCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
6452:   PetscCall(DMPlexGetHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
6453:   PetscCall(DMPlexGetHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_TRUE, s));
6454:   PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNeg));
6455:   PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPos));
6456:   PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCoh));
6457:   PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNegP));
6458:   PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPosP));
6459:   PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCohP));
6460:   for (chunk = 0; chunk < numChunks; ++chunk) {
6461:     PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;

6463:     if (hasBdJac) {
6464:       PetscCall(PetscArrayzero(elemMatNeg, cellChunkSize * totDim * totDim));
6465:       PetscCall(PetscArrayzero(elemMatPos, cellChunkSize * totDim * totDim));
6466:       PetscCall(PetscArrayzero(elemMatCoh, cellChunkSize * totDim * totDim));
6467:     }
6468:     if (hasBdPrec) {
6469:       PetscCall(PetscArrayzero(elemMatNegP, cellChunkSize * totDim * totDim));
6470:       PetscCall(PetscArrayzero(elemMatPosP, cellChunkSize * totDim * totDim));
6471:       PetscCall(PetscArrayzero(elemMatCohP, cellChunkSize * totDim * totDim));
6472:     }
6473:     /* Get faces */
6474:     for (c = cS; c < cE; ++c) {
6475:       const PetscInt  cell = cells ? cells[c] : c;
6476:       const PetscInt *cone, *support;
6477:       PetscCall(DMPlexGetCone(plex, cell, &cone));
6478:       faces[(c - cS) * 2 + 0] = cone[0];
6479:       faces[(c - cS) * 2 + 1] = cone[1];
6480:       PetscCall(DMPlexGetSupport(dm, cone[0], &support));
6481:       neighbors[(c - cS) * 2 + 0] = support[0] == cell ? support[1] : support[0];
6482:       PetscCall(DMPlexGetSupport(dm, cone[1], &support));
6483:       neighbors[(c - cS) * 2 + 1] = support[0] == cell ? support[1] : support[0];
6484:     }
6485:     PetscCall(ISGeneralSetIndices(chunkISF, 2 * cellChunkSize, faces, PETSC_USE_POINTER));
6486:     PetscCall(ISGeneralSetIndices(chunkISN, 2 * cellChunkSize, neighbors, PETSC_USE_POINTER));
6487:     if (maxDegree <= 1) {
6488:       if (!affineQuadF) PetscCall(DMFieldCreateDefaultQuadrature(coordField, chunkISF, &affineQuadF));
6489:       if (affineQuadF) PetscCall(DMSNESGetFEGeom(coordField, chunkISF, affineQuadF, PETSC_FEGEOM_COHESIVE, &affineGeomF));
6490:       if (!affineQuadN) {
6491:         PetscInt dim;
6492:         PetscCall(PetscQuadratureGetData(affineQuadF, &dim, NULL, NULL, NULL, NULL));
6493:         PetscCall(DMFieldCreateDefaultFaceQuadrature(coordField, chunkISN, &affineQuadN));
6494:         PetscCall(PetscQuadratureSetData(affineQuadN, dim + 1, PETSC_DECIDE, PETSC_DECIDE, NULL, NULL));
6495:       }
6496:       if (affineQuadN) PetscCall(DMSNESGetFEGeom(coordField, chunkISN, affineQuadN, PETSC_FEGEOM_BASIC, &affineGeomN));
6497:     } else {
6498:       PetscInt f;
6499:       for (f = 0; f < Nf; ++f) {
6500:         if (quadsF[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkISF, quadsF[f], PETSC_FEGEOM_COHESIVE, &geomsF[f]));
6501:       }
6502:     }

6504:     for (fieldI = 0; fieldI < Nf; ++fieldI) {
6505:       PetscFE         feI;
6506:       PetscFEGeom    *geomF      = affineGeomF ? affineGeomF : geomsF[fieldI];
6507:       PetscFEGeom    *chunkGeomF = NULL, *remGeomF = NULL;
6508:       PetscFEGeom    *geomN      = affineGeomN ? affineGeomN : geomsF[fieldI];
6509:       PetscFEGeom    *chunkGeomN = NULL, *remGeomN = NULL;
6510:       PetscQuadrature quadF = affineQuadF ? affineQuadF : quadsF[fieldI];
6511:       PetscInt        numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb;
6512:       PetscBool       isCohesiveField;

6514:       PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&feI));
6515:       if (!feI) continue;
6516:       PetscCall(PetscFEGetTileSizes(feI, NULL, &numBlocks, NULL, &numBatches));
6517:       PetscCall(PetscQuadratureGetData(quadF, NULL, NULL, &Nq, NULL, NULL));
6518:       PetscCall(PetscFEGetDimension(feI, &Nb));
6519:       blockSize = Nb;
6520:       batchSize = numBlocks * blockSize;
6521:       PetscCall(PetscFESetTileSizes(feI, blockSize, numBlocks, batchSize, numBatches));
6522:       numChunks = numCells / (numBatches * batchSize);
6523:       Ne        = numChunks * numBatches * batchSize;
6524:       Nr        = numCells % (numBatches * batchSize);
6525:       offset    = numCells - Nr;
6526:       PetscCall(PetscFEGeomGetChunk(geomF, 0, offset * 2, &chunkGeomF));
6527:       PetscCall(PetscFEGeomGetChunk(geomF, offset * 2, numCells * 2, &remGeomF));
6528:       PetscCall(PetscFEGeomGetChunk(geomN, 0, offset * 2, &chunkGeomN));
6529:       PetscCall(PetscFEGeomGetChunk(geomN, offset * 2, numCells * 2, &remGeomN));
6530:       PetscCall(PetscDSGetCohesive(ds, fieldI, &isCohesiveField));
6531:       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
6532:         PetscFE feJ;

6534:         PetscCall(PetscDSGetDiscretization(ds, fieldJ, (PetscObject *)&feJ));
6535:         if (!feJ) continue;
6536:         key[0].field = fieldI * Nf + fieldJ;
6537:         key[1].field = fieldI * Nf + fieldJ;
6538:         key[2].field = fieldI * Nf + fieldJ;
6539:         if (hasBdJac) {
6540:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[0], 0, Ne, chunkGeomF, chunkGeomN, u, u_t, dsAux[0], a[0], t, X_tShift, elemMatNeg));
6541:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[0], 0, Nr, remGeomF, remGeomN, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[0], PetscSafePointerPlusOffset(a[0], offset * totDimAux[0]), t, X_tShift, &elemMatNeg[offset * totDim * totDim]));
6542:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[1], 1, Ne, chunkGeomF, chunkGeomN, u, u_t, dsAux[1], a[1], t, X_tShift, elemMatPos));
6543:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[1], 1, Nr, remGeomF, remGeomN, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[1], PetscSafePointerPlusOffset(a[1], offset * totDimAux[1]), t, X_tShift, &elemMatPos[offset * totDim * totDim]));
6544:         }
6545:         if (hasBdPrec) {
6546:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[0], 0, Ne, chunkGeomF, chunkGeomN, u, u_t, dsAux[0], a[0], t, X_tShift, elemMatNegP));
6547:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[0], 0, Nr, remGeomF, remGeomN, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[0], &a[0][offset * totDimAux[0]], t, X_tShift, &elemMatNegP[offset * totDim * totDim]));
6548:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[1], 1, Ne, chunkGeomF, chunkGeomN, u, u_t, dsAux[1], a[1], t, X_tShift, elemMatPosP));
6549:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[1], 1, Nr, remGeomF, remGeomN, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[1], &a[1][offset * totDimAux[1]], t, X_tShift, &elemMatPosP[offset * totDim * totDim]));
6550:         }
6551:         if (hasBdJac) {
6552:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[2], 2, Ne, chunkGeomF, chunkGeomN, u, u_t, dsAux[2], a[2], t, X_tShift, elemMatCoh));
6553:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[2], 2, Nr, remGeomF, remGeomN, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[2], PetscSafePointerPlusOffset(a[2], offset * totDimAux[2]), t, X_tShift, &elemMatCoh[offset * totDim * totDim]));
6554:         }
6555:         if (hasBdPrec) {
6556:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[2], 2, Ne, chunkGeomF, chunkGeomN, u, u_t, dsAux[2], a[2], t, X_tShift, elemMatCohP));
6557:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[2], 2, Nr, remGeomF, remGeomN, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[2], &a[2][offset * totDimAux[2]], t, X_tShift, &elemMatCohP[offset * totDim * totDim]));
6558:         }
6559:       }
6560:       PetscCall(PetscFEGeomRestoreChunk(geomF, offset, numCells, &remGeomF));
6561:       PetscCall(PetscFEGeomRestoreChunk(geomF, 0, offset, &chunkGeomF));
6562:       PetscCall(PetscFEGeomRestoreChunk(geomN, offset, numCells, &remGeomN));
6563:       PetscCall(PetscFEGeomRestoreChunk(geomN, 0, offset, &chunkGeomN));
6564:     }
6565:     /* Insert values into matrix */
6566:     for (c = cS; c < cE; ++c) {
6567:       const PetscInt cell = cells ? cells[c] : c;
6568:       const PetscInt cind = c - cS, coff = cind * totDim * totDim;
6569:       PetscInt       i, j;

6571:       /* Scale element values */
6572:       if (locS[0]) {
6573:         PetscInt  Nb, soff = cind * totDimScale[0], off = 0;
6574:         PetscBool cohesive;

6576:         for (fieldI = 0; fieldI < Nf; ++fieldI) {
6577:           PetscCall(PetscDSGetFieldSize(ds, fieldI, &Nb));
6578:           PetscCall(PetscDSGetCohesive(ds, fieldI, &cohesive));

6580:           if (fieldI == key[2].field) {
6581:             PetscCheck(cohesive, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Scaling should not happen for face fields");
6582:             for (i = 0; i < Nb; ++i) {
6583:               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];
6584:               if (hasBdPrec)
6585:                 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];
6586:             }
6587:             off += Nb;
6588:           } else {
6589:             const PetscInt N = cohesive ? Nb : Nb * 2;

6591:             for (i = 0; i < N; ++i) {
6592:               for (j = 0; j < totDim; ++j) elemMatCoh[coff + (off + i) * totDim + j] += elemMatNeg[coff + (off + i) * totDim + j] + elemMatPos[coff + (off + i) * totDim + j];
6593:               if (hasBdPrec)
6594:                 for (j = 0; j < totDim; ++j) elemMatCohP[coff + (off + i) * totDim + j] += elemMatNegP[coff + (off + i) * totDim + j] + elemMatPosP[coff + (off + i) * totDim + j];
6595:             }
6596:             off += N;
6597:           }
6598:         }
6599:       } else {
6600:         for (i = 0; i < totDim * totDim; ++i) elemMatCoh[coff + i] += elemMatNeg[coff + i] + elemMatPos[coff + i];
6601:         if (hasBdPrec)
6602:           for (i = 0; i < totDim * totDim; ++i) elemMatCohP[coff + i] += elemMatNegP[coff + i] + elemMatPosP[coff + i];
6603:       }
6604:       if (hasBdPrec) {
6605:         if (hasBdJac) {
6606:           if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCoh[cind * totDim * totDim]));
6607:           PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, Jac, cell, &elemMatCoh[cind * totDim * totDim], ADD_VALUES));
6608:         }
6609:         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCohP[cind * totDim * totDim]));
6610:         PetscCall(DMPlexMatSetClosure(plex, section, globalSection, JacP, cell, &elemMatCohP[cind * totDim * totDim], ADD_VALUES));
6611:       } else if (hasBdJac) {
6612:         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCoh[cind * totDim * totDim]));
6613:         PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, JacP, cell, &elemMatCoh[cind * totDim * totDim], ADD_VALUES));
6614:       }
6615:     }
6616:   }
6617:   PetscCall(DMPlexRestoreCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
6618:   PetscCall(DMPlexRestoreHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
6619:   PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNeg));
6620:   PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPos));
6621:   PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCoh));
6622:   PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNegP));
6623:   PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPosP));
6624:   PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCohP));
6625:   PetscCall(PetscFree2(faces, neighbors));
6626:   PetscCall(ISDestroy(&chunkISF));
6627:   PetscCall(ISDestroy(&chunkISN));
6628:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
6629:   if (maxDegree <= 1) {
6630:     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuadF, PETSC_FALSE, &affineGeomF));
6631:     PetscCall(PetscQuadratureDestroy(&affineQuadF));
6632:     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuadN, PETSC_FALSE, &affineGeomN));
6633:     PetscCall(PetscQuadratureDestroy(&affineQuadN));
6634:   } else {
6635:     PetscInt f;
6636:     for (f = 0; f < Nf; ++f) {
6637:       if (geomsF) PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quadsF[f], PETSC_FALSE, &geomsF[f]));
6638:       if (quadsF) PetscCall(PetscQuadratureDestroy(&quadsF[f]));
6639:     }
6640:     PetscCall(PetscFree2(quadsF, geomsF));
6641:   }
6642:   if (dmAux[2]) PetscCall(DMDestroy(&plexA));
6643:   PetscCall(DMDestroy(&plex));
6644: end:
6645:   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
6646:   PetscFunctionReturn(PETSC_SUCCESS);
6647: }

6649: /*@
6650:   DMPlexComputeJacobianActionByKey - Compute the local Jacobian for terms matching the input key

6652:   Collective

6654:   Input Parameters:
6655: + dm       - The output `DM`
6656: . key      - The `PetscFormKey` indicating what should be integrated
6657: . cellIS   - The `IS` give a set of cells to integrate over
6658: . t        - The time
6659: . X_tShift - The multiplier for the Jacobian with respect to $X_t$
6660: . locX     - The local solution
6661: . locX_t   - The time derivative of the local solution, or `NULL` for time-independent problems
6662: . locY     - The local vector acted on by J
6663: - user     - An optional user context, passed to the pointwise functions

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

6668:   Level: developer

6670: .seealso: `DMPlexComputeResidualByKey()`, `DMPlexComputeJacobianByKey()`, `DMPlexComputeResidualHybridByKey()`, `DMPlexComputeJacobianHybridByKey()`, `PetscFormKey`
6671: @*/
6672: PetscErrorCode DMPlexComputeJacobianActionByKey(DM dm, PetscFormKey key, IS cellIS, PetscReal t, PetscReal X_tShift, Vec locX, Vec locX_t, Vec locY, Vec locF, void *user)
6673: {
6674:   DM_Plex        *mesh  = (DM_Plex *)dm->data;
6675:   const char     *name  = "Jacobian";
6676:   DM              dmAux = NULL, plex, plexAux = NULL;
6677:   DMEnclosureType encAux;
6678:   Vec             A;
6679:   DMField         coordField;
6680:   PetscDS         prob, probAux = NULL;
6681:   PetscQuadrature quad;
6682:   PetscSection    section, globalSection, sectionAux;
6683:   PetscScalar    *elemMat, *elemMatD, *u, *u_t, *a = NULL, *y, *z;
6684:   const PetscInt *cells;
6685:   PetscInt        Nf, fieldI, fieldJ;
6686:   PetscInt        totDim, totDimAux = 0, cStart, cEnd, numCells, c;
6687:   PetscBool       hasDyn;

6689:   PetscFunctionBegin;
6690:   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
6691:   PetscCall(DMConvert(dm, DMPLEX, &plex));
6692:   PetscCall(ISGetLocalSize(cellIS, &numCells));
6693:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
6694:   PetscCall(DMGetLocalSection(dm, &section));
6695:   PetscCall(DMGetGlobalSection(dm, &globalSection));
6696:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL));
6697:   PetscCall(PetscDSGetNumFields(prob, &Nf));
6698:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
6699:   PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
6700:   hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
6701:   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &A));
6702:   if (A) {
6703:     PetscCall(VecGetDM(A, &dmAux));
6704:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
6705:     PetscCall(DMConvert(dmAux, DMPLEX, &plexAux));
6706:     PetscCall(DMGetLocalSection(plexAux, &sectionAux));
6707:     PetscCall(DMGetDS(dmAux, &probAux));
6708:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
6709:   }
6710:   PetscCall(VecSet(locF, 0.0));
6711:   PetscCall(PetscMalloc6(numCells * totDim, &u, (locX_t ? (size_t)numCells * totDim : 0), &u_t, numCells * totDim * totDim, &elemMat, (hasDyn ? (size_t)numCells * totDim * totDim : 0), &elemMatD, numCells * totDim, &y, totDim, &z));
6712:   if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
6713:   PetscCall(DMGetCoordinateField(dm, &coordField));
6714:   for (c = cStart; c < cEnd; ++c) {
6715:     const PetscInt cell = cells ? cells[c] : c;
6716:     const PetscInt cind = c - cStart;
6717:     PetscScalar   *x = NULL, *x_t = NULL;
6718:     PetscInt       i;

6720:     PetscCall(DMPlexVecGetClosure(plex, section, locX, cell, NULL, &x));
6721:     for (i = 0; i < totDim; ++i) u[cind * totDim + i] = x[i];
6722:     PetscCall(DMPlexVecRestoreClosure(plex, section, locX, cell, NULL, &x));
6723:     if (locX_t) {
6724:       PetscCall(DMPlexVecGetClosure(plex, section, locX_t, cell, NULL, &x_t));
6725:       for (i = 0; i < totDim; ++i) u_t[cind * totDim + i] = x_t[i];
6726:       PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, cell, NULL, &x_t));
6727:     }
6728:     if (dmAux) {
6729:       PetscInt subcell;
6730:       PetscCall(DMGetEnclosurePoint(dmAux, dm, encAux, cell, &subcell));
6731:       PetscCall(DMPlexVecGetClosure(plexAux, sectionAux, A, subcell, NULL, &x));
6732:       for (i = 0; i < totDimAux; ++i) a[cind * totDimAux + i] = x[i];
6733:       PetscCall(DMPlexVecRestoreClosure(plexAux, sectionAux, A, subcell, NULL, &x));
6734:     }
6735:     PetscCall(DMPlexVecGetClosure(plex, section, locY, cell, NULL, &x));
6736:     for (i = 0; i < totDim; ++i) y[cind * totDim + i] = x[i];
6737:     PetscCall(DMPlexVecRestoreClosure(plex, section, locY, cell, NULL, &x));
6738:   }
6739:   PetscCall(PetscArrayzero(elemMat, numCells * totDim * totDim));
6740:   if (hasDyn) PetscCall(PetscArrayzero(elemMatD, numCells * totDim * totDim));
6741:   for (fieldI = 0; fieldI < Nf; ++fieldI) {
6742:     PetscFE  fe;
6743:     PetscInt Nb;
6744:     /* Conforming batches */
6745:     PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
6746:     /* Remainder */
6747:     PetscInt        Nr, offset, Nq;
6748:     PetscQuadrature qGeom = NULL;
6749:     PetscInt        maxDegree;
6750:     PetscFEGeom    *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL;

6752:     PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
6753:     PetscCall(PetscFEGetQuadrature(fe, &quad));
6754:     PetscCall(PetscFEGetDimension(fe, &Nb));
6755:     PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
6756:     PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
6757:     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
6758:     if (!qGeom) {
6759:       PetscCall(PetscFEGetQuadrature(fe, &qGeom));
6760:       PetscCall(PetscObjectReference((PetscObject)qGeom));
6761:     }
6762:     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
6763:     PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FEGEOM_BASIC, &cgeomFEM));
6764:     blockSize = Nb;
6765:     batchSize = numBlocks * blockSize;
6766:     PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
6767:     numChunks = numCells / (numBatches * batchSize);
6768:     Ne        = numChunks * numBatches * batchSize;
6769:     Nr        = numCells % (numBatches * batchSize);
6770:     offset    = numCells - Nr;
6771:     PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
6772:     PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &remGeom));
6773:     for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
6774:       key.field = fieldI * Nf + fieldJ;
6775:       PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat));
6776:       PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Nr, remGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), probAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, X_tShift, &elemMat[offset * totDim * totDim]));
6777:       if (hasDyn) {
6778:         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatD));
6779:         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Nr, remGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), probAux, &a[offset * totDimAux], t, X_tShift, &elemMatD[offset * totDim * totDim]));
6780:       }
6781:     }
6782:     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &remGeom));
6783:     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, 0, offset, &chunkGeom));
6784:     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
6785:     PetscCall(PetscQuadratureDestroy(&qGeom));
6786:   }
6787:   if (hasDyn) {
6788:     for (c = 0; c < numCells * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
6789:   }
6790:   for (c = cStart; c < cEnd; ++c) {
6791:     const PetscInt     cell = cells ? cells[c] : c;
6792:     const PetscInt     cind = c - cStart;
6793:     const PetscBLASInt one  = 1;
6794:     PetscBLASInt       M;
6795:     const PetscScalar  a = 1.0, b = 0.0;

6797:     PetscCall(PetscBLASIntCast(totDim, &M));
6798:     PetscCallBLAS("BLASgemv", BLASgemv_("N", &M, &M, &a, &elemMat[cind * totDim * totDim], &M, &y[cind * totDim], &one, &b, z, &one));
6799:     if (mesh->printFEM > 1) {
6800:       PetscCall(DMPrintCellMatrix(c, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
6801:       PetscCall(DMPrintCellVector(c, "Y", totDim, &y[cind * totDim]));
6802:       PetscCall(DMPrintCellVector(c, "Z", totDim, z));
6803:     }
6804:     PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, z, ADD_VALUES));
6805:   }
6806:   PetscCall(PetscFree6(u, u_t, elemMat, elemMatD, y, z));
6807:   if (mesh->printFEM) {
6808:     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)locF), "Z:\n"));
6809:     PetscCall(VecView(locF, NULL));
6810:   }
6811:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
6812:   PetscCall(PetscFree(a));
6813:   PetscCall(DMDestroy(&plexAux));
6814:   PetscCall(DMDestroy(&plex));
6815:   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
6816:   PetscFunctionReturn(PETSC_SUCCESS);
6817: }

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

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

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

6833:   f0[0] = 0.0;
6834:   for (d = 0; d < dim; ++d) f0[0] += PetscSqr(x[d]) * u[0];
6835: }

6837: /*@
6838:   DMPlexComputeMoments - Compute the first three moments for a field

6840:   Noncollective

6842:   Input Parameters:
6843: + dm - the `DMPLEX`
6844: - u  - the field

6846:   Output Parameter:
6847: . moments - the field moments

6849:   Level: intermediate

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

6854: .seealso: `DM`, `DMPLEX`, `DMSwarmComputeMoments()`
6855: @*/
6856: PetscErrorCode DMPlexComputeMoments(DM dm, Vec u, PetscReal moments[])
6857: {
6858:   PetscDS            ds;
6859:   PetscScalar        mom, constants[1];
6860:   const PetscScalar *oldConstants;
6861:   PetscInt           cdim, Nf, field = 0, Ncon;
6862:   MPI_Comm           comm;
6863:   void              *user;

6865:   PetscFunctionBeginUser;
6866:   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
6867:   PetscCall(DMGetCoordinateDim(dm, &cdim));
6868:   PetscCall(DMGetApplicationContext(dm, &user));
6869:   PetscCall(DMGetDS(dm, &ds));
6870:   PetscCall(PetscDSGetNumFields(ds, &Nf));
6871:   PetscCall(PetscDSGetConstants(ds, &Ncon, &oldConstants));
6872:   PetscCheck(Nf == 1, comm, PETSC_ERR_ARG_WRONG, "We currently only support 1 field, not %" PetscInt_FMT, Nf);
6873:   PetscCall(PetscDSSetObjective(ds, field, &f0_1));
6874:   PetscCall(DMPlexComputeIntegralFEM(dm, u, &mom, user));
6875:   moments[0] = PetscRealPart(mom);
6876:   for (PetscInt c = 0; c < cdim; ++c) {
6877:     constants[0] = c;
6878:     PetscCall(PetscDSSetConstants(ds, 1, constants));
6879:     PetscCall(PetscDSSetObjective(ds, field, &f0_x));
6880:     PetscCall(DMPlexComputeIntegralFEM(dm, u, &mom, user));
6881:     moments[c + 1] = PetscRealPart(mom);
6882:   }
6883:   PetscCall(PetscDSSetObjective(ds, field, &f0_x2));
6884:   PetscCall(DMPlexComputeIntegralFEM(dm, u, &mom, user));
6885:   moments[cdim + 1] = PetscRealPart(mom);
6886:   PetscCall(PetscDSSetConstants(ds, Ncon, (PetscScalar *)oldConstants));
6887:   PetscFunctionReturn(PETSC_SUCCESS);
6888: }