Actual source code: plexfem.c

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

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

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

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

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

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

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

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

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

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

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

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

 92:   Not Collective

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

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

101:   Level: advanced

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

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

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

119:   Not Collective

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

126:   Level: advanced

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

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

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

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

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

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

160:   Not collective

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

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

168:   Level: intermediate

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

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

185:   Not collective

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

191:   Level: intermediate

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

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

207:   Not collective

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

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

215:   Level: intermediate

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

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

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

233:   Not collective

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

239:   Level: intermediate

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

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

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

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

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

282:   Collective

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

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

291:   Level: advanced

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

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

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

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

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

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

370:   Collective

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

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

382:   Level: advanced

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

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

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

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

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

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

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

448:   Level: advanced

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

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

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

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

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

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

480:   Level: intermediate

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

807:   Level: developer

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

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

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

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

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

833:   Level: developer

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

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

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

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

859:   Level: developer

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

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

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

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

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

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

911:   Level: developer

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

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

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

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

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

950:   Level: developer

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

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

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

973:   Collective

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

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

991:   Level: developer

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

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

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

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

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

1032:   Level: developer

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1301:   Not Collective

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

1311:   Output Parameter:
1312: . locX - Solution updated with boundary values

1314:   Level: intermediate

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

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

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

1341:   Output Parameter:
1342: . locX_t - Solution updated with boundary values

1344:   Level: developer

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

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

1363:   Not Collective

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

1370:   Output Parameter:
1371: . locB - Bounds vector updated with new bounds

1373:   Level: intermediate

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

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

1390:   Collective

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

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

1401:   Level: developer

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

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

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

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

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

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

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

1461:   Collective

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

1470:   Output Parameter:
1471: . diff - The diff ||u - u_h||_2

1473:   Level: developer

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

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

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

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

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

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

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

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

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

1602: 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)
1603: {
1604:   const PetscInt   debug = ((DM_Plex *)dm->data)->printL2;
1605:   DM               tdm;
1606:   PetscSection     section;
1607:   PetscQuadrature  quad;
1608:   Vec              localX, tv;
1609:   PetscScalar     *funcVal, *interpolant;
1610:   const PetscReal *quadWeights;
1611:   PetscFEGeom      fegeom;
1612:   PetscReal       *coords, *gcoords;
1613:   PetscInt         dim, coordDim, qNc = 0, Nq = 0, numFields, numComponents = 0, cStart, cEnd, c, field, fieldOffset;
1614:   PetscBool        transform;

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

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

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

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

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

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

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

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

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

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

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

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

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

1871: /*@C
1872:   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.

1874:   Collective

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

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

1886:   Level: developer

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

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

1916:     PetscCall(DMGetField(dm, field, NULL, &obj));
1917:     PetscCall(PetscObjectGetClassId(obj, &id));
1918:     if (id == PETSCFE_CLASSID) {
1919:       PetscFE fe = (PetscFE)obj;

1921:       PetscCall(PetscFEGetQuadrature(fe, &quad));
1922:       PetscCall(PetscFEGetNumComponents(fe, &Nc));
1923:     } else if (id == PETSCFV_CLASSID) {
1924:       PetscFV fv = (PetscFV)obj;

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

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

1943:     for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1944:       PetscObject  obj;
1945:       PetscClassId id;
1946:       void *const  ctx = ctxs ? ctxs[field] : NULL;
1947:       PetscInt     Nb, Nc, q, fc;

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

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

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

1999:   Collective

2001:   Input Parameters:
2002: + lu  - The local `Vec` containing the primal solution
2003: . f   - The field number for the potential
2004: . lmu - The local `Vec` containing the mixed solution
2005: - mf  - The field number for the flux

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

2010:   Level: advanced

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

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

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

2031:   PetscFunctionBegin;
2032:   PetscCall(VecGetDM(lu, &dm));
2033:   PetscCall(VecGetDM(lmu, &mdm));
2034:   PetscCall(VecGetDM(eFlux, &edm));
2035:   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
2036:   PetscCall(VecSet(eFlux, 0.0));

2038:   // Check if the both problems are on the same mesh
2039:   PetscCall(DMGetCoordinateDim(dm, &cdim));
2040:   PetscCall(DMGetCoordinateDim(mdm, &mcdim));
2041:   PetscCheck(cdim == mcdim, comm, PETSC_ERR_ARG_SIZ, "primal coordinate Dim %" PetscInt_FMT " != %" PetscInt_FMT " mixed coordinate Dim", cdim, mcdim);
2042:   fegeom.dimEmbed = cdim;

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

2052:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
2053:   PetscCall(VecGetArrayWrite(eFlux, &earray));
2054:   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));
2055:   for (PetscInt c = cStart; c < cEnd; ++c) {
2056:     PetscScalar *x            = NULL;
2057:     PetscScalar *mx           = NULL;
2058:     PetscScalar *eval         = NULL;
2059:     PetscReal    fluxElemDiff = 0.0;

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

2065:     for (PetscInt q = 0; q < Nq; ++q) {
2066:       PetscFEGeom qgeom;

2068:       qgeom.dimEmbed = fegeom.dimEmbed;
2069:       qgeom.J        = &fegeom.J[q * cdim * cdim];
2070:       qgeom.invJ     = &fegeom.invJ[q * cdim * cdim];
2071:       qgeom.detJ     = &fegeom.detJ[q];

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

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

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

2092:   PetscCall(VecAssemblyBegin(eFlux));
2093:   PetscCall(VecAssemblyEnd(eFlux));
2094:   PetscCall(VecSqrtAbs(eFlux));
2095:   PetscFunctionReturn(PETSC_SUCCESS);
2096: }

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

2101:   Collective

2103:   Input Parameters:
2104: + u  - The global `Vec` containing the primal solution
2105: . f  - The field number for the potential
2106: . mu - The global `Vec` containing the mixed solution
2107: - mf - The field number for the flux

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

2112:   Level: advanced

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

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

2119: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeL2FluxDiffVecLocal()`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
2120: @*/
2121: PetscErrorCode DMPlexComputeL2FluxDiffVec(Vec u, PetscInt f, Vec mu, PetscInt mf, Vec eFlux)
2122: {
2123:   DM  dm, mdm;
2124:   Vec lu, lmu;

2126:   PetscFunctionBegin;
2127:   PetscCall(VecGetDM(u, &dm));
2128:   PetscCall(DMGetLocalVector(dm, &lu));
2129:   PetscCall(DMGlobalToLocal(dm, u, INSERT_VALUES, lu));
2130:   PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, lu, 0.0, NULL, NULL, NULL));

2132:   PetscCall(VecGetDM(mu, &mdm));
2133:   PetscCall(DMGetLocalVector(mdm, &lmu));
2134:   PetscCall(DMGlobalToLocal(mdm, mu, INSERT_VALUES, lmu));
2135:   PetscCall(DMPlexInsertBoundaryValues(mdm, PETSC_TRUE, lmu, 0.0, NULL, NULL, NULL));

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

2139:   PetscCall(DMRestoreLocalVector(dm, &lu));
2140:   PetscCall(DMRestoreLocalVector(mdm, &lmu));
2141:   PetscFunctionReturn(PETSC_SUCCESS);
2142: }

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

2147:   Collective

2149:   Input Parameters:
2150: + dm   - The `DM`
2151: - locX - The coefficient vector u_h

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

2156:   Level: developer

2158:   Note:
2159:   $ 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

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

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

