Actual source code: plexfem.c

  1: #include "petscdm.h"
  2: #include "petscerror.h"
  3: #include <petsc/private/dmpleximpl.h>
  4: #include <petscsf.h>

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

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

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

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

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

 52: static PetscErrorCode PetscContainerCtxDestroy_PetscFEGeom(void **ctx)
 53: {
 54:   PetscFEGeom *geom = (PetscFEGeom *)*ctx;

 56:   PetscFunctionBegin;
 57:   PetscCall(PetscFEGeomDestroy(&geom));
 58:   PetscFunctionReturn(PETSC_SUCCESS);
 59: }

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

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

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

 91: /*@
 92:   DMPlexGetScale - Get the scale for the specified fundamental unit

 94:   Not Collective

 96:   Input Parameters:
 97: + dm   - the `DM`
 98: - unit - The SI unit

100:   Output Parameter:
101: . scale - The value used to scale all quantities with this unit

103:   Level: advanced

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

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

118: /*@
119:   DMPlexSetScale - Set the scale for the specified fundamental unit

121:   Not Collective

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

128:   Level: advanced

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

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

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

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

154:   PetscFunctionBegin;
155:   mesh->useCeed = useCeed;
156:   PetscFunctionReturn(PETSC_SUCCESS);
157: }

159: /*@
160:   DMPlexGetUseCeed - Get flag for using the LibCEED backend

162:   Not collective

164:   Input Parameter:
165: . dm - The `DM`

167:   Output Parameter:
168: . useCeed - The flag

170:   Level: intermediate

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

184: /*@
185:   DMPlexSetUseCeed - Set flag for using the LibCEED backend

187:   Not collective

189:   Input Parameters:
190: + dm      - The `DM`
191: - useCeed - The flag

193:   Level: intermediate

195: .seealso: `DMPlexGetUseCeed()`
196: @*/
197: PetscErrorCode DMPlexSetUseCeed(DM dm, PetscBool useCeed)
198: {
199:   PetscFunctionBegin;
202:   PetscUseMethod(dm, "DMPlexSetUseCeed_C", (DM, PetscBool), (dm, useCeed));
203:   PetscFunctionReturn(PETSC_SUCCESS);
204: }

206: /*@
207:   DMPlexGetUseMatClosurePermutation - Get flag for using a closure permutation for matrix insertion

209:   Not collective

211:   Input Parameter:
212: . dm - The `DM`

214:   Output Parameter:
215: . useClPerm - The flag

217:   Level: intermediate

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

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

232: /*@
233:   DMPlexSetUseMatClosurePermutation - Set flag for using a closure permutation for matrix insertion

235:   Not collective

237:   Input Parameters:
238: + dm        - The `DM`
239: - useClPerm - The flag

241:   Level: intermediate

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

249:   PetscFunctionBegin;
252:   mesh->useMatClPerm = useClPerm;
253:   PetscFunctionReturn(PETSC_SUCCESS);
254: }

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

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

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

284:   Collective

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

290:   Output Parameter:
291: . sp - the null space

293:   Level: advanced

295:   Note:
296:   This is necessary to provide a suitable coarse space for algebraic multigrid

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

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

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

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

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

360:   Collective

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

369:   Output Parameter:
370: . sp - the null space

372:   Level: advanced

374:   Note:
375:   This is necessary to provide a suitable coarse space for algebraic multigrid

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

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

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

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

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

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

438:   Level: advanced

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

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

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

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

464:   Input Parameter:
465: . dm - the `DMPLEX` object

467:   Output Parameter:
468: . height - the maximum projection height

470:   Level: intermediate

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

794:   Output Parameter:
795: . lv - A local vector with values in the local basis

797:   Level: developer

799:   Note:
800:   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.

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

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

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

820:   Output Parameter:
821: . lv - A local vector with values in the global basis

823:   Level: developer

825:   Note:
826:   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.

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

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

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

849:   Level: developer

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

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

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

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

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

898:   Output Parameter:
899: . locX - A local vector to receives the boundary values

901:   Level: developer

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

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

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

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

937:   Output Parameter:
938: . locX - A local vector to receives the boundary values

940:   Level: developer

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

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

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

963:   Collective

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

978:   Output Parameter:
979: . locX - A local vector to receive the boundary values

981:   Level: developer

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

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

1001: /*@C
1002:   DMPlexInsertBoundaryValuesRiemann - Insert boundary values into a local vector

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

1019:   Output Parameter:
1020: . locX - A local vector to receives the boundary values

1022:   Level: developer

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1251:   Not Collective

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

1261:   Output Parameter:
1262: . locX - Solution updated with boundary values

1264:   Level: intermediate

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

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

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

1291:   Output Parameter:
1292: . locX_t - Solution updated with boundary values

1294:   Level: developer

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

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

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

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

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

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

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

1363:   Collective

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

1372:   Output Parameter:
1373: . diff - The diff ||u - u_h||_2

1375:   Level: developer

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1778: /*@C
1779:   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.

1781:   Collective

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

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

1793:   Level: developer

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

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

1823:     PetscCall(DMGetField(dm, field, NULL, &obj));
1824:     PetscCall(PetscObjectGetClassId(obj, &id));
1825:     if (id == PETSCFE_CLASSID) {
1826:       PetscFE fe = (PetscFE)obj;

1828:       PetscCall(PetscFEGetQuadrature(fe, &quad));
1829:       PetscCall(PetscFEGetNumComponents(fe, &Nc));
1830:     } else if (id == PETSCFV_CLASSID) {
1831:       PetscFV fv = (PetscFV)obj;

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

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

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

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

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

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

1906:   Collective

1908:   Input Parameters:
1909: + lu  - The local `Vec` containing the primal solution
1910: . f   - The field number for the potential
1911: . lmu - The local `Vec` containing the mixed solution
1912: - mf  - The field number for the flux

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

1917:   Level: advanced

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

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

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

1938:   PetscFunctionBegin;
1939:   PetscCall(VecGetDM(lu, &dm));
1940:   PetscCall(VecGetDM(lmu, &mdm));
1941:   PetscCall(VecGetDM(eFlux, &edm));
1942:   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1943:   PetscCall(VecSet(eFlux, 0.0));

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

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

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

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

1972:     for (PetscInt q = 0; q < Nq; ++q) {
1973:       PetscFEGeom qgeom;

1975:       qgeom.dimEmbed = fegeom.dimEmbed;
1976:       qgeom.J        = &fegeom.J[q * cdim * cdim];
1977:       qgeom.invJ     = &fegeom.invJ[q * cdim * cdim];
1978:       qgeom.detJ     = &fegeom.detJ[q];

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

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

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

1999:   PetscCall(VecAssemblyBegin(eFlux));
2000:   PetscCall(VecAssemblyEnd(eFlux));
2001:   PetscCall(VecSqrtAbs(eFlux));
2002:   PetscFunctionReturn(PETSC_SUCCESS);
2003: }

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

2008:   Collective

2010:   Input Parameters:
2011: + u  - The global `Vec` containing the primal solution
2012: . f  - The field number for the potential
2013: . mu - The global `Vec` containing the mixed solution
2014: - mf - The field number for the flux

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

2019:   Level: advanced

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

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

2026: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeL2FluxDiffVecLocal()`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
2027: @*/
2028: PetscErrorCode DMPlexComputeL2FluxDiffVec(Vec u, PetscInt f, Vec mu, PetscInt mf, Vec eFlux)
2029: {
2030:   DM  dm, mdm;
2031:   Vec lu, lmu;

2033:   PetscFunctionBegin;
2034:   PetscCall(VecGetDM(u, &dm));
2035:   PetscCall(DMGetLocalVector(dm, &lu));
2036:   PetscCall(DMGlobalToLocal(dm, u, INSERT_VALUES, lu));
2037:   PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, lu, 0.0, NULL, NULL, NULL));

2039:   PetscCall(VecGetDM(mu, &mdm));
2040:   PetscCall(DMGetLocalVector(mdm, &lmu));
2041:   PetscCall(DMGlobalToLocal(mdm, mu, INSERT_VALUES, lmu));
2042:   PetscCall(DMPlexInsertBoundaryValues(mdm, PETSC_TRUE, lmu, 0.0, NULL, NULL, NULL));

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

2046:   PetscCall(DMRestoreLocalVector(dm, &lu));
2047:   PetscCall(DMRestoreLocalVector(mdm, &lmu));
2048:   PetscFunctionReturn(PETSC_SUCCESS);
2049: }

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

2054:   Collective

2056:   Input Parameters:
2057: + dm   - The `DM`
2058: - locX - The coefficient vector u_h

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

2063:   Level: developer

2065:   Note:
2066:   $ 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

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

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

2095:     PetscCall(DMGetField(dm, f, NULL, &obj));
2096:     PetscCall(PetscObjectGetClassId(obj, &id));
2097:     if (id == PETSCFE_CLASSID) {
2098:       PetscFE fe = (PetscFE)obj;

2100:       PetscCall(PetscFEGetQuadrature(fe, &quad));
2101:       PetscCall(PetscFEGetNumComponents(fe, &fNc));
2102:     } else if (id == PETSCFV_CLASSID) {
2103:       PetscFV fv = (PetscFV)obj;

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

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

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

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

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

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

2186:   Collective

2188:   Input Parameters:
2189: + dm   - The `DM`
2190: - locX - The coefficient vector u_h

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

2195:   Level: developer

2197:   Note:
2198:   $\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

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

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

2228:     PetscCall(DMGetField(dm, field, NULL, &obj));
2229:     PetscCall(PetscObjectGetClassId(obj, &id));
2230:     if (id == PETSCFE_CLASSID) {
2231:       PetscFE fe = (PetscFE)obj;

2233:       PetscCall(PetscFEGetQuadrature(fe, &quad));
2234:       PetscCall(PetscFEGetNumComponents(fe, &Nc));
2235:     } else if (id == PETSCFV_CLASSID) {
2236:       PetscFV fv = (PetscFV)obj;

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

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

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

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

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

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

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

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

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

2396:     for (f = 0; f < Nf; ++f) {
2397:       PetscObject  obj;
2398:       PetscClassId id;

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

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

2446:     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
2447:     PetscCall(PetscObjectGetClassId(obj, &id));
2448:     if (id == PETSCFE_CLASSID) {
2449:       PetscFE         fe = (PetscFE)obj;
2450:       PetscQuadrature q;
2451:       PetscFEGeom    *chunkGeom = NULL;
2452:       PetscInt        Nq, Nb;

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

2476:       PetscCall(PetscDSGetObjective(prob, f, &obj_func));
2477:       PetscCall(PetscDSGetFieldOffset(prob, f, &foff));
2478:       if (obj_func) {
2479:         for (c = 0; c < numCells; ++c) {
2480:           PetscScalar *u_x;
2481:           PetscScalar  lint = 0.;

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

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

2512:   Input Parameters:
2513: + dm   - The mesh
2514: . X    - Global input vector
2515: - user - The user context

2517:   Output Parameter:
2518: . integral - Integral for each field

2520:   Level: developer

2522: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSNESComputeResidualFEM()`
2523: @*/
2524: PetscErrorCode DMPlexComputeIntegralFEM(DM dm, Vec X, PetscScalar *integral, void *user)
2525: {
2526:   PetscInt     printFEM;
2527:   PetscScalar *cintegral, *lintegral;
2528:   PetscInt     Nf, f, cellHeight, cStart, cEnd, cell;
2529:   Vec          locX;
2530:   PetscMPIInt  Nfi;

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

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

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

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

2579:   Output Parameter:
2580: . F - Cellwise integrals for each field

2582:   Level: developer

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

2595:   PetscFunctionBegin;
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(PetscCalloc1((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:   /* Put values in F */
2614:   PetscCall(VecGetArray(F, &af));
2615:   PetscCall(VecGetDM(F, &dmF));
2616:   if (dmF) PetscCall(DMGetLocalSection(dmF, &sectionF));
2617:   PetscCall(VecGetLocalSize(F, &n));
2618:   PetscCheck(n >= (cEnd - cStart) * Nf, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Vector size %" PetscInt_FMT " < %" PetscInt_FMT, n, (cEnd - cStart) * Nf);
2619:   printFEM = ((DM_Plex *)dm->data)->printFEM;
2620:   for (cell = cStart; cell < cEnd; ++cell) {
2621:     const PetscInt c   = cell - cStart;
2622:     PetscInt       dof = Nf, off = c * Nf;

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

2639: 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)
2640: {
2641:   DM                 plex = NULL, plexA = NULL;
2642:   DMEnclosureType    encAux;
2643:   PetscDS            prob, probAux       = NULL;
2644:   PetscSection       section, sectionAux = NULL;
2645:   Vec                locA = NULL;
2646:   DMField            coordField;
2647:   PetscInt           Nf, totDim, *uOff, *uOff_x;
2648:   PetscInt           NfAux = 0, totDimAux = 0, *aOff = NULL;
2649:   PetscScalar       *u, *a = NULL;
2650:   const PetscScalar *constants;
2651:   PetscInt           numConstants, f;

2653:   PetscFunctionBegin;
2654:   PetscCall(DMGetCoordinateField(dm, &coordField));
2655:   PetscCall(DMConvert(dm, DMPLEX, &plex));
2656:   PetscCall(DMGetDS(dm, &prob));
2657:   PetscCall(DMGetLocalSection(dm, &section));
2658:   PetscCall(PetscSectionGetNumFields(section, &Nf));
2659:   /* Determine which discretizations we have */
2660:   for (f = 0; f < Nf; ++f) {
2661:     PetscObject  obj;
2662:     PetscClassId id;

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

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

2696:     PetscCall(ISGetLocalSize(pointIS, &numFaces));
2697:     PetscCall(ISGetIndices(pointIS, &points));
2698:     PetscCall(PetscCalloc2(numFaces * totDim, &u, (locA ? (size_t)numFaces * totDimAux : 0), &a));
2699:     PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
2700:     for (face = 0; face < numFaces; ++face) {
2701:       const PetscInt point = points[face], *support;
2702:       PetscScalar   *x     = NULL;

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

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

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

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

2769:   Input Parameters:
2770: + dm      - The mesh
2771: . X       - Global input vector
2772: . label   - The boundary `DMLabel`
2773: . numVals - The number of label values to use, or `PETSC_DETERMINE` for all values
2774: . vals    - The label values to use, or NULL for all values
2775: . funcs   - The functions to integrate along the boundary for each field
2776: - user    - The user context

2778:   Output Parameter:
2779: . integral - Integral for each field

2781:   Level: developer

2783: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeIntegralFEM()`, `DMPlexComputeBdResidualFEM()`
2784: @*/
2785: 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)
2786: {
2787:   Vec          locX;
2788:   PetscSection section;
2789:   DMLabel      depthLabel;
2790:   IS           facetIS;
2791:   PetscInt     dim, Nf, f, v;

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

2817:     PetscCall(DMLabelGetStratumIS(label, vals[v], &pointIS));
2818:     if (!pointIS) continue; /* No points with that id on this process */
2819:     {
2820:       IS isectIS;

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

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

2845:   Input Parameters:
2846: + dmc       - The coarse mesh
2847: . dmf       - The fine mesh
2848: . isRefined - Flag indicating regular refinement, rather than the same topology
2849: - user      - The user context

2851:   Output Parameter:
2852: . In - The interpolation matrix

2854:   Level: developer

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

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

2885:     PetscCall(DMGetField(dmf, f, NULL, &obj));
2886:     PetscCall(PetscObjectGetClassId(obj, &id));
2887:     if (id == PETSCFE_CLASSID) {
2888:       PetscFE fe = (PetscFE)obj;

2890:       if (isRefined) {
2891:         PetscCall(PetscFERefine(fe, &feRef[f]));
2892:       } else {
2893:         PetscCall(PetscObjectReference((PetscObject)fe));
2894:         feRef[f] = fe;
2895:       }
2896:       PetscCall(PetscFEGetDimension(feRef[f], &rNb));
2897:       PetscCall(PetscFEGetNumComponents(fe, &Nc));
2898:     } else if (id == PETSCFV_CLASSID) {
2899:       PetscFV        fv = (PetscFV)obj;
2900:       PetscDualSpace Q;

2902:       if (isRefined) {
2903:         PetscCall(PetscFVRefine(fv, &fvRef[f]));
2904:       } else {
2905:         PetscCall(PetscObjectReference((PetscObject)fv));
2906:         fvRef[f] = fv;
2907:       }
2908:       PetscCall(PetscFVGetDualSpace(fvRef[f], &Q));
2909:       PetscCall(PetscDualSpaceGetDimension(Q, &rNb));
2910:       PetscCall(PetscFVGetDualSpace(fv, &Q));
2911:       PetscCall(PetscFVGetNumComponents(fv, &Nc));
2912:     }
2913:     PetscCall(DMGetField(dmc, f, NULL, &objc));
2914:     PetscCall(PetscObjectGetClassId(objc, &idc));
2915:     if (idc == PETSCFE_CLASSID) {
2916:       PetscFE fe = (PetscFE)objc;

2918:       PetscCall(PetscFEGetDimension(fe, &cNb));
2919:     } else if (id == PETSCFV_CLASSID) {
2920:       PetscFV        fv = (PetscFV)obj;
2921:       PetscDualSpace Q;

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

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

2960:     for (fieldJ = 0, offsetJ = 0; fieldJ < Nf; ++fieldJ) {
2961:       PetscObject  obj;
2962:       PetscClassId id;
2963:       PetscInt     NcJ = 0, cpdim = 0, j, qNc;

2965:       PetscCall(DMGetField(dmc, fieldJ, NULL, &obj));
2966:       PetscCall(PetscObjectGetClassId(obj, &id));
2967:       if (id == PETSCFE_CLASSID) {
2968:         PetscFE         fe = (PetscFE)obj;
2969:         PetscTabulation T  = NULL;

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

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

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

3075: PetscErrorCode DMPlexComputeMassMatrixNested(DM dmc, DM dmf, Mat mass, void *user)
3076: {
3077:   SETERRQ(PetscObjectComm((PetscObject)dmc), PETSC_ERR_SUP, "Laziness");
3078: }

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

3083:   Input Parameters:
3084: + dmf  - The fine mesh
3085: . dmc  - The coarse mesh
3086: - user - The user context

3088:   Output Parameter:
3089: . In - The interpolation matrix

3091:   Level: developer

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

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

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

3140:       PetscCall(PetscDSGetFieldOffset(prob, field, &off));
3141:       PetscCall(PetscDSGetDiscretization(prob, field, &obj));
3142:       PetscCall(PetscObjectGetClassId(obj, &id));
3143:       if (id == PETSCFE_CLASSID) {
3144:         PetscFE fe = (PetscFE)obj;

3146:         PetscCall(PetscFEGetDualSpace(fe, &Q));
3147:         PetscCall(PetscFEGetNumComponents(fe, &Nc));
3148:         if (s) PetscCall(PetscFECreateTabulation(fe, 1, 1, x, 0, &T));
3149:       } else if (id == PETSCFV_CLASSID) {
3150:         PetscFV fv = (PetscFV)obj;

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

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

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

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

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

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

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

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

3255:   Input Parameters:
3256: + dmf  - The fine mesh
3257: . dmc  - The coarse mesh
3258: - user - The user context

3260:   Output Parameter:
3261: . mass - The mass matrix

3263:   Level: developer

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

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

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

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

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

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

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

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

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

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

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

3439:         if (id == PETSCFE_CLASSID) {
3440:           PetscFE fe = (PetscFE)obj;

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

3487: /*@
3488:   DMPlexComputeInjectorFEM - Compute a mapping from coarse unknowns to fine unknowns

3490:   Input Parameters:
3491: + dmc  - The coarse mesh
3492: . dmf  - The fine mesh
3493: - user - The user context

3495:   Output Parameter:
3496: . sc - The mapping

3498:   Level: developer

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

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

3530:     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
3531:     PetscCall(PetscObjectGetClassId(obj, &id));
3532:     if (id == PETSCFE_CLASSID) {
3533:       PetscFE    fe = (PetscFE)obj;
3534:       PetscSpace sp;
3535:       PetscInt   maxDegree;

3537:       PetscCall(PetscFERefine(fe, &feRef[f]));
3538:       PetscCall(PetscFEGetDimension(feRef[f], &fNb));
3539:       PetscCall(PetscFEGetNumComponents(fe, &Nc));
3540:       PetscCall(PetscFEGetBasisSpace(fe, &sp));
3541:       PetscCall(PetscSpaceGetDegree(sp, NULL, &maxDegree));
3542:       if (!maxDegree) needAvg[f] = PETSC_TRUE;
3543:     } else if (id == PETSCFV_CLASSID) {
3544:       PetscFV        fv = (PetscFV)obj;
3545:       PetscDualSpace Q;

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

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

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

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

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

3646:   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, m, cindices, PETSC_OWN_POINTER, &cis));
3647:   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, m, findices, PETSC_OWN_POINTER, &fis));
3648:   PetscCall(VecScatterCreate(cv, cis, fv, fis, sc));
3649:   PetscCall(ISDestroy(&cis));
3650:   PetscCall(ISDestroy(&fis));
3651:   PetscCall(DMRestoreGlobalVector(dmf, &fv));
3652:   PetscCall(DMRestoreGlobalVector(dmc, &cv));
3653:   PetscCall(PetscLogEventEnd(DMPLEX_InjectorFEM, dmc, dmf, 0, 0));
3654:   PetscFunctionReturn(PETSC_SUCCESS);
3655: }

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

3660:   Input Parameters:
3661: + dm     - The `DM`
3662: . cellIS - The cells to include
3663: . locX   - A local vector with the solution fields
3664: . locX_t - A local vector with solution field time derivatives, or NULL
3665: - locA   - A local vector with auxiliary fields, or NULL

3667:   Output Parameters:
3668: + u   - The field coefficients
3669: . u_t - The fields derivative coefficients
3670: - a   - The auxiliary field coefficients

3672:   Level: developer

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

3685:   PetscFunctionBegin;
3690:   PetscAssertPointer(u, 6);
3691:   PetscAssertPointer(u_t, 7);
3692:   PetscAssertPointer(a, 8);
3693:   PetscCall(DMPlexConvertPlex(dm, &plex, PETSC_FALSE));
3694:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
3695:   PetscCall(DMGetLocalSection(dm, &section));
3696:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL));
3697:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
3698:   if (locA) {
3699:     DM      dmAux;
3700:     PetscDS probAux;

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

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

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

3746:   Input Parameters:
3747: + dm     - The `DM`
3748: . cellIS - The cells to include
3749: . locX   - A local vector with the solution fields
3750: . locX_t - A local vector with solution field time derivatives, or NULL
3751: - locA   - A local vector with auxiliary fields, or NULL

3753:   Output Parameters:
3754: + u   - The field coefficients
3755: . u_t - The fields derivative coefficients
3756: - a   - The auxiliary field coefficients

3758:   Level: developer

3760: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
3761: @*/
3762: PetscErrorCode DMPlexRestoreCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
3763: {
3764:   PetscFunctionBegin;
3765:   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u));
3766:   if (locX_t) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u_t));
3767:   if (locA) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, a));
3768:   PetscFunctionReturn(PETSC_SUCCESS);
3769: }

3771: static PetscErrorCode DMPlexGetHybridCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
3772: {
3773:   DM              plex, plexA = NULL;
3774:   DMEnclosureType encAux;
3775:   PetscSection    section, sectionAux;
3776:   PetscDS         ds, dsIn;
3777:   const PetscInt *cells;
3778:   PetscInt        cStart, cEnd, numCells, c, totDim, totDimAux, Nf, f;

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

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

3825:     PetscCall(DMPlexGetCone(dm, cell, &cone));
3826:     PetscCall(DMPlexGetConeOrientation(dm, cell, &ornt));
3827:     // Put in cohesive unknowns
3828:     PetscCall(DMPlexVecGetClosure(plex, section, locX, cell, &Nxf, &xf));
3829:     if (locX_t) PetscCall(DMPlexVecGetClosure(plex, section, locX_t, cell, NULL, &xf_t));
3830:     for (f = 0; f < Nf; ++f) {
3831:       PetscInt  fdofIn, foff, foffIn;
3832:       PetscBool cohesive;

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

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

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

3880:     if (locA) {
3881:       PetscScalar *al = &(*a)[cind * totDimAux];
3882:       PetscInt     subcell;

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

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

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

3908:   Output Parameter:
3909: . x       - An array of field values, indexed by s

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

3914:   Level: advanced

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

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

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

3958:       if (useCell) {
3959:         const PetscInt *support;
3960:         PetscInt        ssize;

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

3981: static PetscErrorCode DMPlexRestoreHybridFields(DM dm, DM dmX[], PetscDS dsX[], IS cellIS, Vec locX[], PetscBool useCell, PetscScalar *x[])
3982: {
3983:   PetscFunctionBegin;
3984:   if (!locX[0] || !locX[1]) PetscFunctionReturn(PETSC_SUCCESS);
3985:   PetscCall(DMRestoreWorkArray(dmX[0], 0, MPIU_SCALAR, &x[0]));
3986:   PetscCall(DMRestoreWorkArray(dmX[1], 0, MPIU_SCALAR, &x[1]));
3987:   PetscFunctionReturn(PETSC_SUCCESS);
3988: }

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

3993:   Input Parameters:
3994: + dm           - The `DM`
3995: . fStart       - The first face to include
3996: . fEnd         - The first face to exclude
3997: . locX         - A local vector with the solution fields
3998: . locX_t       - A local vector with solution field time derivatives, or NULL
3999: . faceGeometry - A local vector with face geometry
4000: . cellGeometry - A local vector with cell geometry
4001: - locGrad      - A local vector with field gradients, or NULL

4003:   Output Parameters:
4004: + Nface - The number of faces with field values
4005: . uL    - The field values at the left side of the face
4006: - uR    - The field values at the right side of the face

4008:   Level: developer

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

4022:   PetscFunctionBegin;
4029:   PetscAssertPointer(uL, 10);
4030:   PetscAssertPointer(uR, 11);
4031:   PetscCall(DMGetDimension(dm, &dim));
4032:   PetscCall(DMGetDS(dm, &prob));
4033:   PetscCall(DMGetLocalSection(dm, &section));
4034:   PetscCall(PetscDSGetNumFields(prob, &Nf));
4035:   PetscCall(PetscDSGetTotalComponents(prob, &Nc));
4036:   PetscCall(PetscMalloc1(Nf, &isFE));
4037:   for (f = 0; f < Nf; ++f) {
4038:     PetscObject  obj;
4039:     PetscClassId id;

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

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

4083:       PetscCall(PetscDSGetComponentOffset(prob, f, &off));
4084:       if (isFE[f]) {
4085:         const PetscInt *cone;
4086:         PetscInt        comp, coneSizeL, coneSizeR, faceLocL, faceLocR, ldof, rdof, d;

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

4120:         PetscCall(PetscDSGetDiscretization(prob, f, (PetscObject *)&fv));
4121:         PetscCall(PetscFVGetNumComponents(fv, &numComp));
4122:         PetscCall(DMPlexPointLocalFieldRead(dm, cells[0], f, x, &xL));
4123:         PetscCall(DMPlexPointLocalFieldRead(dm, cells[1], f, x, &xR));
4124:         if (dmGrad) {
4125:           PetscReal dxL[3], dxR[3];

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

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

4157:   Input Parameters:
4158: + dm           - The `DM`
4159: . fStart       - The first face to include
4160: . fEnd         - The first face to exclude
4161: . locX         - A local vector with the solution fields
4162: . locX_t       - A local vector with solution field time derivatives, or NULL
4163: . faceGeometry - A local vector with face geometry
4164: . cellGeometry - A local vector with cell geometry
4165: - locGrad      - A local vector with field gradients, or NULL

4167:   Output Parameters:
4168: + Nface - The number of faces with field values
4169: . uL    - The field values at the left side of the face
4170: - uR    - The field values at the right side of the face

4172:   Level: developer

4174: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
4175: @*/
4176: PetscErrorCode DMPlexRestoreFaceFields(DM dm, PetscInt fStart, PetscInt fEnd, Vec locX, Vec locX_t, Vec faceGeometry, Vec cellGeometry, Vec locGrad, PetscInt *Nface, PetscScalar **uL, PetscScalar **uR)
4177: {
4178:   PetscFunctionBegin;
4179:   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uL));
4180:   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uR));
4181:   PetscFunctionReturn(PETSC_SUCCESS);
4182: }

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

4187:   Input Parameters:
4188: + dm           - The `DM`
4189: . fStart       - The first face to include
4190: . fEnd         - The first face to exclude
4191: . faceGeometry - A local vector with face geometry
4192: - cellGeometry - A local vector with cell geometry

4194:   Output Parameters:
4195: + Nface - The number of faces with field values
4196: . fgeom - The extract the face centroid and normal
4197: - vol   - The cell volume

4199:   Level: developer

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

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

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

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

4257:   Input Parameters:
4258: + dm           - The `DM`
4259: . fStart       - The first face to include
4260: . fEnd         - The first face to exclude
4261: . faceGeometry - A local vector with face geometry
4262: - cellGeometry - A local vector with cell geometry

4264:   Output Parameters:
4265: + Nface - The number of faces with field values
4266: . fgeom - The extract the face centroid and normal
4267: - vol   - The cell volume

4269:   Level: developer

4271: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
4272: @*/
4273: PetscErrorCode DMPlexRestoreFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom **fgeom, PetscReal **vol)
4274: {
4275:   PetscFunctionBegin;
4276:   PetscCall(PetscFree(*fgeom));
4277:   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_REAL, vol));
4278:   PetscFunctionReturn(PETSC_SUCCESS);
4279: }

4281: PetscErrorCode DMSNESGetFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
4282: {
4283:   char           composeStr[33] = {0};
4284:   PetscObjectId  id;
4285:   PetscContainer container;

4287:   PetscFunctionBegin;
4288:   PetscCall(PetscObjectGetId((PetscObject)quad, &id));
4289:   PetscCall(PetscSNPrintf(composeStr, 32, "DMSNESGetFEGeom_%" PetscInt64_FMT "\n", id));
4290:   PetscCall(PetscObjectQuery((PetscObject)pointIS, composeStr, (PetscObject *)&container));
4291:   if (container) {
4292:     PetscCall(PetscContainerGetPointer(container, (void **)geom));
4293:   } else {
4294:     PetscCall(DMFieldCreateFEGeom(coordField, pointIS, quad, faceData, geom));
4295:     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &container));
4296:     PetscCall(PetscContainerSetPointer(container, (void *)*geom));
4297:     PetscCall(PetscContainerSetCtxDestroy(container, PetscContainerCtxDestroy_PetscFEGeom));
4298:     PetscCall(PetscObjectCompose((PetscObject)pointIS, composeStr, (PetscObject)container));
4299:     PetscCall(PetscContainerDestroy(&container));
4300:   }
4301:   PetscFunctionReturn(PETSC_SUCCESS);
4302: }

4304: PetscErrorCode DMSNESRestoreFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
4305: {
4306:   PetscFunctionBegin;
4307:   *geom = NULL;
4308:   PetscFunctionReturn(PETSC_SUCCESS);
4309: }

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

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

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

4373:         PetscCall(PetscDSGetImplicit(prob, f, &fimp));
4374:         if (isImplicit != fimp) continue;
4375:         PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4376:         PetscCall(PetscObjectGetClassId(obj, &id));
4377:         if (id == PETSCFE_CLASSID) {
4378:           PetscFE fe = (PetscFE)obj;

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

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

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

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

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

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

4469:           PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
4470:           if (ghostVal > 0) continue;
4471:         }
4472:         PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind * totDim], ADD_ALL_VALUES));
4473:       }
4474:     }
4475:     /* Handle time derivative */
4476:     if (locX_t) {
4477:       PetscScalar *x_t, *fa;

4479:       PetscCall(VecGetArray(locF, &fa));
4480:       PetscCall(VecGetArray(locX_t, &x_t));
4481:       for (f = 0; f < Nf; ++f) {
4482:         PetscFV      fv;
4483:         PetscObject  obj;
4484:         PetscClassId id;
4485:         PetscInt     pdim, d;

4487:         PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4488:         PetscCall(PetscObjectGetClassId(obj, &id));
4489:         if (id != PETSCFV_CLASSID) continue;
4490:         fv = (PetscFV)obj;
4491:         PetscCall(PetscFVGetNumComponents(fv, &pdim));
4492:         for (c = cS; c < cE; ++c) {
4493:           const PetscInt cell = cells ? cells[c] : c;
4494:           PetscScalar   *u_t, *r;

4496:           if (ghostLabel) {
4497:             PetscInt ghostVal;

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

4534: /*
4535:   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

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

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

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

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

4646:     /* Extract values */
4647:     for (c = 0; c < Ncell; ++c) {
4648:       const PetscInt cell = cells ? cells[c + offCell] : c + offCell;
4649:       PetscScalar   *x = NULL, *x_t = NULL;
4650:       PetscInt       i;

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

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

4728: /* FEM Assembly Function */

4730: static PetscErrorCode DMConvertPlex_Internal(DM dm, DM *plex, PetscBool copy)
4731: {
4732:   PetscBool isPlex;

4734:   PetscFunctionBegin;
4735:   PetscCall(PetscObjectTypeCompare((PetscObject)dm, DMPLEX, &isPlex));
4736:   if (isPlex) {
4737:     *plex = dm;
4738:     PetscCall(PetscObjectReference((PetscObject)dm));
4739:   } else {
4740:     PetscCall(PetscObjectQuery((PetscObject)dm, "dm_plex", (PetscObject *)plex));
4741:     if (!*plex) {
4742:       PetscCall(DMConvert(dm, DMPLEX, plex));
4743:       PetscCall(PetscObjectCompose((PetscObject)dm, "dm_plex", (PetscObject)*plex));
4744:     } else {
4745:       PetscCall(PetscObjectReference((PetscObject)*plex));
4746:     }
4747:     if (copy) PetscCall(DMCopyAuxiliaryVec(dm, *plex));
4748:   }
4749:   PetscFunctionReturn(PETSC_SUCCESS);
4750: }

4752: /*@
4753:   DMPlexGetGeometryFVM - Return precomputed geometric data

4755:   Collective

4757:   Input Parameter:
4758: . dm - The `DM`

4760:   Output Parameters:
4761: + facegeom  - The values precomputed from face geometry
4762: . cellgeom  - The values precomputed from cell geometry
4763: - minRadius - The minimum radius over the mesh of an inscribed sphere in a cell

4765:   Level: developer

4767: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMTSSetRHSFunctionLocal()`
4768: @*/
4769: PetscErrorCode DMPlexGetGeometryFVM(DM dm, Vec *facegeom, Vec *cellgeom, PetscReal *minRadius)
4770: {
4771:   DM plex;

4773:   PetscFunctionBegin;
4775:   PetscCall(DMConvertPlex_Internal(dm, &plex, PETSC_TRUE));
4776:   PetscCall(DMPlexGetDataFVM(plex, NULL, cellgeom, facegeom, NULL));
4777:   if (minRadius) PetscCall(DMPlexGetMinRadius(plex, minRadius));
4778:   PetscCall(DMDestroy(&plex));
4779:   PetscFunctionReturn(PETSC_SUCCESS);
4780: }

4782: /*@
4783:   DMPlexGetGradientDM - Return gradient data layout

4785:   Collective

4787:   Input Parameters:
4788: + dm - The `DM`
4789: - fv - The `PetscFV`

4791:   Output Parameter:
4792: . dmGrad - The layout for gradient values

4794:   Level: developer

4796: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetGeometryFVM()`
4797: @*/
4798: PetscErrorCode DMPlexGetGradientDM(DM dm, PetscFV fv, DM *dmGrad)
4799: {
4800:   DM        plex;
4801:   PetscBool computeGradients;

4803:   PetscFunctionBegin;
4806:   PetscAssertPointer(dmGrad, 3);
4807:   PetscCall(PetscFVGetComputeGradients(fv, &computeGradients));
4808:   if (!computeGradients) {
4809:     *dmGrad = NULL;
4810:     PetscFunctionReturn(PETSC_SUCCESS);
4811:   }
4812:   PetscCall(DMConvertPlex_Internal(dm, &plex, PETSC_TRUE));
4813:   PetscCall(DMPlexGetDataFVM(plex, fv, NULL, NULL, dmGrad));
4814:   PetscCall(DMDestroy(&plex));
4815:   PetscFunctionReturn(PETSC_SUCCESS);
4816: }

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

4830:   PetscFunctionBegin;
4831:   PetscCall(DMConvert(dm, DMPLEX, &plex));
4832:   PetscCall(DMGetLocalSection(dm, &section));
4833:   PetscCall(DMGetDS(dm, &prob));
4834:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
4835:   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &locA));
4836:   if (locA) {
4837:     DM dmAux;

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

4854:     PetscCall(DMLabelGetStratumIS(key.label, key.value, &pointIS));
4855:     if (!pointIS) goto end; /* No points with that id on this process */
4856:     {
4857:       IS isectIS;

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

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

4883:       PetscCall(DMPlexGetSupport(dm, point, &support));
4884:       PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
4885:       for (i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
4886:       PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
4887:       if (locX_t) {
4888:         PetscCall(DMPlexVecGetClosure(plex, section, locX_t, support[0], NULL, &x));
4889:         for (i = 0; i < totDim; ++i) u_t[face * totDim + i] = x[i];
4890:         PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, support[0], NULL, &x));
4891:       }
4892:       if (locA) {
4893:         PetscInt subp;

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

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

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

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

4965: PetscErrorCode DMPlexComputeBdResidualSingle(DM dm, PetscReal t, PetscWeakForm wf, PetscFormKey key, Vec locX, Vec locX_t, Vec locF)
4966: {
4967:   DMField  coordField;
4968:   DMLabel  depthLabel;
4969:   IS       facetIS;
4970:   PetscInt dim;

4972:   PetscFunctionBegin;
4973:   PetscCall(DMGetDimension(dm, &dim));
4974:   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
4975:   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
4976:   PetscCall(DMGetCoordinateField(dm, &coordField));
4977:   PetscCall(DMPlexComputeBdResidual_Single_Internal(dm, t, wf, key, locX, locX_t, locF, coordField, facetIS));
4978:   PetscCall(ISDestroy(&facetIS));
4979:   PetscFunctionReturn(PETSC_SUCCESS);
4980: }

4982: static PetscErrorCode DMPlexComputeBdResidual_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
4983: {
4984:   PetscDS  prob;
4985:   PetscInt numBd, bd;
4986:   DMField  coordField = NULL;
4987:   IS       facetIS    = NULL;
4988:   DMLabel  depthLabel;
4989:   PetscInt dim;

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

5007:     PetscCall(PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &field, NULL, NULL, NULL, NULL, NULL));
5008:     if (type & DM_BC_ESSENTIAL) continue;
5009:     PetscCall(PetscDSGetDiscretization(prob, field, &obj));
5010:     PetscCall(PetscObjectGetClassId(obj, &id));
5011:     if (id != PETSCFE_CLASSID) continue;
5012:     if (!facetIS) {
5013:       DMLabel  depthLabel;
5014:       PetscInt dim;

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

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

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

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

5110:         PetscCall(PetscDSGetImplicit(ds, f, &fimp));
5111:         if (isImplicit != fimp) continue;
5112:         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
5113:         PetscCall(PetscObjectGetClassId(obj, &id));
5114:         if (id == PETSCFE_CLASSID) {
5115:           PetscFE fe = (PetscFE)obj;

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

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

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

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

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

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

5216:           PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
5217:           if (ghostVal > 0) continue;
5218:         }
5219:         PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind * totDim], ADD_ALL_VALUES));
5220:       }
5221:     }
5222:     if (useFVM) {
5223:       PetscScalar *fa;
5224:       PetscInt     iface;

5226:       PetscCall(VecGetArray(locF, &fa));
5227:       for (f = 0; f < Nf; ++f) {
5228:         PetscFV      fv;
5229:         PetscObject  obj;
5230:         PetscClassId id;
5231:         PetscInt     cdim, foff, pdim;

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

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

5275:       PetscCall(VecGetArray(locF, &fa));
5276:       PetscCall(VecGetArray(locX_t, &x_t));
5277:       for (f = 0; f < Nf; ++f) {
5278:         PetscFV      fv;
5279:         PetscObject  obj;
5280:         PetscClassId id;
5281:         PetscInt     pdim, d;

5283:         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
5284:         PetscCall(PetscObjectGetClassId(obj, &id));
5285:         if (id != PETSCFV_CLASSID) continue;
5286:         fv = (PetscFV)obj;
5287:         PetscCall(PetscFVGetNumComponents(fv, &pdim));
5288:         for (c = cS; c < cE; ++c) {
5289:           const PetscInt cell = cells ? cells[c] : c;
5290:           PetscScalar   *u_t, *r;

5292:           if (ghostLabel) {
5293:             PetscInt ghostVal;

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

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

5324:     if (maxDegree <= 1) {
5325:       PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
5326:       PetscCall(PetscQuadratureDestroy(&affineQuad));
5327:     } else {
5328:       for (f = 0; f < Nf; ++f) {
5329:         PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
5330:         PetscCall(PetscQuadratureDestroy(&quads[f]));
5331:       }
5332:       PetscCall(PetscFree2(quads, geoms));
5333:     }
5334:   }

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

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

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

5391: /*
5392:   1) Allow multiple kernels for BdResidual for hybrid DS

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

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

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

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

5453:     PetscCall(VecGetDM(locA[2], &dmAux[2]));
5454:     PetscCall(DMGetCellDS(dmAux[2], cellStart, &dsAux[2], NULL));
5455:     PetscCall(PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]));
5456:     {
5457:       const PetscInt *cone;
5458:       PetscInt        c;

5460:       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
5461:       for (c = 0; c < 2; ++c) {
5462:         const PetscInt *support;
5463:         PetscInt        ssize, s;

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

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

5499:       locS[1] = locS[0] = locS[2];
5500:       dmScale[1] = dmScale[0] = dmScale[2];
5501:       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
5502:       for (c = 0; c < 2; ++c) {
5503:         const PetscInt *support;
5504:         PetscInt        ssize, s;

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

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

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

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

5612:       /* Scale element values */
5613:       if (locS[0]) {
5614:         PetscInt  Nb, off = cind * totDim, soff = cind * totDimScale[0];
5615:         PetscBool cohesive;

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

5628:             for (i = 0; i < N; ++i) elemVecCoh[off + i] += elemVecNeg[off + i] + elemVecPos[off + i];
5629:             off += N;
5630:           }
5631:         }
5632:       } else {
5633:         for (i = cind * totDim; i < (cind + 1) * totDim; ++i) elemVecCoh[i] += elemVecNeg[i] + elemVecPos[i];
5634:       }
5635:       if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, name, totDim, &elemVecCoh[cind * totDim]));
5636:       if (ghostLabel) {
5637:         PetscInt ghostVal;

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

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

5684: 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)
5685: {
5686:   DM_Plex        *mesh = (DM_Plex *)dm->data;
5687:   DM              plex = NULL, plexA = NULL, tdm;
5688:   DMEnclosureType encAux;
5689:   PetscDS         ds, dsAux           = NULL;
5690:   PetscSection    section, sectionAux = NULL;
5691:   PetscSection    globalSection;
5692:   Vec             locA = NULL, tv;
5693:   PetscScalar    *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL, *elemMatP = NULL;
5694:   PetscInt        v;
5695:   PetscInt        Nf, totDim, totDimAux = 0;
5696:   PetscBool       hasJac = PETSC_FALSE, hasPrec = PETSC_FALSE, transform;

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

5714:     PetscCall(VecGetDM(locA, &dmAux));
5715:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
5716:     PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
5717:     PetscCall(DMGetDS(plexA, &dsAux));
5718:     PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
5719:     PetscCall(DMGetLocalSection(plexA, &sectionAux));
5720:   }

5722:   PetscCall(DMGetGlobalSection(dm, &globalSection));
5723:   for (v = 0; v < numValues; ++v) {
5724:     PetscFEGeom    *fgeom;
5725:     PetscInt        maxDegree;
5726:     PetscQuadrature qGeom = NULL;
5727:     IS              pointIS;
5728:     const PetscInt *points;
5729:     PetscFormKey    key;
5730:     PetscInt        numFaces, face, Nq;

5732:     key.label = label;
5733:     key.value = values[v];
5734:     key.part  = 0;
5735:     PetscCall(DMLabelGetStratumIS(label, values[v], &pointIS));
5736:     if (!pointIS) continue; /* No points with that id on this process */
5737:     {
5738:       IS isectIS;

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

5753:       PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&fe));
5754:       PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
5755:       PetscCall(PetscObjectReference((PetscObject)qGeom));
5756:     }
5757:     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
5758:     PetscCall(DMSNESGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
5759:     for (face = 0; face < numFaces; ++face) {
5760:       const PetscInt point = points[face], *support;
5761:       PetscScalar   *x     = NULL;
5762:       PetscInt       i;

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

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

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

5850: 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)
5851: {
5852:   DMField  coordField;
5853:   DMLabel  depthLabel;
5854:   IS       facetIS;
5855:   PetscInt dim;

5857:   PetscFunctionBegin;
5858:   PetscCall(DMGetDimension(dm, &dim));
5859:   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5860:   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
5861:   PetscCall(DMGetCoordinateField(dm, &coordField));
5862:   PetscCall(DMPlexComputeBdJacobian_Single_Internal(dm, t, wf, label, numValues, values, field, locX, locX_t, X_tShift, Jac, JacP, coordField, facetIS));
5863:   PetscCall(ISDestroy(&facetIS));
5864:   PetscFunctionReturn(PETSC_SUCCESS);
5865: }

5867: static PetscErrorCode DMPlexComputeBdJacobian_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, PetscReal X_tShift, Mat Jac, Mat JacP, void *user)
5868: {
5869:   PetscDS  prob;
5870:   PetscInt dim, numBd, bd;
5871:   DMLabel  depthLabel;
5872:   DMField  coordField = NULL;
5873:   IS       facetIS;

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

5891:     PetscCall(PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &fieldI, NULL, NULL, NULL, NULL, NULL));
5892:     if (type & DM_BC_ESSENTIAL) continue;
5893:     PetscCall(PetscDSGetDiscretization(prob, fieldI, &obj));
5894:     PetscCall(PetscObjectGetClassId(obj, &id));
5895:     if (id != PETSCFE_CLASSID) continue;
5896:     PetscCall(DMPlexComputeBdJacobian_Single_Internal(dm, t, wf, label, numValues, values, fieldI, locX, locX_t, X_tShift, Jac, JacP, coordField, facetIS));
5897:   }
5898:   PetscCall(ISDestroy(&facetIS));
5899:   PetscFunctionReturn(PETSC_SUCCESS);
5900: }

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

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

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

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

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

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

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