2188:     PetscCall(DMGetField(dm, f, NULL, &obj));
2189:     PetscCall(PetscObjectGetClassId(obj, &id));
2190:     if (id == PETSCFE_CLASSID) {
2191:       PetscFE fe = (PetscFE)obj;

2193:       PetscCall(PetscFEGetQuadrature(fe, &quad));
2194:       PetscCall(PetscFEGetNumComponents(fe, &fNc));
2195:     } else if (id == PETSCFV_CLASSID) {
2196:       PetscFV fv = (PetscFV)obj;

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

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

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

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

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

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

2279:   Collective

2281:   Input Parameters:
2282: + dm   - The `DM`
2283: - locX - The coefficient vector u_h

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

2288:   Level: developer

2290:   Note:
2291:   $\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

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

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

2321:     PetscCall(DMGetField(dm, field, NULL, &obj));
2322:     PetscCall(PetscObjectGetClassId(obj, &id));
2323:     if (id == PETSCFE_CLASSID) {
2324:       PetscFE fe = (PetscFE)obj;

2326:       PetscCall(PetscFEGetQuadrature(fe, &quad));
2327:       PetscCall(PetscFEGetNumComponents(fe, &Nc));
2328:     } else if (id == PETSCFV_CLASSID) {
2329:       PetscFV fv = (PetscFV)obj;

2331:       PetscCall(PetscFVGetQuadrature(fv, &quad));
2332:       PetscCall(PetscFVGetNumComponents(fv, &Nc));
2333:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
2334:     numComponents += Nc;
2335:   }
2336:   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
2337:   PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
2338:   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));
2339:   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
2340:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
2341:   for (v = vStart; v < vEnd; ++v) {
2342:     PetscScalar volsum = 0.0;
2343:     PetscInt   *star   = NULL;
2344:     PetscInt    starSize, st, d, fc;

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

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

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

2375:           qgeom.dimEmbed = fegeom.dimEmbed;
2376:           qgeom.J        = &fegeom.J[q * coordDim * coordDim];
2377:           qgeom.invJ     = &fegeom.invJ[q * coordDim * coordDim];
2378:           qgeom.detJ     = &fegeom.detJ[q];
2379:           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);
2380:           PetscCheck(id == PETSCFE_CLASSID, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
2381:           PetscCall(PetscFEInterpolateGradient_Static((PetscFE)obj, 1, &x[fieldOffset], &qgeom, q, interpolant));
2382:           for (fc = 0; fc < Nc; ++fc) {
2383:             const PetscReal wt = quadWeights[q * qNc + qc];

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

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

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

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

2489:     for (f = 0; f < Nf; ++f) {
2490:       PetscObject  obj;
2491:       PetscClassId id;

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

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

2538:     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
2539:     PetscCall(PetscObjectGetClassId(obj, &id));
2540:     if (id == PETSCFE_CLASSID) {
2541:       PetscFE         fe = (PetscFE)obj;
2542:       PetscQuadrature q;
2543:       PetscFEGeom    *chunkGeom = NULL;
2544:       PetscInt        Nq, Nb;

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

2568:       PetscCall(PetscDSGetObjective(prob, f, &obj_func));
2569:       PetscCall(PetscDSGetFieldOffset(prob, f, &foff));
2570:       if (obj_func) {
2571:         for (c = 0; c < numCells; ++c) {
2572:           PetscScalar *u_x;
2573:           PetscScalar  lint = 0.;

2575:           PetscCall(DMPlexPointLocalRead(dmGrad, c, lgrad, &u_x));
2576:           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);
2577:           cintegral[c * Nf + f] += PetscRealPart(lint) * cgeomFVM[c].volume;
2578:         }
2579:       }
2580:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
2581:   }
2582:   /* Cleanup data arrays */
2583:   if (useFVM) {
2584:     PetscCall(VecRestoreArrayRead(locGrad, &lgrad));
2585:     PetscCall(VecRestoreArrayRead(cellGeometryFVM, (const PetscScalar **)&cgeomFVM));
2586:     PetscCall(DMRestoreLocalVector(dmGrad, &locGrad));
2587:     PetscCall(VecDestroy(&faceGeometryFVM));
2588:     PetscCall(VecDestroy(&cellGeometryFVM));
2589:     PetscCall(DMDestroy(&dmGrad));
2590:   }
2591:   if (dmAux) PetscCall(PetscFree(a));
2592:   PetscCall(DMDestroy(&plexA));
2593:   PetscCall(PetscFree(u));
2594:   /* Cleanup */
2595:   if (affineQuad) PetscCall(PetscFEGeomDestroy(&cgeomFEM));
2596:   PetscCall(PetscQuadratureDestroy(&affineQuad));
2597:   PetscCall(ISDestroy(&cellIS));
2598:   PetscFunctionReturn(PETSC_SUCCESS);
2599: }

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

2604:   Input Parameters:
2605: + dm  - The mesh
2606: . X   - Global input vector
2607: - ctx - The application context

2609:   Output Parameter:
2610: . integral - Integral for each field

2612:   Level: developer

2614: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSNESComputeResidualFEM()`
2615: @*/
2616: PetscErrorCode DMPlexComputeIntegralFEM(DM dm, Vec X, PetscScalar *integral, PetscCtx ctx)
2617: {
2618:   PetscInt     printFEM;
2619:   PetscScalar *cintegral;
2620:   PetscInt     Nf, f, cellHeight, cStart, cEnd, cell;
2621:   Vec          locX;

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

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

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

2665:   Input Parameters:
2666: + dm  - The mesh
2667: . X   - Global input vector
2668: - ctx - The application context

2670:   Output Parameter:
2671: . F - Cellwise integrals for each field

2673:   Level: developer

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

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

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

2730: static PetscErrorCode DMPlexComputeBdIntegral_Internal(DM dm, Vec locX, IS pointIS, void (**funcs)(PetscInt, PetscInt, PetscInt, const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], PetscReal, const PetscReal[], const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]), PetscScalar *fintegral, PetscCtx ctx)
2731: {
2732:   DM                 plex = NULL, plexA = NULL;
2733:   DMEnclosureType    encAux;
2734:   PetscDS            prob, probAux       = NULL;
2735:   PetscSection       section, sectionAux = NULL;
2736:   Vec                locA = NULL;
2737:   DMField            coordField;
2738:   PetscInt           Nf, totDim, *uOff, *uOff_x;
2739:   PetscInt           NfAux = 0, totDimAux = 0, *aOff = NULL;
2740:   PetscScalar       *u, *a = NULL;
2741:   const PetscScalar *constants;
2742:   PetscInt           numConstants, f;

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

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

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

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

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

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

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

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

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

2869:   Output Parameter:
2870: . integral - Integral for each field

2872:   Level: developer

2874: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeIntegralFEM()`, `DMPlexComputeBdResidualFEM()`
2875: @*/
2876: PetscErrorCode DMPlexComputeBdIntegral(DM dm, Vec X, DMLabel label, PetscInt numVals, const PetscInt vals[], void (**funcs)(PetscInt, PetscInt, PetscInt, const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], PetscReal, const PetscReal[], const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]), PetscScalar *integral, PetscCtx ctx)
2877: {
2878:   Vec          locX;
2879:   PetscSection section;
2880:   DMLabel      depthLabel;
2881:   IS           facetIS;
2882:   PetscInt     dim, Nf, f, v;

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

2902:     PetscCall(DMGetPointSF(dm, &sf));
2903:     PetscCall(PetscSFGetGraph(sf, NULL, &nleaves, &leaves, NULL));
2904:     if (nleaves > 0 && leaves) {
2905:       IS leafIS, ownedFacetIS;

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

2928:     PetscCall(DMLabelGetStratumIS(label, vals[v], &pointIS));
2929:     if (!pointIS) continue; /* No points with that id on this process */
2930:     {
2931:       IS isectIS;

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

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

2956:   Input Parameters:
2957: + dmc       - The coarse mesh
2958: . dmf       - The fine mesh
2959: . isRefined - Flag indicating regular refinement, rather than the same topology
2960: - ctx       - The application context

2962:   Output Parameter:
2963: . In - The interpolation matrix

2965:   Level: developer

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

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

2996:     PetscCall(DMGetField(dmf, f, NULL, &obj));
2997:     PetscCall(PetscObjectGetClassId(obj, &id));
2998:     if (id == PETSCFE_CLASSID) {
2999:       PetscFE fe = (PetscFE)obj;

3001:       if (isRefined) PetscCall(PetscFERefine(fe, &feRef[f]));
3002:       else {
3003:         PetscCall(PetscObjectReference((PetscObject)fe));
3004:         feRef[f] = fe;
3005:       }
3006:       PetscCall(PetscFEGetDimension(feRef[f], &rNb));
3007:       PetscCall(PetscFEGetNumComponents(fe, &Nc));
3008:     } else if (id == PETSCFV_CLASSID) {
3009:       PetscFV        fv = (PetscFV)obj;
3010:       PetscDualSpace Q;

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

3027:       PetscCall(PetscFEGetDimension(fe, &cNb));
3028:     } else if (id == PETSCFV_CLASSID) {
3029:       PetscFV        fv = (PetscFV)obj;
3030:       PetscDualSpace Q;

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

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

3069:     for (fieldJ = 0, offsetJ = 0; fieldJ < Nf; ++fieldJ) {
3070:       PetscObject  obj;
3071:       PetscClassId id;
3072:       PetscInt     NcJ = 0, cpdim = 0, j, qNc;

3074:       PetscCall(DMGetField(dmc, fieldJ, NULL, &obj));
3075:       PetscCall(PetscObjectGetClassId(obj, &id));
3076:       if (id == PETSCFE_CLASSID) {
3077:         PetscFE         fe = (PetscFE)obj;
3078:         PetscTabulation T  = NULL;

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

3110:         /* Evaluate constant function at points */
3111:         PetscCall(PetscFVGetNumComponents(fv, &NcJ));
3112:         cpdim = 1;
3113:         /* For now, fields only interpolate themselves */
3114:         if (fieldI == fieldJ) {
3115:           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);
3116:           for (i = 0, k = 0; i < fpdim; ++i) {
3117:             PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
3118:             PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, NULL, &qweights));
3119:             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);
3120:             for (p = 0; p < Np; ++p, ++k) {
3121:               for (j = 0; j < cpdim; ++j) {
3122:                 for (c = 0; c < Nc; ++c) elemMat[(offsetI + i) * cTotDim + offsetJ + j] += 1.0 * qweights[p * qNc + c];
3123:               }
3124:             }
3125:           }
3126:         }
3127:       }
3128:       offsetJ += cpdim;
3129:     }
3130:     offsetI += fpdim;
3131:     PetscCall(PetscFree(points));
3132:   }
3133:   if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(0, name, rTotDim, cTotDim, elemMat));
3134:   /* Preallocate matrix */
3135:   {
3136:     Mat          preallocator;
3137:     PetscScalar *vals;
3138:     PetscInt    *cellCIndices, *cellFIndices;
3139:     PetscInt     locRows, locCols, cell;

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

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

3189:   Collective

3191:   Input Parameters:
3192: + dmc - the coarse mesh
3193: . dmf - the fine mesh
3194: - ctx - the application context

3196:   Output Parameter:
3197: . mass - the mass matrix

3199:   Level: developer

3201:   Note:
3202:   This routine is not implemented and currently raises `PETSC_ERR_SUP`.

3204: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeMassMatrixGeneral()`, `DMPlexComputeInterpolatorNested()`
3205: @*/
3206: PetscErrorCode DMPlexComputeMassMatrixNested(DM dmc, DM dmf, Mat mass, PetscCtx ctx)
3207: {
3208:   SETERRQ(PetscObjectComm((PetscObject)dmc), PETSC_ERR_SUP, "Laziness");
3209: }

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

3214:   Input Parameters:
3215: + dmf - The fine mesh
3216: . dmc - The coarse mesh
3217: - ctx - The application context

3219:   Output Parameter:
3220: . In - The interpolation matrix

3222:   Level: developer

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

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

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

3271:       PetscCall(PetscDSGetFieldOffset(prob, field, &off));
3272:       PetscCall(PetscDSGetDiscretization(prob, field, &obj));
3273:       PetscCall(PetscObjectGetClassId(obj, &id));
3274:       if (id == PETSCFE_CLASSID) {
3275:         PetscFE fe = (PetscFE)obj;

3277:         PetscCall(PetscFEGetDualSpace(fe, &Q));
3278:         PetscCall(PetscFEGetNumComponents(fe, &Nc));
3279:         if (s) PetscCall(PetscFECreateTabulation(fe, 1, 1, x, 0, &T));
3280:       } else if (id == PETSCFV_CLASSID) {
3281:         PetscFV fv = (PetscFV)obj;

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

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

3302:           /* Get points from the dual basis functional quadrature */
3303:           PetscCall(PetscDualSpaceGetFunctional(Q, i, &f));
3304:           PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, &qpoints, &qweights));
3305:           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);
3306:           PetscCall(VecCreateSeq(PETSC_COMM_SELF, Np * dim, &pointVec));
3307:           PetscCall(VecSetBlockSize(pointVec, dim));
3308:           PetscCall(VecGetArray(pointVec, &pV));
3309:           for (q = 0; q < Np; ++q) {
3310:             const PetscReal xi0[3] = {-1., -1., -1.};

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

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

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

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

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

3386:   Input Parameters:
3387: + dmf - The fine mesh
3388: . dmc - The coarse mesh
3389: - ctx - The application context

3391:   Output Parameter:
3392: . mass - The mass matrix

3394:   Level: developer

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

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

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

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

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

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

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

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

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

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

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

3570:         if (id == PETSCFE_CLASSID) {
3571:           PetscFE fe = (PetscFE)obj;

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

3618: /*@
3619:   DMPlexComputeInjectorFEM - Compute a mapping from coarse unknowns to fine unknowns

3621:   Input Parameters:
3622: + dmc - The coarse mesh
3623: . dmf - The fine mesh
3624: - ctx - The application context

3626:   Output Parameter:
3627: . sc - The mapping

3629:   Level: developer

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

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

3661:     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
3662:     PetscCall(PetscObjectGetClassId(obj, &id));
3663:     if (id == PETSCFE_CLASSID) {
3664:       PetscFE    fe = (PetscFE)obj;
3665:       PetscSpace sp;
3666:       PetscInt   maxDegree;

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

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

3694:     if (feRef[field]) {
3695:       PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&feC));
3696:       PetscCall(PetscFEGetNumComponents(feC, &NcC));
3697:       PetscCall(PetscFEGetNumComponents(feRef[field], &NcF));
3698:       PetscCall(PetscFEGetDualSpace(feRef[field], &QF));
3699:       PetscCall(PetscDualSpaceGetOrder(QF, &order));
3700:       PetscCall(PetscDualSpaceGetDimension(QF, &fpdim));
3701:       PetscCall(PetscFEGetDualSpace(feC, &QC));
3702:       PetscCall(PetscDualSpaceGetDimension(QC, &cpdim));
3703:     } else {
3704:       PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fvC));
3705:       PetscCall(PetscFVGetNumComponents(fvC, &NcC));
3706:       PetscCall(PetscFVGetNumComponents(fvRef[field], &NcF));
3707:       PetscCall(PetscFVGetDualSpace(fvRef[field], &QF));
3708:       PetscCall(PetscDualSpaceGetDimension(QF, &fpdim));
3709:       PetscCall(PetscFVGetDualSpace(fvC, &QC));
3710:       PetscCall(PetscDualSpaceGetDimension(QC, &cpdim));
3711:     }
3712:     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);
3713:     for (c = 0; c < cpdim; ++c) {
3714:       PetscQuadrature  cfunc;
3715:       const PetscReal *cqpoints, *cqweights;
3716:       PetscInt         NqcC, NpC;
3717:       PetscBool        found = PETSC_FALSE;

3719:       PetscCall(PetscDualSpaceGetFunctional(QC, c, &cfunc));
3720:       PetscCall(PetscQuadratureGetData(cfunc, NULL, &NqcC, &NpC, &cqpoints, &cqweights));
3721:       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);
3722:       PetscCheck(NpC == 1 || !feRef[field], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Do not know how to do injection for moments");
3723:       for (f = 0; f < fpdim; ++f) {
3724:         PetscQuadrature  ffunc;
3725:         const PetscReal *fqpoints, *fqweights;
3726:         PetscReal        sum = 0.0;
3727:         PetscInt         NqcF, NpF;

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

3756:   PetscCall(DMGetGlobalVector(dmf, &fv));
3757:   PetscCall(DMGetGlobalVector(dmc, &cv));
3758:   PetscCall(VecGetOwnershipRange(cv, &startC, &endC));
3759:   PetscCall(PetscSectionGetConstrainedStorageSize(cglobalSection, &m));
3760:   PetscCall(PetscMalloc2(cTotDim, &cellCIndices, fTotDim, &cellFIndices));
3761:   PetscCall(PetscMalloc1(m, &cindices));
3762:   PetscCall(PetscMalloc1(m, &findices));
3763:   for (d = 0; d < m; ++d) cindices[d] = findices[d] = -1;
3764:   for (c = cStart; c < cEnd; ++c) {
3765:     PetscCall(DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, c, cellCIndices, cellFIndices));
3766:     for (d = 0; d < cTotDim; ++d) {
3767:       if ((cellCIndices[d] < startC) || (cellCIndices[d] >= endC)) continue;
3768:       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]]);
3769:       cindices[cellCIndices[d] - startC] = cellCIndices[d];
3770:       findices[cellCIndices[d] - startC] = cellFIndices[cmap[d]];
3771:     }
3772:   }
3773:   PetscCall(PetscFree(cmap));
3774:   PetscCall(PetscFree2(cellCIndices, cellFIndices));

3776:   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, m, cindices, PETSC_OWN_POINTER, &cis));
3777:   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, m, findices, PETSC_OWN_POINTER, &fis));
3778:   PetscCall(VecScatterCreate(cv, cis, fv, fis, sc));
3779:   PetscCall(ISDestroy(&cis));
3780:   PetscCall(ISDestroy(&fis));
3781:   PetscCall(DMRestoreGlobalVector(dmf, &fv));
3782:   PetscCall(DMRestoreGlobalVector(dmc, &cv));
3783:   PetscCall(PetscLogEventEnd(DMPLEX_InjectorFEM, dmc, dmf, 0, 0));
3784:   PetscFunctionReturn(PETSC_SUCCESS);
3785: }

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

3790:   Input Parameters:
3791: + dm     - The `DM`
3792: . cellIS - The cells to include
3793: . locX   - A local vector with the solution fields
3794: . locX_t - A local vector with solution field time derivatives, or `NULL`
3795: - locA   - A local vector with auxiliary fields, or `NULL`

3797:   Output Parameters:
3798: + u   - The field coefficients
3799: . u_t - The fields derivative coefficients
3800: - a   - The auxiliary field coefficients

3802:   Level: developer

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

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

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

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

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

3875:   Input Parameters:
3876: + dm     - The `DM`
3877: . cellIS - The cells to include
3878: . locX   - A local vector with the solution fields
3879: . locX_t - A local vector with solution field time derivatives, or `NULL`
3880: - locA   - A local vector with auxiliary fields, or `NULL`

3882:   Output Parameters:
3883: + u   - The field coefficients
3884: . u_t - The fields derivative coefficients
3885: - a   - The auxiliary field coefficients

3887:   Level: developer

3889: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
3890: @*/
3891: PetscErrorCode DMPlexRestoreCellFields(DM dm, IS cellIS, Vec locX, PeOp Vec locX_t, PeOp Vec locA, PetscScalar *u[], PetscScalar *u_t[], PetscScalar *a[])
3892: {
3893:   PetscFunctionBegin;
3894:   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u));
3895:   if (locX_t) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u_t));
3896:   if (locA) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, a));
3897:   PetscFunctionReturn(PETSC_SUCCESS);
3898: }

3900: static PetscErrorCode DMPlexGetHybridCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
3901: {
3902:   DM              plex, plexA = NULL;
3903:   DMEnclosureType encAux;
3904:   PetscSection    section, sectionAux;
3905:   PetscDS         ds, dsIn;
3906:   const PetscInt *cells;
3907:   PetscInt        cStart, cEnd, numCells, c, totDim, totDimAux, Nf, f;

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

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

3954:     PetscCall(DMPlexGetCone(dm, cell, &cone));
3955:     PetscCall(DMPlexGetConeOrientation(dm, cell, &ornt));
3956:     // Put in cohesive unknowns
3957:     PetscCall(DMPlexVecGetClosure(plex, section, locX, cell, &Nxf, &xf));
3958:     if (locX_t) PetscCall(DMPlexVecGetClosure(plex, section, locX_t, cell, NULL, &xf_t));
3959:     for (f = 0; f < Nf; ++f) {
3960:       PetscInt  fdofIn, foff, foffIn;
3961:       PetscBool cohesive;

3963:       PetscCall(PetscDSGetCohesive(dsIn, f, &cohesive));
3964:       if (!cohesive) continue;
3965:       PetscCall(PetscDSGetFieldSize(dsIn, f, &fdofIn));
3966:       PetscCall(PetscDSGetFieldOffsetCohesive(ds, f, &foff));
3967:       PetscCall(PetscDSGetFieldOffsetCohesive(dsIn, f, &foffIn));
3968:       for (PetscInt i = 0; i < fdofIn; ++i) ul[foffIn + i] = xf[foff + i];
3969:       if (locX_t)
3970:         for (PetscInt i = 0; i < fdofIn; ++i) ul_t[foffIn + i] = xf_t[foff + i];
3971:       Nx += fdofIn;
3972:     }
3973:     PetscCall(DMPlexVecRestoreClosure(plex, section, locX, cell, &Nxf, &xf));
3974:     if (locX_t) PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, cell, NULL, &xf_t));
3975:     // Loop over sides of surface
3976:     PetscCheck(ornt[0] == ornt[1], PETSC_COMM_SELF, PETSC_ERR_SUP, "Face %" PetscInt_FMT " in hybrid cell %" PetscInt_FMT " has orientation %" PetscInt_FMT " != %" PetscInt_FMT " of face %" PetscInt_FMT, cone[0], cell, ornt[0], ornt[1], cone[1]);
3977:     for (s = 0; s < 2; ++s) {
3978:       const PetscInt *support;
3979:       const PetscInt  face = cone[s];
3980:       PetscDS         dsC;
3981:       PetscInt        ssize, ncell, Nxc;

3983:       // I don't think I need the face to have 0 orientation in the hybrid cell
3984:       //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]);
3985:       PetscCall(DMPlexGetSupport(dm, face, &support));
3986:       PetscCall(DMPlexGetSupportSize(dm, face, &ssize));
3987:       if (support[0] == cell) ncell = support[1];
3988:       else if (support[1] == cell) ncell = support[0];
3989:       else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", face, cell);
3990:       // Get closure of both face and cell, stick in cell for normal fields and face for cohesive fields
3991:       PetscCall(DMGetCellDS(dm, ncell, &dsC, NULL));
3992:       PetscCall(DMPlexVecGetClosure(plex, section, locX, ncell, &Nxc, &xc));
3993:       if (locX_t) PetscCall(DMPlexVecGetClosure(plex, section, locX_t, ncell, NULL, &xc_t));
3994:       for (f = 0; f < Nf; ++f) {
3995:         PetscInt  fdofIn, foffIn, foff;
3996:         PetscBool cohesive;

3998:         PetscCall(PetscDSGetCohesive(dsIn, f, &cohesive));
3999:         if (cohesive) continue;
4000:         PetscCall(PetscDSGetFieldSize(dsIn, f, &fdofIn));
4001:         PetscCall(PetscDSGetFieldOffset(dsC, f, &foff));
4002:         PetscCall(PetscDSGetFieldOffsetCohesive(dsIn, f, &foffIn));
4003:         for (PetscInt i = 0; i < fdofIn; ++i) ul[foffIn + s * fdofIn + i] = xc[foff + i];
4004:         if (locX_t)
4005:           for (PetscInt i = 0; i < fdofIn; ++i) ul_t[foffIn + s * fdofIn + i] = xc_t[foff + i];
4006:         Nx += fdofIn;
4007:       }
4008:       PetscCall(DMPlexVecRestoreClosure(plex, section, locX, ncell, &Nxc, &xc));
4009:       if (locX_t) PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, ncell, NULL, &xc_t));
4010:     }
4011:     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);

4013:     if (locA) {
4014:       PetscScalar *al = &(*a)[cind * totDimAux];
4015:       PetscInt     subcell;

4017:       PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, cell, &subcell));
4018:       PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subcell, &Nx, &x));
4019:       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);
4020:       for (PetscInt i = 0; i < totDimAux; ++i) al[i] = x[i];
4021:       PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subcell, &Nx, &x));
4022:     }
4023:   }
4024:   PetscCall(DMDestroy(&plex));
4025:   PetscCall(DMDestroy(&plexA));
4026:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
4027:   PetscFunctionReturn(PETSC_SUCCESS);
4028: }

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

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

4041:   Output Parameter:
4042: . x       - An array of field values, indexed by s

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

4047:   Level: advanced

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

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

4083:     PetscCall(DMPlexGetCone(dm, cell, &cone));
4084:     PetscCall(DMPlexGetConeOrientation(dm, cell, &ornt));
4085:     //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]);
4086:     for (s = 0; s < 2; ++s) {
4087:       const PetscInt tdX     = totDimX[s];
4088:       PetscScalar   *closure = NULL, *xl = &x[s][cind * tdX];
4089:       PetscInt       face = cone[s], point = face, subpoint, Nx, i;

4091:       if (useCell) {
4092:         const PetscInt *support;
4093:         PetscInt        ssize;

4095:         PetscCall(DMPlexGetSupport(dm, face, &support));
4096:         PetscCall(DMPlexGetSupportSize(dm, face, &ssize));
4097:         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);
4098:         if (support[0] == cell) point = support[1];
4099:         else if (support[1] == cell) point = support[0];
4100:         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", face, cell);
4101:       }
4102:       PetscCall(DMGetEnclosurePoint(plexX[s], dm, encX[s], point, &subpoint));
4103:       PetscCall(DMPlexVecGetOrientedClosure(plexX[s], sectionX[s], PETSC_FALSE, locX[s], subpoint, ornt[s], &Nx, &closure));
4104:       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);
4105:       for (i = 0; i < Nx; ++i) xl[i] = closure[i];
4106:       PetscCall(DMPlexVecRestoreClosure(plexX[s], sectionX[s], locX[s], subpoint, &Nx, &closure));
4107:     }
4108:   }
4109:   for (s = 0; s < 2; ++s) PetscCall(DMDestroy(&plexX[s]));
4110:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
4111:   PetscFunctionReturn(PETSC_SUCCESS);
4112: }

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

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

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

4136:   Output Parameters:
4137: + Nface - The number of faces with field values
4138: . uL    - The field values at the left side of the face
4139: - uR    - The field values at the right side of the face

4141:   Level: developer

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

4155:   PetscFunctionBegin;
4162:   PetscAssertPointer(uL, 10);
4163:   PetscAssertPointer(uR, 11);
4164:   PetscCall(DMGetDimension(dm, &dim));
4165:   PetscCall(DMGetDS(dm, &prob));
4166:   PetscCall(DMGetLocalSection(dm, &section));
4167:   PetscCall(PetscDSGetNumFields(prob, &Nf));
4168:   PetscCall(PetscDSGetTotalComponents(prob, &Nc));
4169:   PetscCall(PetscMalloc1(Nf, &isFE));
4170:   for (f = 0; f < Nf; ++f) {
4171:     PetscObject  obj;
4172:     PetscClassId id;

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

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

4216:       PetscCall(PetscDSGetComponentOffset(prob, f, &off));
4217:       if (isFE[f]) {
4218:         const PetscInt *cone;
4219:         PetscInt        comp, coneSizeL, coneSizeR, faceLocL, faceLocR, ldof, rdof, d;

4221:         xL = xR = NULL;
4222:         PetscCall(PetscSectionGetFieldComponents(section, f, &comp));
4223:         PetscCall(DMPlexVecGetClosure(dm, section, locX, cells[0], &ldof, &xL));
4224:         PetscCall(DMPlexVecGetClosure(dm, section, locX, cells[1], &rdof, &xR));
4225:         PetscCall(DMPlexGetCone(dm, cells[0], &cone));
4226:         PetscCall(DMPlexGetConeSize(dm, cells[0], &coneSizeL));
4227:         for (faceLocL = 0; faceLocL < coneSizeL; ++faceLocL)
4228:           if (cone[faceLocL] == face) break;
4229:         PetscCall(DMPlexGetCone(dm, cells[1], &cone));
4230:         PetscCall(DMPlexGetConeSize(dm, cells[1], &coneSizeR));
4231:         for (faceLocR = 0; faceLocR < coneSizeR; ++faceLocR)
4232:           if (cone[faceLocR] == face) break;
4233:         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]);
4234:         /* Check that FEM field has values in the right cell (sometimes its an FV ghost cell) */
4235:         /* TODO: this is a hack that might not be right for nonconforming */
4236:         if (faceLocL < coneSizeL) {
4237:           PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocL, xL, &uLl[iface * Nc + off]));
4238:           if (rdof == ldof && faceLocR < coneSizeR) PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface * Nc + off]));
4239:           else {
4240:             for (d = 0; d < comp; ++d) uRl[iface * Nc + off + d] = uLl[iface * Nc + off + d];
4241:           }
4242:         } else {
4243:           PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface * Nc + off]));
4244:           PetscCall(PetscSectionGetFieldComponents(section, f, &comp));
4245:           for (d = 0; d < comp; ++d) uLl[iface * Nc + off + d] = uRl[iface * Nc + off + d];
4246:         }
4247:         PetscCall(DMPlexVecRestoreClosure(dm, section, locX, cells[0], &ldof, &xL));
4248:         PetscCall(DMPlexVecRestoreClosure(dm, section, locX, cells[1], &rdof, &xR));
4249:       } else {
4250:         PetscFV  fv;
4251:         PetscInt numComp;

4253:         PetscCall(PetscDSGetDiscretization(prob, f, (PetscObject *)&fv));
4254:         PetscCall(PetscFVGetNumComponents(fv, &numComp));
4255:         PetscCall(DMPlexPointLocalFieldRead(dm, cells[0], f, x, &xL));
4256:         PetscCall(DMPlexPointLocalFieldRead(dm, cells[1], f, x, &xR));
4257:         if (dmGrad) {
4258:           PetscReal dxL[3], dxR[3];

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

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

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

4300:   Output Parameters:
4301: + Nface - The number of faces with field values
4302: . uL    - The field values at the left side of the face
4303: - uR    - The field values at the right side of the face

4305:   Level: developer

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

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

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

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

4332:   Level: developer

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

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

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

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

4390:   Input Parameters:
4391: + dm           - The `DM`
4392: . fStart       - The first face to include
4393: . fEnd         - The first face to exclude
4394: . faceGeometry - A local vector with face geometry
4395: - cellGeometry - A local vector with cell geometry

4397:   Output Parameters:
4398: + Nface - The number of faces with field values
4399: . fgeom - The face centroid and normals
4400: - vol   - The cell volumes

4402:   Level: developer

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

4414: PetscErrorCode DMSNESGetFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscFEGeomMode mode, PetscFEGeom **geom)
4415: {
4416:   char           composeStr[33] = {0};
4417:   PetscObjectId  id;
4418:   PetscContainer container;

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

4437: PetscErrorCode DMSNESRestoreFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
4438: {
4439:   PetscFunctionBegin;
4440:   *geom = NULL;
4441:   PetscFunctionReturn(PETSC_SUCCESS);
4442: }

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

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

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

4506:         PetscCall(PetscDSGetImplicit(prob, f, &fimp));
4507:         if (isImplicit != fimp) continue;
4508:         PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4509:         PetscCall(PetscObjectGetClassId(obj, &id));
4510:         if (id == PETSCFE_CLASSID) {
4511:           PetscFE fe = (PetscFE)obj;

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

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

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

4565:         PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
4566:         PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
4567:         PetscCall(PetscFEGetDimension(fe, &Nb));
4568:         blockSize = Nb;
4569:         batchSize = numBlocks * blockSize;
4570:         PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
4571:         numChunks = numCells / (numBatches * batchSize);
4572:         Ne        = numChunks * numBatches * batchSize;
4573:         Nr        = numCells % (numBatches * batchSize);
4574:         offset    = numCells - Nr;
4575:         /* Integrate FE residual to get elemVec (need fields at quadrature points) */
4576:         /*   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) */
4577:         PetscCall(PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom));
4578:         PetscCall(PetscFEIntegrateResidual(prob, key, Ne, chunkGeom, u, u_t, probAux, a, t, elemVec));
4579:         PetscCall(PetscFEGeomGetChunk(geom, offset, numCells, &chunkGeom));
4580:         PetscCall(PetscFEIntegrateResidual(prob, key, Nr, chunkGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), probAux, &a[offset * totDimAux], t, &elemVec[offset * totDim]));
4581:         PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &chunkGeom));
4582:       } else if (id == PETSCFV_CLASSID) {
4583:         PetscFV fv = (PetscFV)obj;

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

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

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

4612:       PetscCall(VecGetArray(locF, &fa));
4613:       PetscCall(VecGetArray(locX_t, &x_t));
4614:       for (f = 0; f < Nf; ++f) {
4615:         PetscFV      fv;
4616:         PetscObject  obj;
4617:         PetscClassId id;
4618:         PetscInt     pdim;

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

4629:           if (ghostLabel) {
4630:             PetscInt ghostVal;

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

4667: /*
4668:   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

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

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

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

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

4778:     /* Extract values */
4779:     for (PetscInt c = 0; c < Ncell; ++c) {
4780:       const PetscInt cell = cells ? cells[c + offCell] : c + offCell;
4781:       PetscScalar   *x = NULL, *x_t = NULL;

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

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

4859: /* FEM Assembly Function */

4861: static PetscErrorCode DMConvertPlex_Internal(DM dm, DM *plex, PetscBool copy)
4862: {
4863:   PetscBool isPlex;

4865:   PetscFunctionBegin;
4866:   PetscCall(PetscObjectTypeCompare((PetscObject)dm, DMPLEX, &isPlex));
4867:   if (isPlex) {
4868:     *plex = dm;
4869:     PetscCall(PetscObjectReference((PetscObject)dm));
4870:   } else {
4871:     PetscCall(PetscObjectQuery((PetscObject)dm, "dm_plex", (PetscObject *)plex));
4872:     if (!*plex) {
4873:       PetscCall(DMConvert(dm, DMPLEX, plex));
4874:       PetscCall(PetscObjectCompose((PetscObject)dm, "dm_plex", (PetscObject)*plex));
4875:     } else {
4876:       PetscCall(PetscObjectReference((PetscObject)*plex));
4877:     }
4878:     if (copy) PetscCall(DMCopyAuxiliaryVec(dm, *plex));
4879:   }
4880:   PetscFunctionReturn(PETSC_SUCCESS);
4881: }

4883: /*@
4884:   DMPlexGetGeometryFVM - Return precomputed geometric data

4886:   Collective

4888:   Input Parameter:
4889: . dm - The `DM`

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

4896:   Level: developer

4898: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMTSSetRHSFunctionLocal()`
4899: @*/
4900: PetscErrorCode DMPlexGetGeometryFVM(DM dm, Vec *facegeom, Vec *cellgeom, PeOp PetscReal *minRadius)
4901: {
4902:   DM plex;

4904:   PetscFunctionBegin;
4906:   PetscCall(DMConvertPlex_Internal(dm, &plex, PETSC_TRUE));
4907:   PetscCall(DMPlexGetDataFVM(plex, NULL, cellgeom, facegeom, NULL));
4908:   if (minRadius) PetscCall(DMPlexGetMinRadius(plex, minRadius));
4909:   PetscCall(DMDestroy(&plex));
4910:   PetscFunctionReturn(PETSC_SUCCESS);
4911: }

4913: /*@
4914:   DMPlexGetGradientDM - Return gradient data layout

4916:   Collective

4918:   Input Parameters:
4919: + dm - The `DM`
4920: - fv - The `PetscFV`

4922:   Output Parameter:
4923: . dmGrad - The layout for gradient values

4925:   Level: developer

4927: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetGeometryFVM()`
4928: @*/
4929: PetscErrorCode DMPlexGetGradientDM(DM dm, PetscFV fv, DM *dmGrad)
4930: {
4931:   DM        plex;
4932:   PetscBool computeGradients;

4934:   PetscFunctionBegin;
4937:   PetscAssertPointer(dmGrad, 3);
4938:   PetscCall(PetscFVGetComputeGradients(fv, &computeGradients));
4939:   if (!computeGradients) {
4940:     *dmGrad = NULL;
4941:     PetscFunctionReturn(PETSC_SUCCESS);
4942:   }
4943:   PetscCall(DMConvertPlex_Internal(dm, &plex, PETSC_TRUE));
4944:   PetscCall(DMPlexGetDataFVM(plex, fv, NULL, NULL, dmGrad));
4945:   PetscCall(DMDestroy(&plex));
4946:   PetscFunctionReturn(PETSC_SUCCESS);
4947: }

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

4952:   Not collective

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

4964:   Output Parameter:
4965: . locF - The local residual

4967:   Level: developer

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

4983:   PetscFunctionBegin;
4984:   PetscCall(DMConvert(dm, DMPLEX, &plex));
4985:   PetscCall(DMGetLocalSection(dm, &section));
4986:   PetscCall(DMGetDS(dm, &prob));
4987:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
4988:   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &locA));
4989:   if (locA) {
4990:     DM dmAux;

4992:     PetscCall(VecGetDM(locA, &dmAux));
4993:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
4994:     PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
4995:     PetscCall(DMGetDS(plexA, &probAux));
4996:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
4997:     PetscCall(DMGetLocalSection(plexA, &sectionAux));
4998:   }
4999:   {
5000:     PetscFEGeom    *fgeom;
5001:     PetscInt        maxDegree;
5002:     PetscQuadrature qGeom = NULL;
5003:     IS              pointIS;
5004:     const PetscInt *points;
5005:     PetscInt        numFaces, face, Nq;

5007:     PetscCall(DMLabelGetStratumIS(key.label, key.value, &pointIS));
5008:     if (!pointIS) goto end; /* No points with that id on this process */
5009:     {
5010:       IS isectIS;

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

5025:       PetscCall(PetscDSGetDiscretization(prob, key.field, (PetscObject *)&fe));
5026:       PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
5027:       PetscCall(PetscObjectReference((PetscObject)qGeom));
5028:     }
5029:     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
5030:     PetscCall(DMSNESGetFEGeom(coordField, pointIS, qGeom, PETSC_FEGEOM_BOUNDARY, &fgeom));
5031:     for (face = 0; face < numFaces; ++face) {
5032:       const PetscInt point = points[face], *support;
5033:       PetscScalar   *x     = NULL;

5035:       PetscCall(DMPlexGetSupport(dm, point, &support));
5036:       PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
5037:       for (PetscInt i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
5038:       PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
5039:       if (locX_t) {
5040:         PetscCall(DMPlexVecGetClosure(plex, section, locX_t, support[0], NULL, &x));
5041:         for (PetscInt i = 0; i < totDim; ++i) u_t[face * totDim + i] = x[i];
5042:         PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, support[0], NULL, &x));
5043:       }
5044:       if (locA) {
5045:         PetscInt subp;

5047:         PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
5048:         PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
5049:         for (PetscInt i = 0; i < totDimAux; ++i) a[face * totDimAux + i] = x[i];
5050:         PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
5051:       }
5052:     }
5053:     PetscCall(PetscArrayzero(elemVec, numFaces * totDim));
5054:     {
5055:       PetscFE      fe;
5056:       PetscInt     Nb;
5057:       PetscFEGeom *chunkGeom = NULL;
5058:       /* Conforming batches */
5059:       PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
5060:       /* Remainder */
5061:       PetscInt Nr, offset;

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

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

5101:     PetscCall(DMGetLocalSection(dm, &s));
5102:     PetscCall(VecDuplicate(locF, &locFbc));
5103:     PetscCall(VecCopy(locF, locFbc));
5104:     PetscCall(PetscSectionGetChart(s, &pStart, &pEnd));
5105:     PetscCall(PetscSectionGetMaxDof(s, &maxDof));
5106:     PetscCall(PetscCalloc1(maxDof, &zeroes));
5107:     for (PetscInt p = pStart; p < pEnd; p++) PetscCall(VecSetValuesSection(locFbc, s, p, zeroes, INSERT_BC_VALUES));
5108:     PetscCall(PetscFree(zeroes));
5109:     PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc));
5110:     PetscCall(VecDestroy(&locFbc));
5111:   }
5112:   PetscCall(DMDestroy(&plex));
5113:   PetscCall(DMDestroy(&plexA));
5114:   PetscFunctionReturn(PETSC_SUCCESS);
5115: }

5117: /*@
5118:   DMPlexComputeBdResidualSingle - Compute the local boundary residual

5120:   Not collective

5122:   Input Parameters:
5123: + dm     - The output `DM`
5124: . wf     - The `PetscWeakForm` holding forms on this boundary
5125: . key    - The `PetscFormKey` indicating what should be integrated
5126: . locX   - The local solution
5127: . locX_t - The time derivative of the local solution, or `NULL` for time-independent problems
5128: - t      - The time

5130:   Output Parameter:
5131: . locF - The local residual

5133:   Level: developer

5135: .seealso: `DMPlexComputeBdResidualSingleByKey()`, `DMPlexComputeJacobianByKey()`, `DMPlexComputeResidualHybridByKey()`, `DMPlexComputeJacobianHybridByKey()`, `PetscFormKey`
5136: @*/
5137: PetscErrorCode DMPlexComputeBdResidualSingle(DM dm, PetscWeakForm wf, PetscFormKey key, Vec locX, Vec locX_t, PetscReal t, Vec locF)
5138: {
5139:   DMField  coordField;
5140:   DMLabel  depthLabel;
5141:   IS       facetIS;
5142:   PetscInt dim;

5144:   PetscFunctionBegin;
5145:   PetscCall(DMGetDimension(dm, &dim));
5146:   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5147:   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
5148:   PetscCall(DMGetCoordinateField(dm, &coordField));
5149:   PetscCall(DMPlexComputeBdResidualSingleByKey(dm, wf, key, facetIS, locX, locX_t, t, coordField, locF));
5150:   PetscCall(ISDestroy(&facetIS));
5151:   PetscFunctionReturn(PETSC_SUCCESS);
5152: }