6105:   PetscCallMPI(MPIU_Allreduce(&assOp, &gassOp, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
6106:   if (hasJac && hasPrec) {
6107:     PetscCall(MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY));
6108:     PetscCall(MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY));
6109:   }
6110: }
6111:   PetscCall(MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY));
6112:   PetscCall(MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY));
6113:   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
6114:   PetscFunctionReturn(PETSC_SUCCESS);
6115: }

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

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

6176:     PetscCall(VecGetDM(locA[2], &dmAux[2]));
6177:     PetscCall(DMConvert(dmAux[2], DMPLEX, &plexA));
6178:     PetscCall(DMGetLocalSection(dmAux[2], &sectionAux[2]));
6179:     PetscCall(DMGetCellDS(dmAux[2], cellStart, &dsAux[2], NULL));
6180:     PetscCall(PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]));
6181:     {
6182:       const PetscInt *cone;
6183:       PetscInt        c;

6185:       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
6186:       for (c = 0; c < 2; ++c) {
6187:         const PetscInt *support;
6188:         PetscInt        ssize, s;

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

6211:     PetscCall(VecGetDM(locS[2], &dmScale[2]));
6212:     PetscCall(DMGetCellDS(dmScale[2], cells ? cells[cStart] : cStart, &dsScale[2], NULL));
6213:     PetscCall(PetscDSGetTotalDimension(dsScale[2], &totDimScale[2]));
6214:     // BRAD: This is not set correctly
6215:     key[2].field = 2;
6216:     PetscCall(PetscDSGetFieldSize(ds, key[2].field, &Nb));
6217:     PetscCall(PetscDSGetFieldSize(dsScale[2], 0, &Nbs));
6218:     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);
6219:     {
6220:       const PetscInt *cone;
6221:       PetscInt        c;

6223:       locS[1] = locS[0] = locS[2];
6224:       dmScale[1] = dmScale[0] = dmScale[2];
6225:       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
6226:       for (c = 0; c < 2; ++c) {
6227:         const PetscInt *support;
6228:         PetscInt        ssize, s;

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

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

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

6305:     for (fieldI = 0; fieldI < Nf; ++fieldI) {
6306:       PetscFE         feI;
6307:       PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[fieldI];
6308:       PetscFEGeom    *chunkGeom = NULL, *remGeom = NULL;
6309:       PetscQuadrature quad = affineQuad ? affineQuad : quads[fieldI];
6310:       PetscInt        numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb;
6311:       PetscBool       isCohesiveField;

6313:       PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&feI));
6314:       if (!feI) continue;
6315:       PetscCall(PetscFEGetTileSizes(feI, NULL, &numBlocks, NULL, &numBatches));
6316:       PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
6317:       PetscCall(PetscFEGetDimension(feI, &Nb));
6318:       blockSize = Nb;
6319:       batchSize = numBlocks * blockSize;
6320:       PetscCall(PetscFESetTileSizes(feI, blockSize, numBlocks, batchSize, numBatches));
6321:       numChunks = numCells / (numBatches * batchSize);
6322:       Ne        = numChunks * numBatches * batchSize;
6323:       Nr        = numCells % (numBatches * batchSize);
6324:       offset    = numCells - Nr;
6325:       PetscCall(PetscFEGeomGetChunk(geom, 0, offset * 2, &chunkGeom));
6326:       PetscCall(PetscFEGeomGetChunk(geom, offset * 2, numCells * 2, &remGeom));
6327:       PetscCall(PetscDSGetCohesive(ds, fieldI, &isCohesiveField));
6328:       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
6329:         PetscFE feJ;

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

6366:       /* Scale element values */
6367:       if (locS[0]) {
6368:         PetscInt  Nb, soff = cind * totDimScale[0], off = 0;
6369:         PetscBool cohesive;

6371:         for (fieldI = 0; fieldI < Nf; ++fieldI) {
6372:           PetscCall(PetscDSGetFieldSize(ds, fieldI, &Nb));
6373:           PetscCall(PetscDSGetCohesive(ds, fieldI, &cohesive));

6375:           if (fieldI == key[2].field) {
6376:             PetscCheck(cohesive, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Scaling should not happen for face fields");
6377:             for (i = 0; i < Nb; ++i) {
6378:               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];
6379:               if (hasBdPrec)
6380:                 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];
6381:             }
6382:             off += Nb;
6383:           } else {
6384:             const PetscInt N = cohesive ? Nb : Nb * 2;

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

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

6444:   Input Parameters:
6445: + dm     - The mesh
6446: . key    - The PetscWeakFormKey indicating where integration should happen
6447: . cellIS - The cells to integrate over
6448: . t      - The time
6449: . X_tShift - The multiplier for the Jacobian with respect to X_t
6450: . X      - Local solution vector
6451: . X_t    - Time-derivative of the local solution vector
6452: . Y      - Local input vector
6453: - user   - the user context

6455:   Output Parameter:
6456: . Z - Local output vector

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

6479:   PetscFunctionBegin;
6480:   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
6481:   PetscCall(DMConvert(dm, DMPLEX, &plex));
6482:   PetscCall(ISGetLocalSize(cellIS, &numCells));
6483:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
6484:   PetscCall(DMGetLocalSection(dm, &section));
6485:   PetscCall(DMGetGlobalSection(dm, &globalSection));
6486:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL));
6487:   PetscCall(PetscDSGetNumFields(prob, &Nf));
6488:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
6489:   PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
6490:   hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
6491:   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &A));
6492:   if (A) {
6493:     PetscCall(VecGetDM(A, &dmAux));
6494:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
6495:     PetscCall(DMConvert(dmAux, DMPLEX, &plexAux));
6496:     PetscCall(DMGetLocalSection(plexAux, &sectionAux));
6497:     PetscCall(DMGetDS(dmAux, &probAux));
6498:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
6499:   }
6500:   PetscCall(VecSet(Z, 0.0));
6501:   PetscCall(PetscMalloc6(numCells * totDim, &u, (X_t ? (size_t)numCells * totDim : 0), &u_t, numCells * totDim * totDim, &elemMat, (hasDyn ? (size_t)numCells * totDim * totDim : 0), &elemMatD, numCells * totDim, &y, totDim, &z));
6502:   if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
6503:   PetscCall(DMGetCoordinateField(dm, &coordField));
6504:   for (c = cStart; c < cEnd; ++c) {
6505:     const PetscInt cell = cells ? cells[c] : c;
6506:     const PetscInt cind = c - cStart;
6507:     PetscScalar   *x = NULL, *x_t = NULL;
6508:     PetscInt       i;

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

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

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

6609: 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[])
6610: {
6611:   f0[0] = u[0];
6612: }