5154: static PetscErrorCode DMPlexComputeBdResidual_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, Vec locF, PetscCtx ctx)
5155: {
5156:   PetscDS  prob;
5157:   PetscInt numBd;
5158:   DMField  coordField = NULL;
5159:   IS       facetIS    = NULL;
5160:   DMLabel  depthLabel;
5161:   PetscInt dim;

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

5177:     PetscCall(DMGetPointSF(dm, &sf));
5178:     PetscCall(PetscSFGetGraph(sf, NULL, &nleaves, &leaves, NULL));
5179:     if (nleaves > 0 && leaves) {
5180:       IS leafIS, ownedFacetIS;

5182:       PetscCall(ISCreateGeneral(PETSC_COMM_SELF, nleaves, leaves, PETSC_USE_POINTER, &leafIS));
5183:       PetscCall(ISDifference(facetIS, leafIS, &ownedFacetIS));
5184:       PetscCall(ISDestroy(&leafIS));
5185:       PetscCall(ISDestroy(&facetIS));
5186:       facetIS = ownedFacetIS;
5187:     }
5188:   }
5189:   PetscCall(PetscDSGetNumBoundary(prob, &numBd));
5190:   for (PetscInt bd = 0; bd < numBd; ++bd) {
5191:     PetscWeakForm           wf;
5192:     DMBoundaryConditionType type;
5193:     DMLabel                 label;
5194:     const PetscInt         *values;
5195:     PetscInt                field, numValues, v;
5196:     PetscObject             obj;
5197:     PetscClassId            id;
5198:     PetscFormKey            key;

5200:     PetscCall(PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &field, NULL, NULL, NULL, NULL, NULL));
5201:     if (!(type & DM_BC_NATURAL)) continue;
5202:     PetscCall(PetscDSGetDiscretization(prob, field, &obj));
5203:     PetscCall(PetscObjectGetClassId(obj, &id));
5204:     if (id != PETSCFE_CLASSID) continue;
5205:     if (!facetIS) {
5206:       DMLabel  depthLabel;
5207:       PetscInt dim;

5209:       PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5210:       PetscCall(DMGetDimension(dm, &dim));
5211:       PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
5212:     }
5213:     PetscCall(DMGetCoordinateField(dm, &coordField));
5214:     for (v = 0; v < numValues; ++v) {
5215:       key.label = label;
5216:       key.value = values[v];
5217:       key.field = field;
5218:       key.part  = 0;
5219:       PetscCall(DMPlexComputeBdResidualSingleByKey(dm, wf, key, facetIS, locX, locX_t, t, coordField, locF));
5220:     }
5221:   }
5222:   PetscCall(ISDestroy(&facetIS));
5223:   PetscFunctionReturn(PETSC_SUCCESS);
5224: }

5226: /*@
5227:   DMPlexComputeResidualByKey - Compute the local residual for terms matching the input key

5229:   Collective

5231:   Input Parameters:
5232: + dm     - The output `DM`
5233: . key    - The `PetscFormKey` indicating what should be integrated
5234: . cellIS - The `IS` giving a set of cells to integrate over
5235: . time   - The time, or `PETSC_MIN_REAL` to include implicit terms in a time-independent problems
5236: . locX   - The local solution
5237: . locX_t - The time derivative of the local solution, or `NULL` for time-independent problems
5238: . t      - The time
5239: - ctx    - An optional application context, passed to the pointwise functions

5241:   Output Parameter:
5242: . locF - The local residual

5244:   Level: developer

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

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

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

5325:         PetscCall(PetscDSGetImplicit(ds, f, &fimp));
5326:         if (isImplicit != fimp) continue;
5327:         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
5328:         PetscCall(PetscObjectGetClassId(obj, &id));
5329:         if (id == PETSCFE_CLASSID) {
5330:           PetscFE fe = (PetscFE)obj;

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

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

5382:       key.field = f;
5383:       PetscCall(PetscDSGetImplicit(ds, f, &fimp));
5384:       if (isImplicit != fimp) continue;
5385:       PetscCall(PetscDSGetDiscretization(ds, f, &obj));
5386:       PetscCall(PetscObjectGetClassId(obj, &id));
5387:       if (id == PETSCFE_CLASSID) {
5388:         PetscFE         fe        = (PetscFE)obj;
5389:         PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[f];
5390:         PetscFEGeom    *chunkGeom = NULL;
5391:         PetscQuadrature quad      = affineQuad ? affineQuad : quads[f];
5392:         PetscInt        Nq, Nb;

5394:         PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5395:         PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
5396:         PetscCall(PetscFEGetDimension(fe, &Nb));
5397:         blockSize = Nb;
5398:         batchSize = numBlocks * blockSize;
5399:         PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5400:         numChunks = numCells / (numBatches * batchSize);
5401:         Ne        = numChunks * numBatches * batchSize;
5402:         Nr        = numCells % (numBatches * batchSize);
5403:         offset    = numCells - Nr;
5404:         /* Integrate FE residual to get elemVec (need fields at quadrature points) */
5405:         /*   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) */
5406:         PetscCall(PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom));
5407:         PetscCall(PetscFEIntegrateResidual(ds, key, Ne, chunkGeom, u, u_t, dsAux, a, t, elemVec));
5408:         PetscCall(PetscFEGeomGetChunk(geom, offset, numCells, &chunkGeom));
5409:         PetscCall(PetscFEIntegrateResidual(ds, key, Nr, chunkGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), dsAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, &elemVec[offset * totDim]));
5410:         PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &chunkGeom));
5411:       } else if (id == PETSCFV_CLASSID) {
5412:         PetscFV fv = (PetscFV)obj;

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

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

5431:           PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
5432:           if (ghostVal > 0) continue;
5433:         }
5434:         PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind * totDim], ADD_ALL_VALUES));
5435:       }
5436:     }
5437:     if (useFVM) {
5438:       PetscScalar *fa;
5439:       PetscInt     iface;

5441:       PetscCall(VecGetArray(locF, &fa));
5442:       for (f = 0; f < Nf; ++f) {
5443:         PetscFV      fv;
5444:         PetscObject  obj;
5445:         PetscClassId id;
5446:         PetscInt     cdim, foff, pdim;

5448:         PetscCall(DMGetCoordinateDim(dm, &cdim));
5449:         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
5450:         PetscCall(PetscDSGetFieldOffset(ds, f, &foff));
5451:         PetscCall(PetscObjectGetClassId(obj, &id));
5452:         if (id != PETSCFV_CLASSID) continue;
5453:         fv = (PetscFV)obj;
5454:         PetscCall(PetscFVGetNumComponents(fv, &pdim));
5455:         /* Accumulate fluxes to cells */
5456:         for (face = fS, iface = 0; face < fE; ++face) {
5457:           const PetscInt *scells;
5458:           PetscScalar    *fL = NULL, *fR = NULL;
5459:           PetscInt        ghost, d, nsupp, nchild;

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

5490:       PetscCall(VecGetArray(locF, &fa));
5491:       PetscCall(VecGetArray(locX_t, &x_t));
5492:       for (f = 0; f < Nf; ++f) {
5493:         PetscFV      fv;
5494:         PetscObject  obj;
5495:         PetscClassId id;
5496:         PetscInt     pdim;

5498:         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
5499:         PetscCall(PetscObjectGetClassId(obj, &id));
5500:         if (id != PETSCFV_CLASSID) continue;
5501:         fv = (PetscFV)obj;
5502:         PetscCall(PetscFVGetNumComponents(fv, &pdim));
5503:         for (c = cS; c < cE; ++c) {
5504:           const PetscInt cell = cells ? cells[c] : c;
5505:           PetscScalar   *u_t, *r;

5507:           if (ghostLabel) {
5508:             PetscInt ghostVal;

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

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

5539:     if (maxDegree <= 1) {
5540:       PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
5541:       PetscCall(PetscQuadratureDestroy(&affineQuad));
5542:     } else {
5543:       for (f = 0; f < Nf; ++f) {
5544:         PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
5545:         PetscCall(PetscQuadratureDestroy(&quads[f]));
5546:       }
5547:       PetscCall(PetscFree2(quads, geoms));
5548:     }
5549:   }

5551:   /* FEM */
5552:   /* 1: Get sizes from dm and dmAux */
5553:   /* 2: Get geometric data */
5554:   /* 3: Handle boundary values */
5555:   /* 4: Loop over domain */
5556:   /*   Extract coefficients */
5557:   /* Loop over fields */
5558:   /*   Set tiling for FE*/
5559:   /*   Integrate FE residual to get elemVec */
5560:   /*     Loop over subdomain */
5561:   /*       Loop over quad points */
5562:   /*         Transform coords to real space */
5563:   /*         Evaluate field and aux fields at point */
5564:   /*         Evaluate residual at point */
5565:   /*         Transform residual to real space */
5566:   /*       Add residual to elemVec */
5567:   /* Loop over domain */
5568:   /*   Add elemVec to locX */

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

5591:     PetscCall(VecDuplicate(locF, &locFbc));
5592:     PetscCall(VecCopy(locF, locFbc));
5593:     PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5594:     PetscCall(PetscSectionGetMaxDof(section, &maxDof));
5595:     PetscCall(PetscCalloc1(maxDof, &zeroes));
5596:     for (p = pStart; p < pEnd; p++) PetscCall(VecSetValuesSection(locFbc, section, p, zeroes, INSERT_BC_VALUES));
5597:     PetscCall(PetscFree(zeroes));
5598:     PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc));
5599:     PetscCall(VecDestroy(&locFbc));
5600:   }
5601: end:
5602:   PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
5603:   PetscFunctionReturn(PETSC_SUCCESS);
5604: }

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

5609:   Collective

5611:   Input Parameters:
5612: + dm     - The output `DM`
5613: . key    - The `PetscFormKey` array (left cell, right cell, cohesive cell) indicating what should be integrated
5614: . cellIS - The `IS` give a set of cells to integrate over
5615: . time   - The time, or `PETSC_MIN_REAL` to include implicit terms in a time-independent problems
5616: . locX   - The local solution
5617: . locX_t - The time derivative of the local solution, or `NULL` for time-independent problems
5618: . t      - The time
5619: - ctx    - An optional application context, passed to the pointwise functions

5621:   Output Parameter:
5622: . locF - The local residual

5624:   Level: developer

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

5658:   PetscFunctionBegin;
5659:   PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
5660:   if (!cellIS) goto end;
5661:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
5662:   PetscCall(ISGetLocalSize(cellIS, &numCells));
5663:   if (cStart >= cEnd) goto end;
5664:   if ((key[0].label == key[1].label) && (key[0].value == key[1].value) && (key[0].part == key[1].part)) {
5665:     const char *name;
5666:     PetscCall(PetscObjectGetName((PetscObject)key[0].label, &name));
5667:     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);
5668:   }
5669:   /* TODO The places where we have to use isFE are probably the member functions for the PetscDisc class */
5670:   /* FEM */
5671:   /* 1: Get sizes from dm and dmAux */
5672:   PetscCall(DMGetLocalSection(dm, &section));
5673:   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
5674:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn));
5675:   PetscCall(PetscDSGetNumFields(ds, &Nf));
5676:   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
5677:   PetscCall(PetscDSGetTotalDimension(dsIn, &totDimIn));
5678:   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, key[2].value, key[2].part, &locA[2]));
5679:   if (locA[2]) {
5680:     const PetscInt cellStart = cells ? cells[cStart] : cStart;

5682:     PetscCall(VecGetDM(locA[2], &dmAux[2]));
5683:     PetscCall(DMGetCellDS(dmAux[2], cellStart, &dsAux[2], NULL));
5684:     PetscCall(PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]));
5685:     {
5686:       const PetscInt *cone;
5687:       PetscInt        c;

5689:       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
5690:       for (c = 0; c < 2; ++c) {
5691:         const PetscInt *support;
5692:         PetscInt        ssize, s;

5694:         PetscCall(DMPlexGetSupport(dm, cone[c], &support));
5695:         PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
5696:         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);
5697:         if (support[0] == cellStart) s = 1;
5698:         else if (support[1] == cellStart) s = 0;
5699:         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
5700:         PetscCall(DMGetAuxiliaryVec(dm, key[c].label, key[c].value, key[c].part, &locA[c]));
5701:         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);
5702:         if (locA[c]) PetscCall(VecGetDM(locA[c], &dmAux[c]));
5703:         else dmAux[c] = dmAux[2];
5704:         PetscCall(DMGetCellDS(dmAux[c], support[s], &dsAux[c], NULL));
5705:         PetscCall(PetscDSGetTotalDimension(dsAux[c], &totDimAux[c]));
5706:       }
5707:     }
5708:   }
5709:   /* Handle mass matrix scaling
5710:        The field in key[2] is the field to be scaled, and the scaling field is the first in the dsScale */
5711:   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, -key[2].value, key[2].part, &locS[2]));
5712:   if (locS[2]) {
5713:     const PetscInt cellStart = cells ? cells[cStart] : cStart;
5714:     PetscInt       Nb, Nbs;

5716:     PetscCall(VecGetDM(locS[2], &dmScale[2]));
5717:     PetscCall(DMGetCellDS(dmScale[2], cellStart, &dsScale[2], NULL));
5718:     PetscCall(PetscDSGetTotalDimension(dsScale[2], &totDimScale[2]));
5719:     // BRAD: This is not set correctly
5720:     key[2].field = 2;
5721:     PetscCall(PetscDSGetFieldSize(ds, key[2].field, &Nb));
5722:     PetscCall(PetscDSGetFieldSize(dsScale[2], 0, &Nbs));
5723:     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);
5724:     {
5725:       const PetscInt *cone;

5727:       locS[1] = locS[0] = locS[2];
5728:       dmScale[1] = dmScale[0] = dmScale[2];
5729:       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
5730:       for (PetscInt c = 0; c < 2; ++c) {
5731:         const PetscInt *support;
5732:         PetscInt        ssize, s;

5734:         PetscCall(DMPlexGetSupport(dm, cone[c], &support));
5735:         PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
5736:         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);
5737:         if (support[0] == cellStart) s = 1;
5738:         else if (support[1] == cellStart) s = 0;
5739:         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
5740:         PetscCall(DMGetCellDS(dmScale[c], support[s], &dsScale[c], NULL));
5741:         PetscCall(PetscDSGetTotalDimension(dsScale[c], &totDimScale[c]));
5742:       }
5743:     }
5744:   }
5745:   /* 2: Setup geometric data */
5746:   PetscCall(DMGetCoordinateField(dm, &coordField));
5747:   PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
5748:   if (maxDegree > 1) {
5749:     PetscCall(PetscCalloc4(Nf, &quadsF, Nf, &geomsF, Nf, &quadsN, Nf, &geomsN));
5750:     for (f = 0; f < Nf; ++f) {
5751:       PetscFE   fe;
5752:       PetscBool isCohesiveField;

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

5794:     PetscCall(PetscArrayzero(elemVecNeg, cellChunkSize * totDim));
5795:     PetscCall(PetscArrayzero(elemVecPos, cellChunkSize * totDim));
5796:     PetscCall(PetscArrayzero(elemVecCoh, cellChunkSize * totDim));
5797:     /* Get faces and neighbors */
5798:     PetscCall(DMGetPointSF(dm, &sf));
5799:     PetscCall(PetscSFGetGraph(sf, NULL, &Nl, &leaves, NULL));
5800:     for (c = cS; c < cE; ++c) {
5801:       const PetscInt  cell = cells ? cells[c] : c;
5802:       const PetscInt *cone, *support;
5803:       PetscInt        pos = -1;

5805:       if (leaves) PetscCall(PetscFindInt(cell, Nl, leaves, &pos));
5806:       PetscCheck(pos < 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Hybrid Cell %" PetscInt_FMT " is a ghost cell, so it should not be assembled", cell);
5807:       PetscCall(DMPlexGetCone(dm, cell, &cone));
5808:       faces[(c - cS) * 2 + 0] = cone[0];
5809:       faces[(c - cS) * 2 + 1] = cone[1];
5810:       PetscCall(DMPlexGetSupport(dm, cone[0], &support));
5811:       neighbors[(c - cS) * 2 + 0] = support[0] == cell ? support[1] : support[0];
5812:       PetscCall(DMPlexGetSupport(dm, cone[1], &support));
5813:       neighbors[(c - cS) * 2 + 1] = support[0] == cell ? support[1] : support[0];
5814:     }
5815:     PetscCall(ISGeneralSetIndices(chunkISF, 2 * cellChunkSize, faces, PETSC_USE_POINTER));
5816:     PetscCall(ISGeneralSetIndices(chunkISN, 2 * cellChunkSize, neighbors, PETSC_USE_POINTER));
5817:     /* Get geometric data */
5818:     if (maxDegree <= 1) {
5819:       if (!affineQuadF) PetscCall(DMFieldCreateDefaultQuadrature(coordField, chunkISF, &affineQuadF));
5820:       if (affineQuadF) PetscCall(DMSNESGetFEGeom(coordField, chunkISF, affineQuadF, PETSC_FEGEOM_COHESIVE, &affineGeomF));
5821:       if (!affineQuadN) {
5822:         PetscInt dim;
5823:         PetscCall(PetscQuadratureGetData(affineQuadF, &dim, NULL, NULL, NULL, NULL));
5824:         PetscCall(DMFieldCreateDefaultFaceQuadrature(coordField, chunkISN, &affineQuadN));
5825:         PetscCall(PetscQuadratureSetData(affineQuadN, dim + 1, PETSC_DECIDE, PETSC_DECIDE, NULL, NULL));
5826:       }
5827:       if (affineQuadN) PetscCall(DMSNESGetFEGeom(coordField, chunkISN, affineQuadN, PETSC_FEGEOM_BASIC, &affineGeomN));
5828:     } else {
5829:       for (f = 0; f < Nf; ++f) {
5830:         if (quadsF[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkISF, quadsF[f], PETSC_FEGEOM_COHESIVE, &geomsF[f]));
5831:         if (quadsN[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkISN, quadsN[f], PETSC_FEGEOM_BASIC, &geomsN[f]));
5832:       }
5833:     }
5834:     /* Loop over fields */
5835:     for (f = 0; f < Nf; ++f) {
5836:       PetscFE         fe;
5837:       PetscFEGeom    *geomF      = affineGeomF ? affineGeomF : geomsF[f];
5838:       PetscFEGeom    *chunkGeomF = NULL, *remGeomF = NULL;
5839:       PetscFEGeom    *geomN      = affineGeomN ? affineGeomN : geomsN[f];
5840:       PetscFEGeom    *chunkGeomN = NULL, *remGeomN = NULL;
5841:       PetscQuadrature quadF = affineQuadF ? affineQuadF : quadsF[f];
5842:       PetscInt        numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb;
5843:       PetscBool       isCohesiveField;

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

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

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

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

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

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

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

5962:   Not collective

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

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

5982:   Level: developer

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

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

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

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

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

6041:       /* TODO: Special cases of ISIntersect where it is quick to check a prior if one is a superset of the other */
6042:       PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS));
6043:       PetscCall(ISDestroy(&pointIS));
6044:       pointIS = isectIS;
6045:     }
6046:     PetscCall(ISGetLocalSize(pointIS, &numFaces));
6047:     PetscCall(ISGetIndices(pointIS, &points));
6048:     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));
6049:     PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
6050:     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom));
6051:     if (!qGeom) {
6052:       PetscFE fe;

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

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

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

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

6150: /*@
6151:   DMPlexComputeBdJacobianSingle - Compute the local boundary Jacobian

6153:   Not collective

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

6167:   Output Parameters:
6168: + Jac  - The local Jacobian
6169: - JacP - The local Jacobian preconditioner

6171:   Level: developer

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

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

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

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

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

6227: /*@
6228:   DMPlexComputeJacobianByKey - Compute the local Jacobian for terms matching the input key

6230:   Collective

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

6242:   Output Parameters:
6243: + Jac  - The local Jacobian
6244: - JacP - The local Jacobian preconditioner

6246:   Level: developer

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

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

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

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

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

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

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

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

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

6466:   Collective

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

6479:   Output Parameters:
6480: + Jac  - the assembled Jacobian matrix
6481: - JacP - the assembled matrix from which the preconditioner is constructed

6483:   Level: developer

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

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

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

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

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

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

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

6696:   if (dmAux) PetscCall(DMDestroy(&plex));
6697:   PetscCallMPI(MPIU_Allreduce(MPI_IN_PLACE, &gassOp, 1, MPI_C_BOOL, MPI_LOR, comm));
6698:   if (hasJac && hasPrec) {
6699:     PetscCall(MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY));
6700:     PetscCall(MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY));
6701:   }
6702: }
6703:   PetscCall(MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY));
6704:   PetscCall(MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY));
6705:   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dmr, 0, 0, 0));
6706:   PetscFunctionReturn(PETSC_SUCCESS);
6707: }

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

6712:   Collective

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

6724:   Output Parameters:
6725: + Jac  - The local Jacobian
6726: - JacP - The local Jacobian preconditioner

6728:   Level: developer

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

6768:   PetscFunctionBegin;
6769:   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
6770:   if (!cellIS) goto end;
6771:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
6772:   PetscCall(ISGetLocalSize(cellIS, &numCells));
6773:   if (cStart >= cEnd) goto end;
6774:   if ((key[0].label == key[1].label) && (key[0].value == key[1].value) && (key[0].part == key[1].part)) {
6775:     const char *name;
6776:     PetscCall(PetscObjectGetName((PetscObject)key[0].label, &name));
6777:     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);
6778:   }
6779:   PetscCall(DMConvert(dm, DMPLEX, &plex));
6780:   PetscCall(DMGetLocalSection(dm, &section));
6781:   PetscCall(DMGetGlobalSection(dm, &globalSection));
6782:   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
6783:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn));
6784:   PetscCall(PetscDSGetNumFields(ds, &Nf));
6785:   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
6786:   PetscCall(PetscDSGetTotalDimension(dsIn, &totDimIn));
6787:   PetscCall(PetscDSHasBdJacobian(ds, &hasBdJac));
6788:   PetscCall(PetscDSHasBdJacobianPreconditioner(ds, &hasBdPrec));
6789:   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, key[2].value, key[2].part, &locA[2]));
6790:   if (locA[2]) {
6791:     const PetscInt cellStart = cells ? cells[cStart] : cStart;

6793:     PetscCall(VecGetDM(locA[2], &dmAux[2]));
6794:     PetscCall(DMConvert(dmAux[2], DMPLEX, &plexA));
6795:     PetscCall(DMGetLocalSection(dmAux[2], &sectionAux[2]));
6796:     PetscCall(DMGetCellDS(dmAux[2], cellStart, &dsAux[2], NULL));
6797:     PetscCall(PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]));
6798:     {
6799:       const PetscInt *cone;
6800:       PetscInt        c;

6802:       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
6803:       for (c = 0; c < 2; ++c) {
6804:         const PetscInt *support;
6805:         PetscInt        ssize, s;

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

6828:     PetscCall(VecGetDM(locS[2], &dmScale[2]));
6829:     PetscCall(DMGetCellDS(dmScale[2], cells ? cells[cStart] : cStart, &dsScale[2], NULL));
6830:     PetscCall(PetscDSGetTotalDimension(dsScale[2], &totDimScale[2]));
6831:     // BRAD: This is not set correctly
6832:     key[2].field = 2;
6833:     PetscCall(PetscDSGetFieldSize(ds, key[2].field, &Nb));
6834:     PetscCall(PetscDSGetFieldSize(dsScale[2], 0, &Nbs));
6835:     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);
6836:     {
6837:       const PetscInt *cone;

6839:       locS[1] = locS[0] = locS[2];
6840:       dmScale[1] = dmScale[0] = dmScale[2];
6841:       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
6842:       for (PetscInt c = 0; c < 2; ++c) {
6843:         const PetscInt *support;
6844:         PetscInt        ssize, s;

6846:         PetscCall(DMPlexGetSupport(dm, cone[c], &support));
6847:         PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
6848:         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);
6849:         if (support[0] == cellStart) s = 1;
6850:         else if (support[1] == cellStart) s = 0;
6851:         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
6852:         PetscCall(DMGetCellDS(dmScale[c], support[s], &dsScale[c], NULL));
6853:         PetscCall(PetscDSGetTotalDimension(dsScale[c], &totDimScale[c]));
6854:       }
6855:     }
6856:   }
6857:   /* 2: Setup geometric data */
6858:   PetscCall(DMGetCoordinateField(dm, &coordField));
6859:   PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
6860:   if (maxDegree > 1) {
6861:     PetscCall(PetscCalloc2(Nf, &quadsF, Nf, &geomsF));
6862:     for (PetscInt f = 0; f < Nf; ++f) {
6863:       PetscFE fe;

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

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

6932:     for (fieldI = 0; fieldI < Nf; ++fieldI) {
6933:       PetscFE         feI;
6934:       PetscFEGeom    *geomF      = affineGeomF ? affineGeomF : geomsF[fieldI];
6935:       PetscFEGeom    *chunkGeomF = NULL, *remGeomF = NULL;
6936:       PetscFEGeom    *geomN      = affineGeomN ? affineGeomN : geomsF[fieldI];
6937:       PetscFEGeom    *chunkGeomN = NULL, *remGeomN = NULL;
6938:       PetscQuadrature quadF = affineQuadF ? affineQuadF : quadsF[fieldI];
6939:       PetscInt        numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb;
6940:       PetscBool       isCohesiveField;

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

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

6998:       /* Scale element values */
6999:       if (locS[0]) {
7000:         PetscInt  Nb, soff = cind * totDimScale[0], off = 0;
7001:         PetscBool cohesive;

7003:         for (fieldI = 0; fieldI < Nf; ++fieldI) {
7004:           PetscCall(PetscDSGetFieldSize(ds, fieldI, &Nb));
7005:           PetscCall(PetscDSGetCohesive(ds, fieldI, &cohesive));

7007:           if (fieldI == key[2].field) {
7008:             PetscCheck(cohesive, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Scaling should not happen for face fields");
7009:             for (PetscInt i = 0; i < Nb; ++i) {
7010:               for (PetscInt 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];
7011:               if (hasBdPrec)
7012:                 for (PetscInt 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];
7013:             }
7014:             off += Nb;
7015:           } else {
7016:             const PetscInt N = cohesive ? Nb : Nb * 2;

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

7075: /*@
7076:   DMPlexComputeJacobianActionByKey - Compute the local Jacobian for terms matching the input key

7078:   Collective

7080:   Input Parameters:
7081: + dm       - The output `DM`
7082: . key      - The `PetscFormKey` indicating what should be integrated
7083: . cellIS   - The `IS` give a set of cells to integrate over
7084: . t        - The time
7085: . X_tShift - The multiplier for the Jacobian with respect to $X_t$
7086: . locX     - The local solution
7087: . locX_t   - The time derivative of the local solution, or `NULL` for time-independent problems
7088: . locY     - The local vector acted on by J
7089: - ctx      - An optional application context, passed to the pointwise functions

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

7094:   Level: developer

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

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

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

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

7222:     PetscCall(PetscBLASIntCast(totDim, &M));
7223:     PetscCallBLAS("BLASgemv", BLASgemv_("N", &M, &M, &a, &elemMat[cind * totDim * totDim], &M, &y[cind * totDim], &one, &b, z, &one));
7224:     if (mesh->printFEM > 1) {
7225:       PetscCall(DMPrintCellMatrix(c, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
7226:       PetscCall(DMPrintCellVector(c, "Y", totDim, &y[cind * totDim]));
7227:       PetscCall(DMPrintCellVector(c, "Z", totDim, z));
7228:     }
7229:     PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, z, ADD_VALUES));
7230:   }
7231:   PetscCall(PetscFree6(u, u_t, elemMat, elemMatD, y, z));
7232:   if (mesh->printFEM) {
7233:     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)locF), "Z:\n"));
7234:     PetscCall(VecView(locF, NULL));
7235:   }
7236:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
7237:   PetscCall(PetscFree(a));
7238:   PetscCall(DMDestroy(&plexAux));
7239:   PetscCall(DMDestroy(&plex));
7240:   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
7241:   PetscFunctionReturn(PETSC_SUCCESS);
7242: }

7244: 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[])
7245: {
7246:   f0[0] = u[0];
7247: }

7249: 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[])
7250: {
7251:   f0[0] = x[(int)PetscRealPart(constants[0])] * u[0];
7252: }

7254: 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[])
7255: {
7256:   f0[0] = 0.0;
7257:   for (PetscInt d = 0; d < dim; ++d) f0[0] += PetscSqr(x[d]) * u[0];
7258: }

7260: /*@
7261:   DMPlexComputeMoments - Compute the first three moments for a field

7263:   Noncollective

7265:   Input Parameters:
7266: + dm - the `DMPLEX`
7267: - u  - the field

7269:   Output Parameter:
7270: . moments - the field moments

7272:   Level: intermediate

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

7277: .seealso: `DM`, `DMPLEX`, `DMSwarmComputeMoments()`
7278: @*/
7279: PetscErrorCode DMPlexComputeMoments(DM dm, Vec u, PetscReal moments[])
7280: {
7281:   PetscDS            ds;
7282:   PetscScalar        mom, constants[1];
7283:   const PetscScalar *oldConstants;
7284:   PetscInt           cdim, Nf, field = 0, Ncon;
7285:   MPI_Comm           comm;
7286:   void              *ctx;

7288:   PetscFunctionBeginUser;
7289:   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
7290:   PetscCall(DMGetCoordinateDim(dm, &cdim));
7291:   PetscCall(DMGetApplicationContext(dm, &ctx));
7292:   PetscCall(DMGetDS(dm, &ds));
7293:   PetscCall(PetscDSGetNumFields(ds, &Nf));
7294:   PetscCall(PetscDSGetConstants(ds, &Ncon, &oldConstants));
7295:   PetscCheck(Nf == 1, comm, PETSC_ERR_ARG_WRONG, "We currently only support 1 field, not %" PetscInt_FMT, Nf);
7296:   PetscCall(PetscDSSetObjective(ds, field, &f0_1));
7297:   PetscCall(DMPlexComputeIntegralFEM(dm, u, &mom, ctx));
7298:   moments[0] = PetscRealPart(mom);
7299:   for (PetscInt c = 0; c < cdim; ++c) {
7300:     constants[0] = c;
7301:     PetscCall(PetscDSSetConstants(ds, 1, constants));
7302:     PetscCall(PetscDSSetObjective(ds, field, &f0_x));
7303:     PetscCall(DMPlexComputeIntegralFEM(dm, u, &mom, ctx));
7304:     moments[c + 1] = PetscRealPart(mom);
7305:   }
7306:   PetscCall(PetscDSSetObjective(ds, field, &f0_x2));
7307:   PetscCall(DMPlexComputeIntegralFEM(dm, u, &mom, ctx));
7308:   moments[cdim + 1] = PetscRealPart(mom);
7309:   PetscCall(PetscDSSetConstants(ds, Ncon, (PetscScalar *)oldConstants));
7310:   PetscFunctionReturn(PETSC_SUCCESS);
7311: }