6614: 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[])
6615: {
6616:   f0[0] = x[(int)PetscRealPart(constants[0])] * u[0];
6617: }

6619: 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[])
6620: {
6621:   PetscInt d;

6623:   f0[0] = 0.0;
6624:   for (d = 0; d < dim; ++d) f0[0] += PetscSqr(x[d]) * u[0];
6625: }

6627: /*@
6628:   DMPlexComputeMoments - Compute the first three moments for a field

6630:   Noncollective

6632:   Input Parameters:
6633: + dm - the `DMPLEX`
6634: - u  - the field

6636:   Output Parameter:
6637: . moments - the field moments

6639:   Level: intermediate

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

6644: .seealso: `DM`, `DMPLEX`, `DMSwarmComputeMoments()`
6645: @*/
6646: PetscErrorCode DMPlexComputeMoments(DM dm, Vec u, PetscReal moments[])
6647: {
6648:   PetscDS            ds;
6649:   PetscScalar        mom, constants[1];
6650:   const PetscScalar *oldConstants;
6651:   PetscInt           cdim, Nf, field = 0, Ncon;
6652:   MPI_Comm           comm;
6653:   void              *user;

6655:   PetscFunctionBeginUser;
6656:   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
6657:   PetscCall(DMGetCoordinateDim(dm, &cdim));
6658:   PetscCall(DMGetApplicationContext(dm, &user));
6659:   PetscCall(DMGetDS(dm, &ds));
6660:   PetscCall(PetscDSGetNumFields(ds, &Nf));
6661:   PetscCall(PetscDSGetConstants(ds, &Ncon, &oldConstants));
6662:   PetscCheck(Nf == 1, comm, PETSC_ERR_ARG_WRONG, "We currently only support 1 field, not %" PetscInt_FMT, Nf);
6663:   PetscCall(PetscDSSetObjective(ds, field, &f0_1));
6664:   PetscCall(DMPlexComputeIntegralFEM(dm, u, &mom, user));
6665:   moments[0] = PetscRealPart(mom);
6666:   for (PetscInt c = 0; c < cdim; ++c) {
6667:     constants[0] = c;
6668:     PetscCall(PetscDSSetConstants(ds, 1, constants));
6669:     PetscCall(PetscDSSetObjective(ds, field, &f0_x));
6670:     PetscCall(DMPlexComputeIntegralFEM(dm, u, &mom, user));
6671:     moments[c + 1] = PetscRealPart(mom);
6672:   }
6673:   PetscCall(PetscDSSetObjective(ds, field, &f0_x2));
6674:   PetscCall(DMPlexComputeIntegralFEM(dm, u, &mom, user));
6675:   moments[cdim + 1] = PetscRealPart(mom);
6676:   PetscCall(PetscDSSetConstants(ds, Ncon, (PetscScalar *)oldConstants));
6677:   PetscFunctionReturn(PETSC_SUCCESS);
6678: }