Actual source code: plexfem.c

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

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

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

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

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

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

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

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

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

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

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

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

 91:   Not Collective

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

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

100:   Level: advanced

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

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

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

118:   Not Collective

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

125:   Level: advanced

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

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

139: static PetscErrorCode DMPlexProjectRigidBody_Private(PetscInt dim, PetscReal t, const PetscReal X[], PetscInt Nc, PetscScalar *mode, void *ctx)
140: {
141:   const PetscInt eps[3][3][3] = {
142:     {{0, 0, 0},  {0, 0, 1},  {0, -1, 0}},
143:     {{0, 0, -1}, {0, 0, 0},  {1, 0, 0} },
144:     {{0, 1, 0},  {-1, 0, 0}, {0, 0, 0} }
145:   };
146:   PetscInt *ctxInt = (PetscInt *)ctx;
147:   PetscInt  dim2   = ctxInt[0];
148:   PetscInt  d      = ctxInt[1];
149:   PetscInt  i, j, k = dim > 2 ? d - dim : d;

151:   PetscFunctionBegin;
152:   PetscCheck(dim == dim2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Input dimension %" PetscInt_FMT " does not match context dimension %" PetscInt_FMT, dim, dim2);
153:   for (i = 0; i < dim; i++) mode[i] = 0.;
154:   if (d < dim) {
155:     mode[d] = 1.; /* Translation along axis d */
156:   } else {
157:     for (i = 0; i < dim; i++) {
158:       for (j = 0; j < dim; j++) { mode[j] += eps[i][j][k] * X[i]; /* Rotation about axis d */ }
159:     }
160:   }
161:   PetscFunctionReturn(PETSC_SUCCESS);
162: }

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

167:   Collective

169:   Input Parameters:
170: + dm - the `DM`
171: - field - The field number for the rigid body space, or 0 for the default

173:   Output Parameter:
174: . sp - the null space

176:   Level: advanced

178:   Note:
179:   This is necessary to provide a suitable coarse space for algebraic multigrid

181: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `MatNullSpaceCreate()`, `PCGAMG`
182: @*/
183: PetscErrorCode DMPlexCreateRigidBody(DM dm, PetscInt field, MatNullSpace *sp)
184: {
185:   PetscErrorCode (**func)(PetscInt, PetscReal, const PetscReal *, PetscInt, PetscScalar *, void *);
186:   MPI_Comm     comm;
187:   Vec          mode[6];
188:   PetscSection section, globalSection;
189:   PetscInt     dim, dimEmbed, Nf, n, m, mmin, d, i, j;
190:   void       **ctxs;

192:   PetscFunctionBegin;
193:   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
194:   PetscCall(DMGetDimension(dm, &dim));
195:   PetscCall(DMGetCoordinateDim(dm, &dimEmbed));
196:   PetscCall(DMGetNumFields(dm, &Nf));
197:   PetscCheck(!Nf || !(field < 0 || field >= Nf), comm, PETSC_ERR_ARG_OUTOFRANGE, "Field %" PetscInt_FMT " is not in [0, %" PetscInt_FMT ")", field, Nf);
198:   if (dim == 1 && Nf < 2) {
199:     PetscCall(MatNullSpaceCreate(comm, PETSC_TRUE, 0, NULL, sp));
200:     PetscFunctionReturn(PETSC_SUCCESS);
201:   }
202:   PetscCall(DMGetLocalSection(dm, &section));
203:   PetscCall(DMGetGlobalSection(dm, &globalSection));
204:   PetscCall(PetscSectionGetConstrainedStorageSize(globalSection, &n));
205:   PetscCall(PetscCalloc2(Nf, &func, Nf, &ctxs));
206:   m = (dim * (dim + 1)) / 2;
207:   PetscCall(VecCreate(comm, &mode[0]));
208:   PetscCall(VecSetType(mode[0], dm->vectype));
209:   PetscCall(VecSetSizes(mode[0], n, PETSC_DETERMINE));
210:   PetscCall(VecSetUp(mode[0]));
211:   PetscCall(VecGetSize(mode[0], &n));
212:   mmin        = PetscMin(m, n);
213:   func[field] = DMPlexProjectRigidBody_Private;
214:   for (i = 1; i < m; ++i) PetscCall(VecDuplicate(mode[0], &mode[i]));
215:   for (d = 0; d < m; d++) {
216:     PetscInt ctx[2];

218:     ctxs[field] = (void *)(&ctx[0]);
219:     ctx[0]      = dimEmbed;
220:     ctx[1]      = d;
221:     PetscCall(DMProjectFunction(dm, 0.0, func, ctxs, INSERT_VALUES, mode[d]));
222:   }
223:   /* Orthonormalize system */
224:   for (i = 0; i < mmin; ++i) {
225:     PetscScalar dots[6];

227:     PetscCall(VecNormalize(mode[i], NULL));
228:     PetscCall(VecMDot(mode[i], mmin - i - 1, mode + i + 1, dots + i + 1));
229:     for (j = i + 1; j < mmin; ++j) {
230:       dots[j] *= -1.0;
231:       PetscCall(VecAXPY(mode[j], dots[j], mode[i]));
232:     }
233:   }
234:   PetscCall(MatNullSpaceCreate(comm, PETSC_FALSE, mmin, mode, sp));
235:   for (i = 0; i < m; ++i) PetscCall(VecDestroy(&mode[i]));
236:   PetscCall(PetscFree2(func, ctxs));
237:   PetscFunctionReturn(PETSC_SUCCESS);
238: }

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

243:   Collective

245:   Input Parameters:
246: + dm    - the `DM`
247: . nb    - The number of bodies
248: . label - The `DMLabel` marking each domain
249: . nids  - The number of ids per body
250: - ids   - An array of the label ids in sequence for each domain

252:   Output Parameter:
253: . sp - the null space

255:   Level: advanced

257:   Note:
258:   This is necessary to provide a suitable coarse space for algebraic multigrid

260: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `MatNullSpaceCreate()`
261: @*/
262: PetscErrorCode DMPlexCreateRigidBodies(DM dm, PetscInt nb, DMLabel label, const PetscInt nids[], const PetscInt ids[], MatNullSpace *sp)
263: {
264:   MPI_Comm     comm;
265:   PetscSection section, globalSection;
266:   Vec         *mode;
267:   PetscScalar *dots;
268:   PetscInt     dim, dimEmbed, n, m, b, d, i, j, off;

270:   PetscFunctionBegin;
271:   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
272:   PetscCall(DMGetDimension(dm, &dim));
273:   PetscCall(DMGetCoordinateDim(dm, &dimEmbed));
274:   PetscCall(DMGetLocalSection(dm, &section));
275:   PetscCall(DMGetGlobalSection(dm, &globalSection));
276:   PetscCall(PetscSectionGetConstrainedStorageSize(globalSection, &n));
277:   m = nb * (dim * (dim + 1)) / 2;
278:   PetscCall(PetscMalloc2(m, &mode, m, &dots));
279:   PetscCall(VecCreate(comm, &mode[0]));
280:   PetscCall(VecSetSizes(mode[0], n, PETSC_DETERMINE));
281:   PetscCall(VecSetUp(mode[0]));
282:   for (i = 1; i < m; ++i) PetscCall(VecDuplicate(mode[0], &mode[i]));
283:   for (b = 0, off = 0; b < nb; ++b) {
284:     for (d = 0; d < m / nb; ++d) {
285:       PetscInt ctx[2];
286:       PetscErrorCode (*func)(PetscInt, PetscReal, const PetscReal *, PetscInt, PetscScalar *, void *) = DMPlexProjectRigidBody_Private;
287:       void *voidctx                                                                                   = (void *)(&ctx[0]);

289:       ctx[0] = dimEmbed;
290:       ctx[1] = d;
291:       PetscCall(DMProjectFunctionLabel(dm, 0.0, label, nids[b], &ids[off], 0, NULL, &func, &voidctx, INSERT_VALUES, mode[d]));
292:       off += nids[b];
293:     }
294:   }
295:   /* Orthonormalize system */
296:   for (i = 0; i < m; ++i) {
297:     PetscScalar dots[6];

299:     PetscCall(VecNormalize(mode[i], NULL));
300:     PetscCall(VecMDot(mode[i], m - i - 1, mode + i + 1, dots + i + 1));
301:     for (j = i + 1; j < m; ++j) {
302:       dots[j] *= -1.0;
303:       PetscCall(VecAXPY(mode[j], dots[j], mode[i]));
304:     }
305:   }
306:   PetscCall(MatNullSpaceCreate(comm, PETSC_FALSE, m, mode, sp));
307:   for (i = 0; i < m; ++i) PetscCall(VecDestroy(&mode[i]));
308:   PetscCall(PetscFree2(mode, dots));
309:   PetscFunctionReturn(PETSC_SUCCESS);
310: }

312: /*@
313:   DMPlexSetMaxProjectionHeight - In DMPlexProjectXXXLocal() functions, the projected values of a basis function's dofs
314:   are computed by associating the basis function with one of the mesh points in its transitively-closed support, and
315:   evaluating the dual space basis of that point.  A basis function is associated with the point in its
316:   transitively-closed support whose mesh height is highest (w.r.t. DAG height), but not greater than the maximum
317:   projection height, which is set with this function.  By default, the maximum projection height is zero, which means
318:   that only mesh cells are used to project basis functions.  A height of one, for example, evaluates a cell-interior
319:   basis functions using its cells dual space basis, but all other basis functions with the dual space basis of a face.

321:   Input Parameters:
322: + dm - the `DMPLEX` object
323: - height - the maximum projection height >= 0

325:   Level: advanced

327: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMaxProjectionHeight()`, `DMProjectFunctionLocal()`, `DMProjectFunctionLabelLocal()`
328: @*/
329: PetscErrorCode DMPlexSetMaxProjectionHeight(DM dm, PetscInt height)
330: {
331:   DM_Plex *plex = (DM_Plex *)dm->data;

333:   PetscFunctionBegin;
335:   plex->maxProjectionHeight = height;
336:   PetscFunctionReturn(PETSC_SUCCESS);
337: }

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

343:   Input Parameter:
344: . dm - the `DMPLEX` object

346:   Output Parameter:
347: . height - the maximum projection height

349:   Level: intermediate

351: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetMaxProjectionHeight()`, `DMProjectFunctionLocal()`, `DMProjectFunctionLabelLocal()`
352: @*/
353: PetscErrorCode DMPlexGetMaxProjectionHeight(DM dm, PetscInt *height)
354: {
355:   DM_Plex *plex = (DM_Plex *)dm->data;

357:   PetscFunctionBegin;
359:   *height = plex->maxProjectionHeight;
360:   PetscFunctionReturn(PETSC_SUCCESS);
361: }

363: typedef struct {
364:   PetscReal    alpha; /* The first Euler angle, and in 2D the only one */
365:   PetscReal    beta;  /* The second Euler angle */
366:   PetscReal    gamma; /* The third Euler angle */
367:   PetscInt     dim;   /* The dimension of R */
368:   PetscScalar *R;     /* The rotation matrix, transforming a vector in the local basis to the global basis */
369:   PetscScalar *RT;    /* The transposed rotation matrix, transforming a vector in the global basis to the local basis */
370: } RotCtx;

372: /*
373:   Note: Following https://en.wikipedia.org/wiki/Euler_angles, we will specify Euler angles by extrinsic rotations, meaning that
374:   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:
375:   $ The XYZ system rotates about the z axis by alpha. The X axis is now at angle alpha with respect to the x axis.
376:   $ 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.
377:   $ The XYZ system rotates a third time about the z axis by gamma.
378: */
379: static PetscErrorCode DMPlexBasisTransformSetUp_Rotation_Internal(DM dm, void *ctx)
380: {
381:   RotCtx   *rc  = (RotCtx *)ctx;
382:   PetscInt  dim = rc->dim;
383:   PetscReal c1, s1, c2, s2, c3, s3;

385:   PetscFunctionBegin;
386:   PetscCall(PetscMalloc2(PetscSqr(dim), &rc->R, PetscSqr(dim), &rc->RT));
387:   switch (dim) {
388:   case 2:
389:     c1       = PetscCosReal(rc->alpha);
390:     s1       = PetscSinReal(rc->alpha);
391:     rc->R[0] = c1;
392:     rc->R[1] = s1;
393:     rc->R[2] = -s1;
394:     rc->R[3] = c1;
395:     PetscCall(PetscArraycpy(rc->RT, rc->R, PetscSqr(dim)));
396:     DMPlex_Transpose2D_Internal(rc->RT);
397:     break;
398:   case 3:
399:     c1       = PetscCosReal(rc->alpha);
400:     s1       = PetscSinReal(rc->alpha);
401:     c2       = PetscCosReal(rc->beta);
402:     s2       = PetscSinReal(rc->beta);
403:     c3       = PetscCosReal(rc->gamma);
404:     s3       = PetscSinReal(rc->gamma);
405:     rc->R[0] = c1 * c3 - c2 * s1 * s3;
406:     rc->R[1] = c3 * s1 + c1 * c2 * s3;
407:     rc->R[2] = s2 * s3;
408:     rc->R[3] = -c1 * s3 - c2 * c3 * s1;
409:     rc->R[4] = c1 * c2 * c3 - s1 * s3;
410:     rc->R[5] = c3 * s2;
411:     rc->R[6] = s1 * s2;
412:     rc->R[7] = -c1 * s2;
413:     rc->R[8] = c2;
414:     PetscCall(PetscArraycpy(rc->RT, rc->R, PetscSqr(dim)));
415:     DMPlex_Transpose3D_Internal(rc->RT);
416:     break;
417:   default:
418:     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Dimension %" PetscInt_FMT " not supported", dim);
419:   }
420:   PetscFunctionReturn(PETSC_SUCCESS);
421: }

423: static PetscErrorCode DMPlexBasisTransformDestroy_Rotation_Internal(DM dm, void *ctx)
424: {
425:   RotCtx *rc = (RotCtx *)ctx;

427:   PetscFunctionBegin;
428:   PetscCall(PetscFree2(rc->R, rc->RT));
429:   PetscCall(PetscFree(rc));
430:   PetscFunctionReturn(PETSC_SUCCESS);
431: }

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

437:   PetscFunctionBeginHot;
439:   if (l2g) {
440:     *A = rc->R;
441:   } else {
442:     *A = rc->RT;
443:   }
444:   PetscFunctionReturn(PETSC_SUCCESS);
445: }

447: PetscErrorCode DMPlexBasisTransformApplyReal_Internal(DM dm, const PetscReal x[], PetscBool l2g, PetscInt dim, const PetscReal *y, PetscReal *z, void *ctx)
448: {
449:   PetscFunctionBegin;
450: #if defined(PETSC_USE_COMPLEX)
451:   switch (dim) {
452:   case 2: {
453:     PetscScalar yt[2] = {y[0], y[1]}, zt[2] = {0.0, 0.0};

455:     PetscCall(DMPlexBasisTransformApply_Internal(dm, x, l2g, dim, yt, zt, ctx));
456:     z[0] = PetscRealPart(zt[0]);
457:     z[1] = PetscRealPart(zt[1]);
458:   } break;
459:   case 3: {
460:     PetscScalar yt[3] = {y[0], y[1], y[2]}, zt[3] = {0.0, 0.0, 0.0};

462:     PetscCall(DMPlexBasisTransformApply_Internal(dm, x, l2g, dim, yt, zt, ctx));
463:     z[0] = PetscRealPart(zt[0]);
464:     z[1] = PetscRealPart(zt[1]);
465:     z[2] = PetscRealPart(zt[2]);
466:   } break;
467:   }
468: #else
469:   PetscCall(DMPlexBasisTransformApply_Internal(dm, x, l2g, dim, y, z, ctx));
470: #endif
471:   PetscFunctionReturn(PETSC_SUCCESS);
472: }

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

478:   PetscFunctionBeginHot;
479:   PetscCall((*dm->transformGetMatrix)(dm, x, l2g, &A, ctx));
480:   switch (dim) {
481:   case 2:
482:     DMPlex_Mult2D_Internal(A, 1, y, z);
483:     break;
484:   case 3:
485:     DMPlex_Mult3D_Internal(A, 1, y, z);
486:     break;
487:   }
488:   PetscFunctionReturn(PETSC_SUCCESS);
489: }

491: static PetscErrorCode DMPlexBasisTransformField_Internal(DM dm, DM tdm, Vec tv, PetscInt p, PetscInt f, PetscBool l2g, PetscScalar *a)
492: {
493:   PetscSection       ts;
494:   const PetscScalar *ta, *tva;
495:   PetscInt           dof;

497:   PetscFunctionBeginHot;
498:   PetscCall(DMGetLocalSection(tdm, &ts));
499:   PetscCall(PetscSectionGetFieldDof(ts, p, f, &dof));
500:   PetscCall(VecGetArrayRead(tv, &ta));
501:   PetscCall(DMPlexPointLocalFieldRead(tdm, p, f, ta, &tva));
502:   if (l2g) {
503:     switch (dof) {
504:     case 4:
505:       DMPlex_Mult2D_Internal(tva, 1, a, a);
506:       break;
507:     case 9:
508:       DMPlex_Mult3D_Internal(tva, 1, a, a);
509:       break;
510:     }
511:   } else {
512:     switch (dof) {
513:     case 4:
514:       DMPlex_MultTranspose2D_Internal(tva, 1, a, a);
515:       break;
516:     case 9:
517:       DMPlex_MultTranspose3D_Internal(tva, 1, a, a);
518:       break;
519:     }
520:   }
521:   PetscCall(VecRestoreArrayRead(tv, &ta));
522:   PetscFunctionReturn(PETSC_SUCCESS);
523: }

525: static PetscErrorCode DMPlexBasisTransformFieldTensor_Internal(DM dm, DM tdm, Vec tv, PetscInt pf, PetscInt f, PetscInt pg, PetscInt g, PetscBool l2g, PetscInt lda, PetscScalar *a)
526: {
527:   PetscSection       s, ts;
528:   const PetscScalar *ta, *tvaf, *tvag;
529:   PetscInt           fdof, gdof, fpdof, gpdof;

531:   PetscFunctionBeginHot;
532:   PetscCall(DMGetLocalSection(dm, &s));
533:   PetscCall(DMGetLocalSection(tdm, &ts));
534:   PetscCall(PetscSectionGetFieldDof(s, pf, f, &fpdof));
535:   PetscCall(PetscSectionGetFieldDof(s, pg, g, &gpdof));
536:   PetscCall(PetscSectionGetFieldDof(ts, pf, f, &fdof));
537:   PetscCall(PetscSectionGetFieldDof(ts, pg, g, &gdof));
538:   PetscCall(VecGetArrayRead(tv, &ta));
539:   PetscCall(DMPlexPointLocalFieldRead(tdm, pf, f, ta, &tvaf));
540:   PetscCall(DMPlexPointLocalFieldRead(tdm, pg, g, ta, &tvag));
541:   if (l2g) {
542:     switch (fdof) {
543:     case 4:
544:       DMPlex_MatMult2D_Internal(tvaf, gpdof, lda, a, a);
545:       break;
546:     case 9:
547:       DMPlex_MatMult3D_Internal(tvaf, gpdof, lda, a, a);
548:       break;
549:     }
550:     switch (gdof) {
551:     case 4:
552:       DMPlex_MatMultTransposeLeft2D_Internal(tvag, fpdof, lda, a, a);
553:       break;
554:     case 9:
555:       DMPlex_MatMultTransposeLeft3D_Internal(tvag, fpdof, lda, a, a);
556:       break;
557:     }
558:   } else {
559:     switch (fdof) {
560:     case 4:
561:       DMPlex_MatMultTranspose2D_Internal(tvaf, gpdof, lda, a, a);
562:       break;
563:     case 9:
564:       DMPlex_MatMultTranspose3D_Internal(tvaf, gpdof, lda, a, a);
565:       break;
566:     }
567:     switch (gdof) {
568:     case 4:
569:       DMPlex_MatMultLeft2D_Internal(tvag, fpdof, lda, a, a);
570:       break;
571:     case 9:
572:       DMPlex_MatMultLeft3D_Internal(tvag, fpdof, lda, a, a);
573:       break;
574:     }
575:   }
576:   PetscCall(VecRestoreArrayRead(tv, &ta));
577:   PetscFunctionReturn(PETSC_SUCCESS);
578: }

580: PetscErrorCode DMPlexBasisTransformPoint_Internal(DM dm, DM tdm, Vec tv, PetscInt p, PetscBool fieldActive[], PetscBool l2g, PetscScalar *a)
581: {
582:   PetscSection    s;
583:   PetscSection    clSection;
584:   IS              clPoints;
585:   const PetscInt *clp;
586:   PetscInt       *points = NULL;
587:   PetscInt        Nf, f, Np, cp, dof, d = 0;

589:   PetscFunctionBegin;
590:   PetscCall(DMGetLocalSection(dm, &s));
591:   PetscCall(PetscSectionGetNumFields(s, &Nf));
592:   PetscCall(DMPlexGetCompressedClosure(dm, s, p, 0, &Np, &points, &clSection, &clPoints, &clp));
593:   for (f = 0; f < Nf; ++f) {
594:     for (cp = 0; cp < Np * 2; cp += 2) {
595:       PetscCall(PetscSectionGetFieldDof(s, points[cp], f, &dof));
596:       if (!dof) continue;
597:       if (fieldActive[f]) PetscCall(DMPlexBasisTransformField_Internal(dm, tdm, tv, points[cp], f, l2g, &a[d]));
598:       d += dof;
599:     }
600:   }
601:   PetscCall(DMPlexRestoreCompressedClosure(dm, s, p, &Np, &points, &clSection, &clPoints, &clp));
602:   PetscFunctionReturn(PETSC_SUCCESS);
603: }

605: PetscErrorCode DMPlexBasisTransformPointTensor_Internal(DM dm, DM tdm, Vec tv, PetscInt p, PetscBool l2g, PetscInt lda, PetscScalar *a)
606: {
607:   PetscSection    s;
608:   PetscSection    clSection;
609:   IS              clPoints;
610:   const PetscInt *clp;
611:   PetscInt       *points = NULL;
612:   PetscInt        Nf, f, g, Np, cpf, cpg, fdof, gdof, r, c = 0;

614:   PetscFunctionBegin;
615:   PetscCall(DMGetLocalSection(dm, &s));
616:   PetscCall(PetscSectionGetNumFields(s, &Nf));
617:   PetscCall(DMPlexGetCompressedClosure(dm, s, p, 0, &Np, &points, &clSection, &clPoints, &clp));
618:   for (f = 0, r = 0; f < Nf; ++f) {
619:     for (cpf = 0; cpf < Np * 2; cpf += 2) {
620:       PetscCall(PetscSectionGetFieldDof(s, points[cpf], f, &fdof));
621:       for (g = 0, c = 0; g < Nf; ++g) {
622:         for (cpg = 0; cpg < Np * 2; cpg += 2) {
623:           PetscCall(PetscSectionGetFieldDof(s, points[cpg], g, &gdof));
624:           PetscCall(DMPlexBasisTransformFieldTensor_Internal(dm, tdm, tv, points[cpf], f, points[cpg], g, l2g, lda, &a[r * lda + c]));
625:           c += gdof;
626:         }
627:       }
628:       PetscCheck(c == lda, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid number of columns %" PetscInt_FMT " should be %" PetscInt_FMT, c, lda);
629:       r += fdof;
630:     }
631:   }
632:   PetscCheck(r == lda, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid number of rows %" PetscInt_FMT " should be %" PetscInt_FMT, c, lda);
633:   PetscCall(DMPlexRestoreCompressedClosure(dm, s, p, &Np, &points, &clSection, &clPoints, &clp));
634:   PetscFunctionReturn(PETSC_SUCCESS);
635: }

637: static PetscErrorCode DMPlexBasisTransform_Internal(DM dm, Vec lv, PetscBool l2g)
638: {
639:   DM                 tdm;
640:   Vec                tv;
641:   PetscSection       ts, s;
642:   const PetscScalar *ta;
643:   PetscScalar       *a, *va;
644:   PetscInt           pStart, pEnd, p, Nf, f;

646:   PetscFunctionBegin;
647:   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
648:   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
649:   PetscCall(DMGetLocalSection(tdm, &ts));
650:   PetscCall(DMGetLocalSection(dm, &s));
651:   PetscCall(PetscSectionGetChart(s, &pStart, &pEnd));
652:   PetscCall(PetscSectionGetNumFields(s, &Nf));
653:   PetscCall(VecGetArray(lv, &a));
654:   PetscCall(VecGetArrayRead(tv, &ta));
655:   for (p = pStart; p < pEnd; ++p) {
656:     for (f = 0; f < Nf; ++f) {
657:       PetscCall(DMPlexPointLocalFieldRef(dm, p, f, a, &va));
658:       PetscCall(DMPlexBasisTransformField_Internal(dm, tdm, tv, p, f, l2g, va));
659:     }
660:   }
661:   PetscCall(VecRestoreArray(lv, &a));
662:   PetscCall(VecRestoreArrayRead(tv, &ta));
663:   PetscFunctionReturn(PETSC_SUCCESS);
664: }

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

669:   Input Parameters:
670: + dm - The `DM`
671: - lv - A local vector with values in the global basis

673:   Output Parameter:
674: . lv - A local vector with values in the local basis

676:   Level: developer

678:   Note:
679:   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.

681: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexLocalToGlobalBasis()`, `DMGetLocalSection()`, `DMPlexCreateBasisRotation()`
682: @*/
683: PetscErrorCode DMPlexGlobalToLocalBasis(DM dm, Vec lv)
684: {
685:   PetscFunctionBegin;
688:   PetscCall(DMPlexBasisTransform_Internal(dm, lv, PETSC_FALSE));
689:   PetscFunctionReturn(PETSC_SUCCESS);
690: }

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

695:   Input Parameters:
696: + dm - The `DM`
697: - lv - A local vector with values in the local basis

699:   Output Parameter:
700: . lv - A local vector with values in the global basis

702:   Level: developer

704:   Note:
705:   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.

707: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGlobalToLocalBasis()`, `DMGetLocalSection()`, `DMPlexCreateBasisRotation()`
708: @*/
709: PetscErrorCode DMPlexLocalToGlobalBasis(DM dm, Vec lv)
710: {
711:   PetscFunctionBegin;
714:   PetscCall(DMPlexBasisTransform_Internal(dm, lv, PETSC_TRUE));
715:   PetscFunctionReturn(PETSC_SUCCESS);
716: }

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

722:   Input Parameters:
723: + dm    - The `DM`
724: . alpha - The first Euler angle, and in 2D the only one
725: . beta  - The second Euler angle
726: - gamma - The third Euler angle

728:   Level: developer

730:   Note:
731:   Following https://en.wikipedia.org/wiki/Euler_angles, we will specify Euler angles by extrinsic rotations, meaning that
732:   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
733: .vb
734:    The XYZ system rotates about the z axis by alpha. The X axis is now at angle alpha with respect to the x axis.
735:    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.
736:    The XYZ system rotates a third time about the z axis by gamma.
737: .ve

739: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGlobalToLocalBasis()`, `DMPlexLocalToGlobalBasis()`
740: @*/
741: PetscErrorCode DMPlexCreateBasisRotation(DM dm, PetscReal alpha, PetscReal beta, PetscReal gamma)
742: {
743:   RotCtx  *rc;
744:   PetscInt cdim;

746:   PetscFunctionBegin;
747:   PetscCall(DMGetCoordinateDim(dm, &cdim));
748:   PetscCall(PetscMalloc1(1, &rc));
749:   dm->transformCtx       = rc;
750:   dm->transformSetUp     = DMPlexBasisTransformSetUp_Rotation_Internal;
751:   dm->transformDestroy   = DMPlexBasisTransformDestroy_Rotation_Internal;
752:   dm->transformGetMatrix = DMPlexBasisTransformGetMatrix_Rotation_Internal;
753:   rc->dim                = cdim;
754:   rc->alpha              = alpha;
755:   rc->beta               = beta;
756:   rc->gamma              = gamma;
757:   PetscCall((*dm->transformSetUp)(dm, dm->transformCtx));
758:   PetscCall(DMConstructBasisTransform_Internal(dm));
759:   PetscFunctionReturn(PETSC_SUCCESS);
760: }

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

765:   Input Parameters:
766: + dm     - The `DM`, with a `PetscDS` that matches the problem being constrained
767: . time   - The time
768: . field  - The field to constrain
769: . Nc     - The number of constrained field components, or 0 for all components
770: . comps  - An array of constrained component numbers, or `NULL` for all components
771: . label  - The `DMLabel` defining constrained points
772: . numids - The number of `DMLabel` ids for constrained points
773: . ids    - An array of ids for constrained points
774: . func   - A pointwise function giving boundary values
775: - ctx    - An optional user context for bcFunc

777:   Output Parameter:
778: . locX   - A local vector to receives the boundary values

780:   Level: developer

782: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLabel`, `DMPlexInsertBoundaryValuesEssentialField()`, `DMPlexInsertBoundaryValuesEssentialBdField()`, `DMAddBoundary()`
783: @*/
784: PetscErrorCode DMPlexInsertBoundaryValuesEssential(DM dm, PetscReal time, PetscInt field, PetscInt Nc, const PetscInt comps[], DMLabel label, PetscInt numids, const PetscInt ids[], PetscErrorCode (*func)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void *ctx, Vec locX)
785: {
786:   PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal x[], PetscInt, PetscScalar *u, void *ctx);
787:   void   **ctxs;
788:   PetscInt numFields;

790:   PetscFunctionBegin;
791:   PetscCall(DMGetNumFields(dm, &numFields));
792:   PetscCall(PetscCalloc2(numFields, &funcs, numFields, &ctxs));
793:   funcs[field] = func;
794:   ctxs[field]  = ctx;
795:   PetscCall(DMProjectFunctionLabelLocal(dm, time, label, numids, ids, Nc, comps, funcs, ctxs, INSERT_BC_VALUES, locX));
796:   PetscCall(PetscFree2(funcs, ctxs));
797:   PetscFunctionReturn(PETSC_SUCCESS);
798: }

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

803:   Input Parameters:
804: + dm     - The `DM`, with a `PetscDS` that matches the problem being constrained
805: . time   - The time
806: . locU   - A local vector with the input solution values
807: . field  - The field to constrain
808: . Nc     - The number of constrained field components, or 0 for all components
809: . comps  - An array of constrained component numbers, or `NULL` for all components
810: . label  - The `DMLabel` defining constrained points
811: . numids - The number of `DMLabel` ids for constrained points
812: . ids    - An array of ids for constrained points
813: . func   - A pointwise function giving boundary values
814: - ctx    - An optional user context for bcFunc

816:   Output Parameter:
817: . locX   - A local vector to receives the boundary values

819:   Level: developer

821: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexInsertBoundaryValuesEssential()`, `DMPlexInsertBoundaryValuesEssentialBdField()`, `DMAddBoundary()`
822: @*/
823: PetscErrorCode DMPlexInsertBoundaryValuesEssentialField(DM dm, PetscReal time, Vec locU, PetscInt field, PetscInt Nc, const PetscInt comps[], DMLabel label, PetscInt numids, const PetscInt ids[], void (*func)(PetscInt, PetscInt, PetscInt, const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], PetscReal, const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]), void *ctx, Vec locX)
824: {
825:   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[]);
826:   void   **ctxs;
827:   PetscInt numFields;

829:   PetscFunctionBegin;
830:   PetscCall(DMGetNumFields(dm, &numFields));
831:   PetscCall(PetscCalloc2(numFields, &funcs, numFields, &ctxs));
832:   funcs[field] = func;
833:   ctxs[field]  = ctx;
834:   PetscCall(DMProjectFieldLabelLocal(dm, time, label, numids, ids, Nc, comps, locU, funcs, INSERT_BC_VALUES, locX));
835:   PetscCall(PetscFree2(funcs, ctxs));
836:   PetscFunctionReturn(PETSC_SUCCESS);
837: }

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

842:   Collective

844:   Input Parameters:
845: + dm     - The `DM`, with a `PetscDS` that matches the problem being constrained
846: . time   - The time
847: . locU   - A local vector with the input solution values
848: . field  - The field to constrain
849: . Nc     - The number of constrained field components, or 0 for all components
850: . comps  - An array of constrained component numbers, or `NULL` for all components
851: . label  - The `DMLabel` defining constrained points
852: . numids - The number of `DMLabel` ids for constrained points
853: . ids    - An array of ids for constrained points
854: . func   - A pointwise function giving boundary values, the calling sequence is given in `DMProjectBdFieldLabelLocal()`
855: - ctx    - An optional user context for `func`

857:   Output Parameter:
858: . locX   - A local vector to receive the boundary values

860:   Level: developer

862: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectBdFieldLabelLocal()`, `DMPlexInsertBoundaryValuesEssential()`, `DMPlexInsertBoundaryValuesEssentialField()`, `DMAddBoundary()`
863: @*/
864: PetscErrorCode DMPlexInsertBoundaryValuesEssentialBdField(DM dm, PetscReal time, Vec locU, PetscInt field, PetscInt Nc, const PetscInt comps[], DMLabel label, PetscInt numids, const PetscInt ids[], void (*func)(PetscInt, PetscInt, PetscInt, const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], PetscReal, const PetscReal[], const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]), void *ctx, Vec locX)
865: {
866:   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[]);
867:   void   **ctxs;
868:   PetscInt numFields;

870:   PetscFunctionBegin;
871:   PetscCall(DMGetNumFields(dm, &numFields));
872:   PetscCall(PetscCalloc2(numFields, &funcs, numFields, &ctxs));
873:   funcs[field] = func;
874:   ctxs[field]  = ctx;
875:   PetscCall(DMProjectBdFieldLabelLocal(dm, time, label, numids, ids, Nc, comps, locU, funcs, INSERT_BC_VALUES, locX));
876:   PetscCall(PetscFree2(funcs, ctxs));
877:   PetscFunctionReturn(PETSC_SUCCESS);
878: }

880: /*@C
881:   DMPlexInsertBoundaryValuesRiemann - Insert boundary values into a local vector

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

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

901:   Level: developer

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

906: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexInsertBoundaryValuesEssential()`, `DMPlexInsertBoundaryValuesEssentialField()`, `DMAddBoundary()`
907: @*/
908: PetscErrorCode DMPlexInsertBoundaryValuesRiemann(DM dm, PetscReal time, Vec faceGeometry, Vec cellGeometry, Vec Grad, PetscInt field, PetscInt Nc, const PetscInt comps[], DMLabel label, PetscInt numids, const PetscInt ids[], PetscErrorCode (*func)(PetscReal, const PetscReal *, const PetscReal *, const PetscScalar *, PetscScalar *, void *), void *ctx, Vec locX)
909: {
910:   PetscDS            prob;
911:   PetscSF            sf;
912:   DM                 dmFace, dmCell, dmGrad;
913:   const PetscScalar *facegeom, *cellgeom = NULL, *grad;
914:   const PetscInt    *leaves;
915:   PetscScalar       *x, *fx;
916:   PetscInt           dim, nleaves, loc, fStart, fEnd, pdim, i;
917:   PetscErrorCode     ierru = PETSC_SUCCESS;

919:   PetscFunctionBegin;
920:   PetscCall(DMGetPointSF(dm, &sf));
921:   PetscCall(PetscSFGetGraph(sf, NULL, &nleaves, &leaves, NULL));
922:   nleaves = PetscMax(0, nleaves);
923:   PetscCall(DMGetDimension(dm, &dim));
924:   PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
925:   PetscCall(DMGetDS(dm, &prob));
926:   PetscCall(VecGetDM(faceGeometry, &dmFace));
927:   PetscCall(VecGetArrayRead(faceGeometry, &facegeom));
928:   if (cellGeometry) {
929:     PetscCall(VecGetDM(cellGeometry, &dmCell));
930:     PetscCall(VecGetArrayRead(cellGeometry, &cellgeom));
931:   }
932:   if (Grad) {
933:     PetscFV fv;

935:     PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fv));
936:     PetscCall(VecGetDM(Grad, &dmGrad));
937:     PetscCall(VecGetArrayRead(Grad, &grad));
938:     PetscCall(PetscFVGetNumComponents(fv, &pdim));
939:     PetscCall(DMGetWorkArray(dm, pdim, MPIU_SCALAR, &fx));
940:   }
941:   PetscCall(VecGetArray(locX, &x));
942:   for (i = 0; i < numids; ++i) {
943:     IS              faceIS;
944:     const PetscInt *faces;
945:     PetscInt        numFaces, f;

947:     PetscCall(DMLabelGetStratumIS(label, ids[i], &faceIS));
948:     if (!faceIS) continue; /* No points with that id on this process */
949:     PetscCall(ISGetLocalSize(faceIS, &numFaces));
950:     PetscCall(ISGetIndices(faceIS, &faces));
951:     for (f = 0; f < numFaces; ++f) {
952:       const PetscInt   face = faces[f], *cells;
953:       PetscFVFaceGeom *fg;

955:       if ((face < fStart) || (face >= fEnd)) continue; /* Refinement adds non-faces to labels */
956:       PetscCall(PetscFindInt(face, nleaves, (PetscInt *)leaves, &loc));
957:       if (loc >= 0) continue;
958:       PetscCall(DMPlexPointLocalRead(dmFace, face, facegeom, &fg));
959:       PetscCall(DMPlexGetSupport(dm, face, &cells));
960:       if (Grad) {
961:         PetscFVCellGeom *cg;
962:         PetscScalar     *cx, *cgrad;
963:         PetscScalar     *xG;
964:         PetscReal        dx[3];
965:         PetscInt         d;

967:         PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cg));
968:         PetscCall(DMPlexPointLocalRead(dm, cells[0], x, &cx));
969:         PetscCall(DMPlexPointLocalRead(dmGrad, cells[0], grad, &cgrad));
970:         PetscCall(DMPlexPointLocalFieldRef(dm, cells[1], field, x, &xG));
971:         DMPlex_WaxpyD_Internal(dim, -1, cg->centroid, fg->centroid, dx);
972:         for (d = 0; d < pdim; ++d) fx[d] = cx[d] + DMPlex_DotD_Internal(dim, &cgrad[d * dim], dx);
973:         PetscCall((*func)(time, fg->centroid, fg->normal, fx, xG, ctx));
974:       } else {
975:         PetscScalar *xI;
976:         PetscScalar *xG;

978:         PetscCall(DMPlexPointLocalRead(dm, cells[0], x, &xI));
979:         PetscCall(DMPlexPointLocalFieldRef(dm, cells[1], field, x, &xG));
980:         ierru = (*func)(time, fg->centroid, fg->normal, xI, xG, ctx);
981:         if (ierru) {
982:           PetscCall(ISRestoreIndices(faceIS, &faces));
983:           PetscCall(ISDestroy(&faceIS));
984:           goto cleanup;
985:         }
986:       }
987:     }
988:     PetscCall(ISRestoreIndices(faceIS, &faces));
989:     PetscCall(ISDestroy(&faceIS));
990:   }
991: cleanup:
992:   PetscCall(VecRestoreArray(locX, &x));
993:   if (Grad) {
994:     PetscCall(DMRestoreWorkArray(dm, pdim, MPIU_SCALAR, &fx));
995:     PetscCall(VecRestoreArrayRead(Grad, &grad));
996:   }
997:   if (cellGeometry) PetscCall(VecRestoreArrayRead(cellGeometry, &cellgeom));
998:   PetscCall(VecRestoreArrayRead(faceGeometry, &facegeom));
999:   PetscCall(ierru);
1000:   PetscFunctionReturn(PETSC_SUCCESS);
1001: }

1003: static PetscErrorCode zero(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nc, PetscScalar *u, void *ctx)
1004: {
1005:   PetscInt c;
1006:   for (c = 0; c < Nc; ++c) u[c] = 0.0;
1007:   return PETSC_SUCCESS;
1008: }

1010: PetscErrorCode DMPlexInsertBoundaryValues_Plex(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
1011: {
1012:   PetscObject isZero;
1013:   PetscDS     prob;
1014:   PetscInt    numBd, b;

1016:   PetscFunctionBegin;
1017:   PetscCall(DMGetDS(dm, &prob));
1018:   PetscCall(PetscDSGetNumBoundary(prob, &numBd));
1019:   PetscCall(PetscObjectQuery((PetscObject)locX, "__Vec_bc_zero__", &isZero));
1020:   for (b = 0; b < numBd; ++b) {
1021:     PetscWeakForm           wf;
1022:     DMBoundaryConditionType type;
1023:     const char             *name;
1024:     DMLabel                 label;
1025:     PetscInt                field, Nc;
1026:     const PetscInt         *comps;
1027:     PetscObject             obj;
1028:     PetscClassId            id;
1029:     void (*bvfunc)(void);
1030:     PetscInt        numids;
1031:     const PetscInt *ids;
1032:     void           *ctx;

1034:     PetscCall(PetscDSGetBoundary(prob, b, &wf, &type, &name, &label, &numids, &ids, &field, &Nc, &comps, &bvfunc, NULL, &ctx));
1035:     if (insertEssential != (type & DM_BC_ESSENTIAL)) continue;
1036:     PetscCall(DMGetField(dm, field, NULL, &obj));
1037:     PetscCall(PetscObjectGetClassId(obj, &id));
1038:     if (id == PETSCFE_CLASSID) {
1039:       switch (type) {
1040:         /* for FEM, there is no insertion to be done for non-essential boundary conditions */
1041:       case DM_BC_ESSENTIAL: {
1042:         PetscSimplePointFunc func = (PetscSimplePointFunc)bvfunc;

1044:         if (isZero) func = zero;
1045:         PetscCall(DMPlexLabelAddCells(dm, label));
1046:         PetscCall(DMPlexInsertBoundaryValuesEssential(dm, time, field, Nc, comps, label, numids, ids, func, ctx, locX));
1047:         PetscCall(DMPlexLabelClearCells(dm, label));
1048:       } break;
1049:       case DM_BC_ESSENTIAL_FIELD: {
1050:         PetscPointFunc func = (PetscPointFunc)bvfunc;

1052:         PetscCall(DMPlexLabelAddCells(dm, label));
1053:         PetscCall(DMPlexInsertBoundaryValuesEssentialField(dm, time, locX, field, Nc, comps, label, numids, ids, func, ctx, locX));
1054:         PetscCall(DMPlexLabelClearCells(dm, label));
1055:       } break;
1056:       default:
1057:         break;
1058:       }
1059:     } else if (id == PETSCFV_CLASSID) {
1060:       {
1061:         PetscErrorCode (*func)(PetscReal, const PetscReal *, const PetscReal *, const PetscScalar *, PetscScalar *, void *) = (PetscErrorCode(*)(PetscReal, const PetscReal *, const PetscReal *, const PetscScalar *, PetscScalar *, void *))bvfunc;

1063:         if (!faceGeomFVM) continue;
1064:         PetscCall(DMPlexInsertBoundaryValuesRiemann(dm, time, faceGeomFVM, cellGeomFVM, gradFVM, field, Nc, comps, label, numids, ids, func, ctx, locX));
1065:       }
1066:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1067:   }
1068:   PetscFunctionReturn(PETSC_SUCCESS);
1069: }

1071: PetscErrorCode DMPlexInsertTimeDerivativeBoundaryValues_Plex(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
1072: {
1073:   PetscObject isZero;
1074:   PetscDS     prob;
1075:   PetscInt    numBd, b;

1077:   PetscFunctionBegin;
1078:   if (!locX) PetscFunctionReturn(PETSC_SUCCESS);
1079:   PetscCall(DMGetDS(dm, &prob));
1080:   PetscCall(PetscDSGetNumBoundary(prob, &numBd));
1081:   PetscCall(PetscObjectQuery((PetscObject)locX, "__Vec_bc_zero__", &isZero));
1082:   for (b = 0; b < numBd; ++b) {
1083:     PetscWeakForm           wf;
1084:     DMBoundaryConditionType type;
1085:     const char             *name;
1086:     DMLabel                 label;
1087:     PetscInt                field, Nc;
1088:     const PetscInt         *comps;
1089:     PetscObject             obj;
1090:     PetscClassId            id;
1091:     PetscInt                numids;
1092:     const PetscInt         *ids;
1093:     void (*bvfunc)(void);
1094:     void *ctx;

1096:     PetscCall(PetscDSGetBoundary(prob, b, &wf, &type, &name, &label, &numids, &ids, &field, &Nc, &comps, NULL, &bvfunc, &ctx));
1097:     if (insertEssential != (type & DM_BC_ESSENTIAL)) continue;
1098:     PetscCall(DMGetField(dm, field, NULL, &obj));
1099:     PetscCall(PetscObjectGetClassId(obj, &id));
1100:     if (id == PETSCFE_CLASSID) {
1101:       switch (type) {
1102:         /* for FEM, there is no insertion to be done for non-essential boundary conditions */
1103:       case DM_BC_ESSENTIAL: {
1104:         PetscSimplePointFunc func_t = (PetscSimplePointFunc)bvfunc;

1106:         if (isZero) func_t = zero;
1107:         PetscCall(DMPlexLabelAddCells(dm, label));
1108:         PetscCall(DMPlexInsertBoundaryValuesEssential(dm, time, field, Nc, comps, label, numids, ids, func_t, ctx, locX));
1109:         PetscCall(DMPlexLabelClearCells(dm, label));
1110:       } break;
1111:       case DM_BC_ESSENTIAL_FIELD: {
1112:         PetscPointFunc func_t = (PetscPointFunc)bvfunc;

1114:         PetscCall(DMPlexLabelAddCells(dm, label));
1115:         PetscCall(DMPlexInsertBoundaryValuesEssentialField(dm, time, locX, field, Nc, comps, label, numids, ids, func_t, ctx, locX));
1116:         PetscCall(DMPlexLabelClearCells(dm, label));
1117:       } break;
1118:       default:
1119:         break;
1120:       }
1121:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1122:   }
1123:   PetscFunctionReturn(PETSC_SUCCESS);
1124: }

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

1129:   Not Collective

1131:   Input Parameters:
1132: + dm - The `DM`
1133: . insertEssential - Should I insert essential (e.g. Dirichlet) or inessential (e.g. Neumann) boundary conditions
1134: . time - The time
1135: . faceGeomFVM - Face geometry data for FV discretizations
1136: . cellGeomFVM - Cell geometry data for FV discretizations
1137: - gradFVM - Gradient reconstruction data for FV discretizations

1139:   Output Parameter:
1140: . locX - Solution updated with boundary values

1142:   Level: intermediate

1144: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunctionLabelLocal()`, `DMAddBoundary()`
1145: @*/
1146: PetscErrorCode DMPlexInsertBoundaryValues(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
1147: {
1148:   PetscFunctionBegin;
1154:   PetscTryMethod(dm, "DMPlexInsertBoundaryValues_C", (DM, PetscBool, Vec, PetscReal, Vec, Vec, Vec), (dm, insertEssential, locX, time, faceGeomFVM, cellGeomFVM, gradFVM));
1155:   PetscFunctionReturn(PETSC_SUCCESS);
1156: }

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

1161:   Input Parameters:
1162: + dm - The `DM`
1163: . insertEssential - Should I insert essential (e.g. Dirichlet) or inessential (e.g. Neumann) boundary conditions
1164: . time - The time
1165: . faceGeomFVM - Face geometry data for FV discretizations
1166: . cellGeomFVM - Cell geometry data for FV discretizations
1167: - gradFVM - Gradient reconstruction data for FV discretizations

1169:   Output Parameter:
1170: . locX_t - Solution updated with boundary values

1172:   Level: developer

1174: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunctionLabelLocal()`
1175: @*/
1176: PetscErrorCode DMPlexInsertTimeDerivativeBoundaryValues(DM dm, PetscBool insertEssential, Vec locX_t, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
1177: {
1178:   PetscFunctionBegin;
1184:   PetscTryMethod(dm, "DMPlexInsertTimeDerviativeBoundaryValues_C", (DM, PetscBool, Vec, PetscReal, Vec, Vec, Vec), (dm, insertEssential, locX_t, time, faceGeomFVM, cellGeomFVM, gradFVM));
1185:   PetscFunctionReturn(PETSC_SUCCESS);
1186: }

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

1192:   PetscFunctionBegin;
1193:   PetscCall(DMGetLocalVector(dm, &localX));
1194:   PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, localX, time, NULL, NULL, NULL));
1195:   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
1196:   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
1197:   PetscCall(DMPlexComputeL2DiffLocal(dm, time, funcs, ctxs, localX, diff));
1198:   PetscCall(DMRestoreLocalVector(dm, &localX));
1199:   PetscFunctionReturn(PETSC_SUCCESS);
1200: }

1202: /*@C
1203:   DMComputeL2DiffLocal - This function computes the L_2 difference between a function u and an FEM interpolant solution u_h.

1205:   Collective

1207:   Input Parameters:
1208: + dm     - The `DM`
1209: . time   - The time
1210: . funcs  - The functions to evaluate for each field component
1211: . ctxs   - Optional array of contexts to pass to each function, or `NULL`.
1212: - localX - The coefficient vector u_h, a local vector

1214:   Output Parameter:
1215: . diff - The diff ||u - u_h||_2

1217:   Level: developer

1219: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
1220: @*/
1221: PetscErrorCode DMPlexComputeL2DiffLocal(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec localX, PetscReal *diff)
1222: {
1223:   const PetscInt   debug = ((DM_Plex *)dm->data)->printL2;
1224:   DM               tdm;
1225:   Vec              tv;
1226:   PetscSection     section;
1227:   PetscQuadrature  quad;
1228:   PetscFEGeom      fegeom;
1229:   PetscScalar     *funcVal, *interpolant;
1230:   PetscReal       *coords, *gcoords;
1231:   PetscReal        localDiff = 0.0;
1232:   const PetscReal *quadWeights;
1233:   PetscInt         dim, coordDim, numFields, numComponents = 0, qNc, Nq, cellHeight, cStart, cEnd, c, field, fieldOffset;
1234:   PetscBool        transform;

1236:   PetscFunctionBegin;
1237:   PetscCall(DMGetDimension(dm, &dim));
1238:   PetscCall(DMGetCoordinateDim(dm, &coordDim));
1239:   fegeom.dimEmbed = coordDim;
1240:   PetscCall(DMGetLocalSection(dm, &section));
1241:   PetscCall(PetscSectionGetNumFields(section, &numFields));
1242:   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
1243:   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
1244:   PetscCall(DMHasBasisTransform(dm, &transform));
1245:   PetscCheck(numFields, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fields is zero!");
1246:   for (field = 0; field < numFields; ++field) {
1247:     PetscObject  obj;
1248:     PetscClassId id;
1249:     PetscInt     Nc;

1251:     PetscCall(DMGetField(dm, field, NULL, &obj));
1252:     PetscCall(PetscObjectGetClassId(obj, &id));
1253:     if (id == PETSCFE_CLASSID) {
1254:       PetscFE fe = (PetscFE)obj;

1256:       PetscCall(PetscFEGetQuadrature(fe, &quad));
1257:       PetscCall(PetscFEGetNumComponents(fe, &Nc));
1258:     } else if (id == PETSCFV_CLASSID) {
1259:       PetscFV fv = (PetscFV)obj;

1261:       PetscCall(PetscFVGetQuadrature(fv, &quad));
1262:       PetscCall(PetscFVGetNumComponents(fv, &Nc));
1263:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1264:     numComponents += Nc;
1265:   }
1266:   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, NULL, &quadWeights));
1267:   PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
1268:   PetscCall(PetscMalloc6(numComponents, &funcVal, numComponents, &interpolant, coordDim * (Nq + 1), &coords, Nq, &fegeom.detJ, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ));
1269:   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1270:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
1271:   for (c = cStart; c < cEnd; ++c) {
1272:     PetscScalar *x        = NULL;
1273:     PetscReal    elemDiff = 0.0;
1274:     PetscInt     qc       = 0;

1276:     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1277:     PetscCall(DMPlexVecGetClosure(dm, NULL, localX, c, NULL, &x));

1279:     for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1280:       PetscObject  obj;
1281:       PetscClassId id;
1282:       void *const  ctx = ctxs ? ctxs[field] : NULL;
1283:       PetscInt     Nb, Nc, q, fc;

1285:       PetscCall(DMGetField(dm, field, NULL, &obj));
1286:       PetscCall(PetscObjectGetClassId(obj, &id));
1287:       if (id == PETSCFE_CLASSID) {
1288:         PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
1289:         PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
1290:       } else if (id == PETSCFV_CLASSID) {
1291:         PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
1292:         Nb = 1;
1293:       } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1294:       if (debug) {
1295:         char title[1024];
1296:         PetscCall(PetscSNPrintf(title, 1023, "Solution for Field %" PetscInt_FMT, field));
1297:         PetscCall(DMPrintCellVector(c, title, Nb, &x[fieldOffset]));
1298:       }
1299:       for (q = 0; q < Nq; ++q) {
1300:         PetscFEGeom    qgeom;
1301:         PetscErrorCode ierr;

1303:         qgeom.dimEmbed = fegeom.dimEmbed;
1304:         qgeom.J        = &fegeom.J[q * coordDim * coordDim];
1305:         qgeom.invJ     = &fegeom.invJ[q * coordDim * coordDim];
1306:         qgeom.detJ     = &fegeom.detJ[q];
1307:         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);
1308:         if (transform) {
1309:           gcoords = &coords[coordDim * Nq];
1310:           PetscCall(DMPlexBasisTransformApplyReal_Internal(dm, &coords[coordDim * q], PETSC_TRUE, coordDim, &coords[coordDim * q], gcoords, dm->transformCtx));
1311:         } else {
1312:           gcoords = &coords[coordDim * q];
1313:         }
1314:         PetscCall(PetscArrayzero(funcVal, Nc));
1315:         ierr = (*funcs[field])(coordDim, time, gcoords, Nc, funcVal, ctx);
1316:         if (ierr) {
1317:           PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1318:           PetscCall(DMRestoreLocalVector(dm, &localX));
1319:           PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1320:         }
1321:         if (transform) PetscCall(DMPlexBasisTransformApply_Internal(dm, &coords[coordDim * q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx));
1322:         if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[fieldOffset], &qgeom, q, interpolant));
1323:         else if (id == PETSCFV_CLASSID) PetscCall(PetscFVInterpolate_Static((PetscFV)obj, &x[fieldOffset], q, interpolant));
1324:         else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1325:         for (fc = 0; fc < Nc; ++fc) {
1326:           const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
1327:           if (debug)
1328:             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.),
1329:                                   (double)(PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q]), (double)PetscRealPart(interpolant[fc]), (double)PetscRealPart(funcVal[fc])));
1330:           elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
1331:         }
1332:       }
1333:       fieldOffset += Nb;
1334:       qc += Nc;
1335:     }
1336:     PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1337:     if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  elem %" PetscInt_FMT " diff %g\n", c, (double)elemDiff));
1338:     localDiff += elemDiff;
1339:   }
1340:   PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1341:   PetscCall(MPIU_Allreduce(&localDiff, diff, 1, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
1342:   *diff = PetscSqrtReal(*diff);
1343:   PetscFunctionReturn(PETSC_SUCCESS);
1344: }

1346: 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)
1347: {
1348:   const PetscInt   debug = ((DM_Plex *)dm->data)->printL2;
1349:   DM               tdm;
1350:   PetscSection     section;
1351:   PetscQuadrature  quad;
1352:   Vec              localX, tv;
1353:   PetscScalar     *funcVal, *interpolant;
1354:   const PetscReal *quadWeights;
1355:   PetscFEGeom      fegeom;
1356:   PetscReal       *coords, *gcoords;
1357:   PetscReal        localDiff = 0.0;
1358:   PetscInt         dim, coordDim, qNc = 0, Nq = 0, numFields, numComponents = 0, cStart, cEnd, c, field, fieldOffset;
1359:   PetscBool        transform;

1361:   PetscFunctionBegin;
1362:   PetscCall(DMGetDimension(dm, &dim));
1363:   PetscCall(DMGetCoordinateDim(dm, &coordDim));
1364:   fegeom.dimEmbed = coordDim;
1365:   PetscCall(DMGetLocalSection(dm, &section));
1366:   PetscCall(PetscSectionGetNumFields(section, &numFields));
1367:   PetscCall(DMGetLocalVector(dm, &localX));
1368:   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
1369:   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
1370:   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
1371:   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
1372:   PetscCall(DMHasBasisTransform(dm, &transform));
1373:   for (field = 0; field < numFields; ++field) {
1374:     PetscFE  fe;
1375:     PetscInt Nc;

1377:     PetscCall(DMGetField(dm, field, NULL, (PetscObject *)&fe));
1378:     PetscCall(PetscFEGetQuadrature(fe, &quad));
1379:     PetscCall(PetscFEGetNumComponents(fe, &Nc));
1380:     numComponents += Nc;
1381:   }
1382:   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, NULL, &quadWeights));
1383:   PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
1384:   /* PetscCall(DMProjectFunctionLocal(dm, fe, funcs, INSERT_BC_VALUES, localX)); */
1385:   PetscCall(PetscMalloc6(numComponents, &funcVal, coordDim * (Nq + 1), &coords, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ, numComponents * coordDim, &interpolant, Nq, &fegeom.detJ));
1386:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
1387:   for (c = cStart; c < cEnd; ++c) {
1388:     PetscScalar *x        = NULL;
1389:     PetscReal    elemDiff = 0.0;
1390:     PetscInt     qc       = 0;

1392:     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1393:     PetscCall(DMPlexVecGetClosure(dm, NULL, localX, c, NULL, &x));

1395:     for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1396:       PetscFE     fe;
1397:       void *const ctx = ctxs ? ctxs[field] : NULL;
1398:       PetscInt    Nb, Nc, q, fc;

1400:       PetscCall(DMGetField(dm, field, NULL, (PetscObject *)&fe));
1401:       PetscCall(PetscFEGetDimension(fe, &Nb));
1402:       PetscCall(PetscFEGetNumComponents(fe, &Nc));
1403:       if (debug) {
1404:         char title[1024];
1405:         PetscCall(PetscSNPrintf(title, 1023, "Solution for Field %" PetscInt_FMT, field));
1406:         PetscCall(DMPrintCellVector(c, title, Nb, &x[fieldOffset]));
1407:       }
1408:       for (q = 0; q < Nq; ++q) {
1409:         PetscFEGeom    qgeom;
1410:         PetscErrorCode ierr;

1412:         qgeom.dimEmbed = fegeom.dimEmbed;
1413:         qgeom.J        = &fegeom.J[q * coordDim * coordDim];
1414:         qgeom.invJ     = &fegeom.invJ[q * coordDim * coordDim];
1415:         qgeom.detJ     = &fegeom.detJ[q];
1416:         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);
1417:         if (transform) {
1418:           gcoords = &coords[coordDim * Nq];
1419:           PetscCall(DMPlexBasisTransformApplyReal_Internal(dm, &coords[coordDim * q], PETSC_TRUE, coordDim, &coords[coordDim * q], gcoords, dm->transformCtx));
1420:         } else {
1421:           gcoords = &coords[coordDim * q];
1422:         }
1423:         PetscCall(PetscArrayzero(funcVal, Nc));
1424:         ierr = (*funcs[field])(coordDim, time, gcoords, n, Nc, funcVal, ctx);
1425:         if (ierr) {
1426:           PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1427:           PetscCall(DMRestoreLocalVector(dm, &localX));
1428:           PetscCall(PetscFree6(funcVal, coords, fegeom.J, fegeom.invJ, interpolant, fegeom.detJ));
1429:         }
1430:         if (transform) PetscCall(DMPlexBasisTransformApply_Internal(dm, &coords[coordDim * q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx));
1431:         PetscCall(PetscFEInterpolateGradient_Static(fe, 1, &x[fieldOffset], &qgeom, q, interpolant));
1432:         /* Overwrite with the dot product if the normal is given */
1433:         if (n) {
1434:           for (fc = 0; fc < Nc; ++fc) {
1435:             PetscScalar sum = 0.0;
1436:             PetscInt    d;
1437:             for (d = 0; d < dim; ++d) sum += interpolant[fc * dim + d] * n[d];
1438:             interpolant[fc] = sum;
1439:           }
1440:         }
1441:         for (fc = 0; fc < Nc; ++fc) {
1442:           const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
1443:           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])));
1444:           elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
1445:         }
1446:       }
1447:       fieldOffset += Nb;
1448:       qc += Nc;
1449:     }
1450:     PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1451:     if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  elem %" PetscInt_FMT " diff %g\n", c, (double)elemDiff));
1452:     localDiff += elemDiff;
1453:   }
1454:   PetscCall(PetscFree6(funcVal, coords, fegeom.J, fegeom.invJ, interpolant, fegeom.detJ));
1455:   PetscCall(DMRestoreLocalVector(dm, &localX));
1456:   PetscCall(MPIU_Allreduce(&localDiff, diff, 1, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
1457:   *diff = PetscSqrtReal(*diff);
1458:   PetscFunctionReturn(PETSC_SUCCESS);
1459: }

1461: PetscErrorCode DMComputeL2FieldDiff_Plex(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, PetscReal *diff)
1462: {
1463:   const PetscInt debug = ((DM_Plex *)dm->data)->printL2;
1464:   DM             tdm;
1465:   DMLabel        depthLabel;
1466:   PetscSection   section;
1467:   Vec            localX, tv;
1468:   PetscReal     *localDiff;
1469:   PetscInt       dim, depth, dE, Nf, f, Nds, s;
1470:   PetscBool      transform;

1472:   PetscFunctionBegin;
1473:   PetscCall(DMGetDimension(dm, &dim));
1474:   PetscCall(DMGetCoordinateDim(dm, &dE));
1475:   PetscCall(DMGetLocalSection(dm, &section));
1476:   PetscCall(DMGetLocalVector(dm, &localX));
1477:   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
1478:   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
1479:   PetscCall(DMHasBasisTransform(dm, &transform));
1480:   PetscCall(DMGetNumFields(dm, &Nf));
1481:   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
1482:   PetscCall(DMLabelGetNumValues(depthLabel, &depth));

1484:   PetscCall(VecSet(localX, 0.0));
1485:   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
1486:   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
1487:   PetscCall(DMProjectFunctionLocal(dm, time, funcs, ctxs, INSERT_BC_VALUES, localX));
1488:   PetscCall(DMGetNumDS(dm, &Nds));
1489:   PetscCall(PetscCalloc1(Nf, &localDiff));
1490:   for (s = 0; s < Nds; ++s) {
1491:     PetscDS          ds;
1492:     DMLabel          label;
1493:     IS               fieldIS, pointIS;
1494:     const PetscInt  *fields, *points = NULL;
1495:     PetscQuadrature  quad;
1496:     const PetscReal *quadPoints, *quadWeights;
1497:     PetscFEGeom      fegeom;
1498:     PetscReal       *coords, *gcoords;
1499:     PetscScalar     *funcVal, *interpolant;
1500:     PetscBool        isCohesive;
1501:     PetscInt         qNc, Nq, totNc, cStart = 0, cEnd, c, dsNf;

1503:     PetscCall(DMGetRegionNumDS(dm, s, &label, &fieldIS, &ds, NULL));
1504:     PetscCall(ISGetIndices(fieldIS, &fields));
1505:     PetscCall(PetscDSIsCohesive(ds, &isCohesive));
1506:     PetscCall(PetscDSGetNumFields(ds, &dsNf));
1507:     PetscCall(PetscDSGetTotalComponents(ds, &totNc));
1508:     PetscCall(PetscDSGetQuadrature(ds, &quad));
1509:     PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
1510:     PetscCheck(!(qNc != 1) || !(qNc != totNc), PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, totNc);
1511:     PetscCall(PetscCalloc6(totNc, &funcVal, totNc, &interpolant, dE * (Nq + 1), &coords, Nq, &fegeom.detJ, dE * dE * Nq, &fegeom.J, dE * dE * Nq, &fegeom.invJ));
1512:     if (!label) {
1513:       PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
1514:     } else {
1515:       PetscCall(DMLabelGetStratumIS(label, 1, &pointIS));
1516:       PetscCall(ISGetLocalSize(pointIS, &cEnd));
1517:       PetscCall(ISGetIndices(pointIS, &points));
1518:     }
1519:     for (c = cStart; c < cEnd; ++c) {
1520:       const PetscInt  cell = points ? points[c] : c;
1521:       PetscScalar    *x    = NULL;
1522:       const PetscInt *cone;
1523:       PetscInt        qc = 0, fOff = 0, dep;

1525:       PetscCall(DMLabelGetValue(depthLabel, cell, &dep));
1526:       if (dep != depth - 1) continue;
1527:       if (isCohesive) {
1528:         PetscCall(DMPlexGetCone(dm, cell, &cone));
1529:         PetscCall(DMPlexComputeCellGeometryFEM(dm, cone[0], quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1530:       } else {
1531:         PetscCall(DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1532:       }
1533:       PetscCall(DMPlexVecGetClosure(dm, NULL, localX, cell, NULL, &x));
1534:       for (f = 0; f < dsNf; ++f) {
1535:         PetscObject  obj;
1536:         PetscClassId id;
1537:         void *const  ctx = ctxs ? ctxs[fields[f]] : NULL;
1538:         PetscInt     Nb, Nc, q, fc;
1539:         PetscReal    elemDiff = 0.0;
1540:         PetscBool    cohesive;

1542:         PetscCall(PetscDSGetCohesive(ds, f, &cohesive));
1543:         if (isCohesive && !cohesive) continue;
1544:         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
1545:         PetscCall(PetscObjectGetClassId(obj, &id));
1546:         if (id == PETSCFE_CLASSID) {
1547:           PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
1548:           PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
1549:         } else if (id == PETSCFV_CLASSID) {
1550:           PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
1551:           Nb = 1;
1552:         } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, fields[f]);
1553:         if (debug) {
1554:           char title[1024];
1555:           PetscCall(PetscSNPrintf(title, 1023, "Solution for Field %" PetscInt_FMT, fields[f]));
1556:           PetscCall(DMPrintCellVector(cell, title, Nb, &x[fOff]));
1557:         }
1558:         for (q = 0; q < Nq; ++q) {
1559:           PetscFEGeom    qgeom;
1560:           PetscErrorCode ierr;

1562:           qgeom.dimEmbed = fegeom.dimEmbed;
1563:           qgeom.J        = &fegeom.J[q * dE * dE];
1564:           qgeom.invJ     = &fegeom.invJ[q * dE * dE];
1565:           qgeom.detJ     = &fegeom.detJ[q];
1566:           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);
1567:           if (transform) {
1568:             gcoords = &coords[dE * Nq];
1569:             PetscCall(DMPlexBasisTransformApplyReal_Internal(dm, &coords[dE * q], PETSC_TRUE, dE, &coords[dE * q], gcoords, dm->transformCtx));
1570:           } else {
1571:             gcoords = &coords[dE * q];
1572:           }
1573:           for (fc = 0; fc < Nc; ++fc) funcVal[fc] = 0.;
1574:           ierr = (*funcs[fields[f]])(dE, time, gcoords, Nc, funcVal, ctx);
1575:           if (ierr) {
1576:             PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, cell, NULL, &x));
1577:             PetscCall(DMRestoreLocalVector(dm, &localX));
1578:             PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1579:           }
1580:           if (transform) PetscCall(DMPlexBasisTransformApply_Internal(dm, &coords[dE * q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx));
1581:           /* Call once for each face, except for lagrange field */
1582:           if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[fOff], &qgeom, q, interpolant));
1583:           else if (id == PETSCFV_CLASSID) PetscCall(PetscFVInterpolate_Static((PetscFV)obj, &x[fOff], q, interpolant));
1584:           else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, fields[f]);
1585:           for (fc = 0; fc < Nc; ++fc) {
1586:             const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
1587:             if (debug)
1588:               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.),
1589:                                     (double)(PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q])));
1590:             elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
1591:           }
1592:         }
1593:         fOff += Nb;
1594:         qc += Nc;
1595:         localDiff[fields[f]] += elemDiff;
1596:         if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  cell %" PetscInt_FMT " field %" PetscInt_FMT " cum diff %g\n", cell, fields[f], (double)localDiff[fields[f]]));
1597:       }
1598:       PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, cell, NULL, &x));
1599:     }
1600:     if (label) {
1601:       PetscCall(ISRestoreIndices(pointIS, &points));
1602:       PetscCall(ISDestroy(&pointIS));
1603:     }
1604:     PetscCall(ISRestoreIndices(fieldIS, &fields));
1605:     PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1606:   }
1607:   PetscCall(DMRestoreLocalVector(dm, &localX));
1608:   PetscCall(MPIU_Allreduce(localDiff, diff, Nf, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
1609:   PetscCall(PetscFree(localDiff));
1610:   for (f = 0; f < Nf; ++f) diff[f] = PetscSqrtReal(diff[f]);
1611:   PetscFunctionReturn(PETSC_SUCCESS);
1612: }

1614: /*@C
1615:   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.

1617:   Collective

1619:   Input Parameters:
1620: + dm    - The `DM`
1621: . time  - The time
1622: . funcs - The functions to evaluate for each field component: `NULL` means that component does not contribute to error calculation
1623: . ctxs  - Optional array of contexts to pass to each function, or `NULL`.
1624: - X     - The coefficient vector u_h

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

1629:   Level: developer

1631: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
1632: @*/
1633: PetscErrorCode DMPlexComputeL2DiffVec(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, Vec D)
1634: {
1635:   PetscSection     section;
1636:   PetscQuadrature  quad;
1637:   Vec              localX;
1638:   PetscFEGeom      fegeom;
1639:   PetscScalar     *funcVal, *interpolant;
1640:   PetscReal       *coords;
1641:   const PetscReal *quadPoints, *quadWeights;
1642:   PetscInt         dim, coordDim, numFields, numComponents = 0, qNc, Nq, cStart, cEnd, c, field, fieldOffset;

1644:   PetscFunctionBegin;
1645:   PetscCall(VecSet(D, 0.0));
1646:   PetscCall(DMGetDimension(dm, &dim));
1647:   PetscCall(DMGetCoordinateDim(dm, &coordDim));
1648:   PetscCall(DMGetLocalSection(dm, &section));
1649:   PetscCall(PetscSectionGetNumFields(section, &numFields));
1650:   PetscCall(DMGetLocalVector(dm, &localX));
1651:   PetscCall(DMProjectFunctionLocal(dm, time, funcs, ctxs, INSERT_BC_VALUES, localX));
1652:   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
1653:   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
1654:   for (field = 0; field < numFields; ++field) {
1655:     PetscObject  obj;
1656:     PetscClassId id;
1657:     PetscInt     Nc;

1659:     PetscCall(DMGetField(dm, field, NULL, &obj));
1660:     PetscCall(PetscObjectGetClassId(obj, &id));
1661:     if (id == PETSCFE_CLASSID) {
1662:       PetscFE fe = (PetscFE)obj;

1664:       PetscCall(PetscFEGetQuadrature(fe, &quad));
1665:       PetscCall(PetscFEGetNumComponents(fe, &Nc));
1666:     } else if (id == PETSCFV_CLASSID) {
1667:       PetscFV fv = (PetscFV)obj;

1669:       PetscCall(PetscFVGetQuadrature(fv, &quad));
1670:       PetscCall(PetscFVGetNumComponents(fv, &Nc));
1671:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1672:     numComponents += Nc;
1673:   }
1674:   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
1675:   PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
1676:   PetscCall(PetscMalloc6(numComponents, &funcVal, numComponents, &interpolant, coordDim * Nq, &coords, Nq, &fegeom.detJ, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ));
1677:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
1678:   for (c = cStart; c < cEnd; ++c) {
1679:     PetscScalar *x        = NULL;
1680:     PetscScalar  elemDiff = 0.0;
1681:     PetscInt     qc       = 0;

1683:     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1684:     PetscCall(DMPlexVecGetClosure(dm, NULL, localX, c, NULL, &x));

1686:     for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1687:       PetscObject  obj;
1688:       PetscClassId id;
1689:       void *const  ctx = ctxs ? ctxs[field] : NULL;
1690:       PetscInt     Nb, Nc, q, fc;

1692:       PetscCall(DMGetField(dm, field, NULL, &obj));
1693:       PetscCall(PetscObjectGetClassId(obj, &id));
1694:       if (id == PETSCFE_CLASSID) {
1695:         PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
1696:         PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
1697:       } else if (id == PETSCFV_CLASSID) {
1698:         PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
1699:         Nb = 1;
1700:       } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1701:       if (funcs[field]) {
1702:         for (q = 0; q < Nq; ++q) {
1703:           PetscFEGeom qgeom;

1705:           qgeom.dimEmbed = fegeom.dimEmbed;
1706:           qgeom.J        = &fegeom.J[q * coordDim * coordDim];
1707:           qgeom.invJ     = &fegeom.invJ[q * coordDim * coordDim];
1708:           qgeom.detJ     = &fegeom.detJ[q];
1709:           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);
1710:           PetscCall((*funcs[field])(coordDim, time, &coords[q * coordDim], Nc, funcVal, ctx));
1711: #if defined(needs_fix_with_return_code_argument)
1712:           if (ierr) {
1713:             PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1714:             PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1715:             PetscCall(DMRestoreLocalVector(dm, &localX));
1716:           }
1717: #endif
1718:           if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[fieldOffset], &qgeom, q, interpolant));
1719:           else if (id == PETSCFV_CLASSID) PetscCall(PetscFVInterpolate_Static((PetscFV)obj, &x[fieldOffset], q, interpolant));
1720:           else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1721:           for (fc = 0; fc < Nc; ++fc) {
1722:             const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
1723:             elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
1724:           }
1725:         }
1726:       }
1727:       fieldOffset += Nb;
1728:       qc += Nc;
1729:     }
1730:     PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1731:     PetscCall(VecSetValue(D, c - cStart, elemDiff, INSERT_VALUES));
1732:   }
1733:   PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1734:   PetscCall(DMRestoreLocalVector(dm, &localX));
1735:   PetscCall(VecSqrtAbs(D));
1736:   PetscFunctionReturn(PETSC_SUCCESS);
1737: }

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

1742:   Collective

1744:   Input Parameters:
1745: + dm - The `DM`
1746: - locX  - The coefficient vector u_h

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

1751:   Level: developer

1753:   Note:
1754:   $ 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

1756: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
1757: @*/
1758: PetscErrorCode DMPlexComputeClementInterpolant(DM dm, Vec locX, Vec locC)
1759: {
1760:   PetscInt         debug = ((DM_Plex *)dm->data)->printFEM;
1761:   DM               dmc;
1762:   PetscQuadrature  quad;
1763:   PetscScalar     *interpolant, *valsum;
1764:   PetscFEGeom      fegeom;
1765:   PetscReal       *coords;
1766:   const PetscReal *quadPoints, *quadWeights;
1767:   PetscInt         dim, cdim, Nf, f, Nc = 0, Nq, qNc, cStart, cEnd, vStart, vEnd, v;

1769:   PetscFunctionBegin;
1770:   PetscCall(PetscCitationsRegister(ClementCitation, &Clementcite));
1771:   PetscCall(VecGetDM(locC, &dmc));
1772:   PetscCall(VecSet(locC, 0.0));
1773:   PetscCall(DMGetDimension(dm, &dim));
1774:   PetscCall(DMGetCoordinateDim(dm, &cdim));
1775:   fegeom.dimEmbed = cdim;
1776:   PetscCall(DMGetNumFields(dm, &Nf));
1777:   PetscCheck(Nf > 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fields is zero!");
1778:   for (f = 0; f < Nf; ++f) {
1779:     PetscObject  obj;
1780:     PetscClassId id;
1781:     PetscInt     fNc;

1783:     PetscCall(DMGetField(dm, f, NULL, &obj));
1784:     PetscCall(PetscObjectGetClassId(obj, &id));
1785:     if (id == PETSCFE_CLASSID) {
1786:       PetscFE fe = (PetscFE)obj;

1788:       PetscCall(PetscFEGetQuadrature(fe, &quad));
1789:       PetscCall(PetscFEGetNumComponents(fe, &fNc));
1790:     } else if (id == PETSCFV_CLASSID) {
1791:       PetscFV fv = (PetscFV)obj;

1793:       PetscCall(PetscFVGetQuadrature(fv, &quad));
1794:       PetscCall(PetscFVGetNumComponents(fv, &fNc));
1795:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
1796:     Nc += fNc;
1797:   }
1798:   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
1799:   PetscCheck(qNc == 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " > 1", qNc);
1800:   PetscCall(PetscMalloc6(Nc * 2, &valsum, Nc, &interpolant, cdim * Nq, &coords, Nq, &fegeom.detJ, cdim * cdim * Nq, &fegeom.J, cdim * cdim * Nq, &fegeom.invJ));
1801:   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1802:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
1803:   for (v = vStart; v < vEnd; ++v) {
1804:     PetscScalar volsum = 0.0;
1805:     PetscInt   *star   = NULL;
1806:     PetscInt    starSize, st, fc;

1808:     PetscCall(PetscArrayzero(valsum, Nc));
1809:     PetscCall(DMPlexGetTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
1810:     for (st = 0; st < starSize * 2; st += 2) {
1811:       const PetscInt cell = star[st];
1812:       PetscScalar   *val  = &valsum[Nc];
1813:       PetscScalar   *x    = NULL;
1814:       PetscReal      vol  = 0.0;
1815:       PetscInt       foff = 0;

1817:       if ((cell < cStart) || (cell >= cEnd)) continue;
1818:       PetscCall(DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1819:       PetscCall(DMPlexVecGetClosure(dm, NULL, locX, cell, NULL, &x));
1820:       for (f = 0; f < Nf; ++f) {
1821:         PetscObject  obj;
1822:         PetscClassId id;
1823:         PetscInt     Nb, fNc, q;

1825:         PetscCall(PetscArrayzero(val, Nc));
1826:         PetscCall(DMGetField(dm, f, NULL, &obj));
1827:         PetscCall(PetscObjectGetClassId(obj, &id));
1828:         if (id == PETSCFE_CLASSID) {
1829:           PetscCall(PetscFEGetNumComponents((PetscFE)obj, &fNc));
1830:           PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
1831:         } else if (id == PETSCFV_CLASSID) {
1832:           PetscCall(PetscFVGetNumComponents((PetscFV)obj, &fNc));
1833:           Nb = 1;
1834:         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
1835:         for (q = 0; q < Nq; ++q) {
1836:           const PetscReal wt = quadWeights[q] * fegeom.detJ[q];
1837:           PetscFEGeom     qgeom;

1839:           qgeom.dimEmbed = fegeom.dimEmbed;
1840:           qgeom.J        = &fegeom.J[q * cdim * cdim];
1841:           qgeom.invJ     = &fegeom.invJ[q * cdim * cdim];
1842:           qgeom.detJ     = &fegeom.detJ[q];
1843:           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);
1844:           if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[foff], &qgeom, q, interpolant));
1845:           else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
1846:           for (fc = 0; fc < fNc; ++fc) val[foff + fc] += interpolant[fc] * wt;
1847:           vol += wt;
1848:         }
1849:         foff += Nb;
1850:       }
1851:       PetscCall(DMPlexVecRestoreClosure(dm, NULL, locX, cell, NULL, &x));
1852:       for (fc = 0; fc < Nc; ++fc) valsum[fc] += val[fc];
1853:       volsum += vol;
1854:       if (debug) {
1855:         PetscCall(PetscPrintf(PETSC_COMM_SELF, "Vertex %" PetscInt_FMT " Cell %" PetscInt_FMT " value: [", v, cell));
1856:         for (fc = 0; fc < Nc; ++fc) {
1857:           if (fc) PetscCall(PetscPrintf(PETSC_COMM_SELF, ", "));
1858:           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%g", (double)PetscRealPart(val[fc])));
1859:         }
1860:         PetscCall(PetscPrintf(PETSC_COMM_SELF, "]\n"));
1861:       }
1862:     }
1863:     for (fc = 0; fc < Nc; ++fc) valsum[fc] /= volsum;
1864:     PetscCall(DMPlexRestoreTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
1865:     PetscCall(DMPlexVecSetClosure(dmc, NULL, locC, v, valsum, INSERT_VALUES));
1866:   }
1867:   PetscCall(PetscFree6(valsum, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1868:   PetscFunctionReturn(PETSC_SUCCESS);
1869: }

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

1874:   Collective

1876:   Input Parameters:
1877: + dm - The `DM`
1878: - locX  - The coefficient vector u_h

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

1883:   Level: developer

1885:   Note:
1886:   $\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

1888: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
1889: @*/
1890: PetscErrorCode DMPlexComputeGradientClementInterpolant(DM dm, Vec locX, Vec locC)
1891: {
1892:   DM_Plex         *mesh  = (DM_Plex *)dm->data;
1893:   PetscInt         debug = mesh->printFEM;
1894:   DM               dmC;
1895:   PetscQuadrature  quad;
1896:   PetscScalar     *interpolant, *gradsum;
1897:   PetscFEGeom      fegeom;
1898:   PetscReal       *coords;
1899:   const PetscReal *quadPoints, *quadWeights;
1900:   PetscInt         dim, coordDim, numFields, numComponents = 0, qNc, Nq, cStart, cEnd, vStart, vEnd, v, field, fieldOffset;

1902:   PetscFunctionBegin;
1903:   PetscCall(PetscCitationsRegister(ClementCitation, &Clementcite));
1904:   PetscCall(VecGetDM(locC, &dmC));
1905:   PetscCall(VecSet(locC, 0.0));
1906:   PetscCall(DMGetDimension(dm, &dim));
1907:   PetscCall(DMGetCoordinateDim(dm, &coordDim));
1908:   fegeom.dimEmbed = coordDim;
1909:   PetscCall(DMGetNumFields(dm, &numFields));
1910:   PetscCheck(numFields, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fields is zero!");
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(coordDim * numComponents * 2, &gradsum, coordDim * numComponents, &interpolant, coordDim * Nq, &coords, Nq, &fegeom.detJ, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ));
1934:   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1935:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
1936:   for (v = vStart; v < vEnd; ++v) {
1937:     PetscScalar volsum = 0.0;
1938:     PetscInt   *star   = NULL;
1939:     PetscInt    starSize, st, d, fc;

1941:     PetscCall(PetscArrayzero(gradsum, coordDim * numComponents));
1942:     PetscCall(DMPlexGetTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
1943:     for (st = 0; st < starSize * 2; st += 2) {
1944:       const PetscInt cell = star[st];
1945:       PetscScalar   *grad = &gradsum[coordDim * numComponents];
1946:       PetscScalar   *x    = NULL;
1947:       PetscReal      vol  = 0.0;

1949:       if ((cell < cStart) || (cell >= cEnd)) continue;
1950:       PetscCall(DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1951:       PetscCall(DMPlexVecGetClosure(dm, NULL, locX, cell, NULL, &x));
1952:       for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1953:         PetscObject  obj;
1954:         PetscClassId id;
1955:         PetscInt     Nb, Nc, q, qc = 0;

1957:         PetscCall(PetscArrayzero(grad, coordDim * numComponents));
1958:         PetscCall(DMGetField(dm, field, NULL, &obj));
1959:         PetscCall(PetscObjectGetClassId(obj, &id));
1960:         if (id == PETSCFE_CLASSID) {
1961:           PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
1962:           PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
1963:         } else if (id == PETSCFV_CLASSID) {
1964:           PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
1965:           Nb = 1;
1966:         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1967:         for (q = 0; q < Nq; ++q) {
1968:           PetscFEGeom qgeom;

1970:           qgeom.dimEmbed = fegeom.dimEmbed;
1971:           qgeom.J        = &fegeom.J[q * coordDim * coordDim];
1972:           qgeom.invJ     = &fegeom.invJ[q * coordDim * coordDim];
1973:           qgeom.detJ     = &fegeom.detJ[q];
1974:           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);
1975:           if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolateGradient_Static((PetscFE)obj, 1, &x[fieldOffset], &qgeom, q, interpolant));
1976:           else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1977:           for (fc = 0; fc < Nc; ++fc) {
1978:             const PetscReal wt = quadWeights[q * qNc + qc];

1980:             for (d = 0; d < coordDim; ++d) grad[fc * coordDim + d] += interpolant[fc * dim + d] * wt * fegeom.detJ[q];
1981:           }
1982:           vol += quadWeights[q * qNc] * fegeom.detJ[q];
1983:         }
1984:         fieldOffset += Nb;
1985:         qc += Nc;
1986:       }
1987:       PetscCall(DMPlexVecRestoreClosure(dm, NULL, locX, cell, NULL, &x));
1988:       for (fc = 0; fc < numComponents; ++fc) {
1989:         for (d = 0; d < coordDim; ++d) gradsum[fc * coordDim + d] += grad[fc * coordDim + d];
1990:       }
1991:       volsum += vol;
1992:       if (debug) {
1993:         PetscCall(PetscPrintf(PETSC_COMM_SELF, "Vertex %" PetscInt_FMT " Cell %" PetscInt_FMT " gradient: [", v, cell));
1994:         for (fc = 0; fc < numComponents; ++fc) {
1995:           for (d = 0; d < coordDim; ++d) {
1996:             if (fc || d > 0) PetscCall(PetscPrintf(PETSC_COMM_SELF, ", "));
1997:             PetscCall(PetscPrintf(PETSC_COMM_SELF, "%g", (double)PetscRealPart(grad[fc * coordDim + d])));
1998:           }
1999:         }
2000:         PetscCall(PetscPrintf(PETSC_COMM_SELF, "]\n"));
2001:       }
2002:     }
2003:     for (fc = 0; fc < numComponents; ++fc) {
2004:       for (d = 0; d < coordDim; ++d) gradsum[fc * coordDim + d] /= volsum;
2005:     }
2006:     PetscCall(DMPlexRestoreTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
2007:     PetscCall(DMPlexVecSetClosure(dmC, NULL, locC, v, gradsum, INSERT_VALUES));
2008:   }
2009:   PetscCall(PetscFree6(gradsum, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
2010:   PetscFunctionReturn(PETSC_SUCCESS);
2011: }

2013: static PetscErrorCode DMPlexComputeIntegral_Internal(DM dm, Vec X, PetscInt cStart, PetscInt cEnd, PetscScalar *cintegral, void *user)
2014: {
2015:   DM           dmAux = NULL;
2016:   PetscDS      prob, probAux = NULL;
2017:   PetscSection section, sectionAux;
2018:   Vec          locX, locA;
2019:   PetscInt     dim, numCells = cEnd - cStart, c, f;
2020:   PetscBool    useFVM = PETSC_FALSE;
2021:   /* DS */
2022:   PetscInt           Nf, totDim, *uOff, *uOff_x, numConstants;
2023:   PetscInt           NfAux, totDimAux, *aOff;
2024:   PetscScalar       *u, *a;
2025:   const PetscScalar *constants;
2026:   /* Geometry */
2027:   PetscFEGeom       *cgeomFEM;
2028:   DM                 dmGrad;
2029:   PetscQuadrature    affineQuad      = NULL;
2030:   Vec                cellGeometryFVM = NULL, faceGeometryFVM = NULL, locGrad = NULL;
2031:   PetscFVCellGeom   *cgeomFVM;
2032:   const PetscScalar *lgrad;
2033:   PetscInt           maxDegree;
2034:   DMField            coordField;
2035:   IS                 cellIS;

2037:   PetscFunctionBegin;
2038:   PetscCall(DMGetDS(dm, &prob));
2039:   PetscCall(DMGetDimension(dm, &dim));
2040:   PetscCall(DMGetLocalSection(dm, &section));
2041:   PetscCall(DMGetNumFields(dm, &Nf));
2042:   /* Determine which discretizations we have */
2043:   for (f = 0; f < Nf; ++f) {
2044:     PetscObject  obj;
2045:     PetscClassId id;

2047:     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
2048:     PetscCall(PetscObjectGetClassId(obj, &id));
2049:     if (id == PETSCFV_CLASSID) useFVM = PETSC_TRUE;
2050:   }
2051:   /* Get local solution with boundary values */
2052:   PetscCall(DMGetLocalVector(dm, &locX));
2053:   PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL));
2054:   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX));
2055:   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX));
2056:   /* Read DS information */
2057:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
2058:   PetscCall(PetscDSGetComponentOffsets(prob, &uOff));
2059:   PetscCall(PetscDSGetComponentDerivativeOffsets(prob, &uOff_x));
2060:   PetscCall(ISCreateStride(PETSC_COMM_SELF, numCells, cStart, 1, &cellIS));
2061:   PetscCall(PetscDSGetConstants(prob, &numConstants, &constants));
2062:   /* Read Auxiliary DS information */
2063:   PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA));
2064:   if (locA) {
2065:     PetscCall(VecGetDM(locA, &dmAux));
2066:     PetscCall(DMGetDS(dmAux, &probAux));
2067:     PetscCall(PetscDSGetNumFields(probAux, &NfAux));
2068:     PetscCall(DMGetLocalSection(dmAux, &sectionAux));
2069:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
2070:     PetscCall(PetscDSGetComponentOffsets(probAux, &aOff));
2071:   }
2072:   /* Allocate data  arrays */
2073:   PetscCall(PetscCalloc1(numCells * totDim, &u));
2074:   if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
2075:   /* Read out geometry */
2076:   PetscCall(DMGetCoordinateField(dm, &coordField));
2077:   PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
2078:   if (maxDegree <= 1) {
2079:     PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad));
2080:     if (affineQuad) PetscCall(DMFieldCreateFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &cgeomFEM));
2081:   }
2082:   if (useFVM) {
2083:     PetscFV   fv = NULL;
2084:     Vec       grad;
2085:     PetscInt  fStart, fEnd;
2086:     PetscBool compGrad;

2088:     for (f = 0; f < Nf; ++f) {
2089:       PetscObject  obj;
2090:       PetscClassId id;

2092:       PetscCall(PetscDSGetDiscretization(prob, f, &obj));
2093:       PetscCall(PetscObjectGetClassId(obj, &id));
2094:       if (id == PETSCFV_CLASSID) {
2095:         fv = (PetscFV)obj;
2096:         break;
2097:       }
2098:     }
2099:     PetscCall(PetscFVGetComputeGradients(fv, &compGrad));
2100:     PetscCall(PetscFVSetComputeGradients(fv, PETSC_TRUE));
2101:     PetscCall(DMPlexComputeGeometryFVM(dm, &cellGeometryFVM, &faceGeometryFVM));
2102:     PetscCall(DMPlexComputeGradientFVM(dm, fv, faceGeometryFVM, cellGeometryFVM, &dmGrad));
2103:     PetscCall(PetscFVSetComputeGradients(fv, compGrad));
2104:     PetscCall(VecGetArrayRead(cellGeometryFVM, (const PetscScalar **)&cgeomFVM));
2105:     /* Reconstruct and limit cell gradients */
2106:     PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
2107:     PetscCall(DMGetGlobalVector(dmGrad, &grad));
2108:     PetscCall(DMPlexReconstructGradients_Internal(dm, fv, fStart, fEnd, faceGeometryFVM, cellGeometryFVM, locX, grad));
2109:     /* Communicate gradient values */
2110:     PetscCall(DMGetLocalVector(dmGrad, &locGrad));
2111:     PetscCall(DMGlobalToLocalBegin(dmGrad, grad, INSERT_VALUES, locGrad));
2112:     PetscCall(DMGlobalToLocalEnd(dmGrad, grad, INSERT_VALUES, locGrad));
2113:     PetscCall(DMRestoreGlobalVector(dmGrad, &grad));
2114:     /* Handle non-essential (e.g. outflow) boundary values */
2115:     PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_FALSE, locX, 0.0, faceGeometryFVM, cellGeometryFVM, locGrad));
2116:     PetscCall(VecGetArrayRead(locGrad, &lgrad));
2117:   }
2118:   /* Read out data from inputs */
2119:   for (c = cStart; c < cEnd; ++c) {
2120:     PetscScalar *x = NULL;
2121:     PetscInt     i;

2123:     PetscCall(DMPlexVecGetClosure(dm, section, locX, c, NULL, &x));
2124:     for (i = 0; i < totDim; ++i) u[c * totDim + i] = x[i];
2125:     PetscCall(DMPlexVecRestoreClosure(dm, section, locX, c, NULL, &x));
2126:     if (dmAux) {
2127:       PetscCall(DMPlexVecGetClosure(dmAux, sectionAux, locA, c, NULL, &x));
2128:       for (i = 0; i < totDimAux; ++i) a[c * totDimAux + i] = x[i];
2129:       PetscCall(DMPlexVecRestoreClosure(dmAux, sectionAux, locA, c, NULL, &x));
2130:     }
2131:   }
2132:   /* Do integration for each field */
2133:   for (f = 0; f < Nf; ++f) {
2134:     PetscObject  obj;
2135:     PetscClassId id;
2136:     PetscInt     numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;

2138:     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
2139:     PetscCall(PetscObjectGetClassId(obj, &id));
2140:     if (id == PETSCFE_CLASSID) {
2141:       PetscFE         fe = (PetscFE)obj;
2142:       PetscQuadrature q;
2143:       PetscFEGeom    *chunkGeom = NULL;
2144:       PetscInt        Nq, Nb;

2146:       PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
2147:       PetscCall(PetscFEGetQuadrature(fe, &q));
2148:       PetscCall(PetscQuadratureGetData(q, NULL, NULL, &Nq, NULL, NULL));
2149:       PetscCall(PetscFEGetDimension(fe, &Nb));
2150:       blockSize = Nb * Nq;
2151:       batchSize = numBlocks * blockSize;
2152:       PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
2153:       numChunks = numCells / (numBatches * batchSize);
2154:       Ne        = numChunks * numBatches * batchSize;
2155:       Nr        = numCells % (numBatches * batchSize);
2156:       offset    = numCells - Nr;
2157:       if (!affineQuad) PetscCall(DMFieldCreateFEGeom(coordField, cellIS, q, PETSC_FALSE, &cgeomFEM));
2158:       PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
2159:       PetscCall(PetscFEIntegrate(prob, f, Ne, chunkGeom, u, probAux, a, cintegral));
2160:       PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &chunkGeom));
2161:       PetscCall(PetscFEIntegrate(prob, f, Nr, chunkGeom, &u[offset * totDim], probAux, &a[offset * totDimAux], &cintegral[offset * Nf]));
2162:       PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &chunkGeom));
2163:       if (!affineQuad) PetscCall(PetscFEGeomDestroy(&cgeomFEM));
2164:     } else if (id == PETSCFV_CLASSID) {
2165:       PetscInt       foff;
2166:       PetscPointFunc obj_func;
2167:       PetscScalar    lint;

2169:       PetscCall(PetscDSGetObjective(prob, f, &obj_func));
2170:       PetscCall(PetscDSGetFieldOffset(prob, f, &foff));
2171:       if (obj_func) {
2172:         for (c = 0; c < numCells; ++c) {
2173:           PetscScalar *u_x;

2175:           PetscCall(DMPlexPointLocalRead(dmGrad, c, lgrad, &u_x));
2176:           obj_func(dim, Nf, NfAux, uOff, uOff_x, &u[totDim * c + foff], NULL, u_x, aOff, NULL, &a[totDimAux * c], NULL, NULL, 0.0, cgeomFVM[c].centroid, numConstants, constants, &lint);
2177:           cintegral[c * Nf + f] += PetscRealPart(lint) * cgeomFVM[c].volume;
2178:         }
2179:       }
2180:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
2181:   }
2182:   /* Cleanup data arrays */
2183:   if (useFVM) {
2184:     PetscCall(VecRestoreArrayRead(locGrad, &lgrad));
2185:     PetscCall(VecRestoreArrayRead(cellGeometryFVM, (const PetscScalar **)&cgeomFVM));
2186:     PetscCall(DMRestoreLocalVector(dmGrad, &locGrad));
2187:     PetscCall(VecDestroy(&faceGeometryFVM));
2188:     PetscCall(VecDestroy(&cellGeometryFVM));
2189:     PetscCall(DMDestroy(&dmGrad));
2190:   }
2191:   if (dmAux) PetscCall(PetscFree(a));
2192:   PetscCall(PetscFree(u));
2193:   /* Cleanup */
2194:   if (affineQuad) PetscCall(PetscFEGeomDestroy(&cgeomFEM));
2195:   PetscCall(PetscQuadratureDestroy(&affineQuad));
2196:   PetscCall(ISDestroy(&cellIS));
2197:   PetscCall(DMRestoreLocalVector(dm, &locX));
2198:   PetscFunctionReturn(PETSC_SUCCESS);
2199: }

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

2204:   Input Parameters:
2205: + dm - The mesh
2206: . X  - Global input vector
2207: - user - The user context

2209:   Output Parameter:
2210: . integral - Integral for each field

2212:   Level: developer

2214: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSNESComputeResidualFEM()`
2215: @*/
2216: PetscErrorCode DMPlexComputeIntegralFEM(DM dm, Vec X, PetscScalar *integral, void *user)
2217: {
2218:   DM_Plex     *mesh = (DM_Plex *)dm->data;
2219:   PetscScalar *cintegral, *lintegral;
2220:   PetscInt     Nf, f, cellHeight, cStart, cEnd, cell;

2222:   PetscFunctionBegin;
2226:   PetscCall(PetscLogEventBegin(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2227:   PetscCall(DMGetNumFields(dm, &Nf));
2228:   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
2229:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
2230:   /* TODO Introduce a loop over large chunks (right now this is a single chunk) */
2231:   PetscCall(PetscCalloc2(Nf, &lintegral, (cEnd - cStart) * Nf, &cintegral));
2232:   PetscCall(DMPlexComputeIntegral_Internal(dm, X, cStart, cEnd, cintegral, user));
2233:   /* Sum up values */
2234:   for (cell = cStart; cell < cEnd; ++cell) {
2235:     const PetscInt c = cell - cStart;

2237:     if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, "Cell Integral", Nf, &cintegral[c * Nf]));
2238:     for (f = 0; f < Nf; ++f) lintegral[f] += cintegral[c * Nf + f];
2239:   }
2240:   PetscCall(MPIU_Allreduce(lintegral, integral, Nf, MPIU_SCALAR, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
2241:   if (mesh->printFEM) {
2242:     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "Integral:"));
2243:     for (f = 0; f < Nf; ++f) PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), " %g", (double)PetscRealPart(integral[f])));
2244:     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "\n"));
2245:   }
2246:   PetscCall(PetscFree2(lintegral, cintegral));
2247:   PetscCall(PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2248:   PetscFunctionReturn(PETSC_SUCCESS);
2249: }

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

2254:   Input Parameters:
2255: + dm - The mesh
2256: . X  - Global input vector
2257: - user - The user context

2259:   Output Parameter:
2260: . integral - Cellwise integrals for each field

2262:   Level: developer

2264: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSNESComputeResidualFEM()`
2265: @*/
2266: PetscErrorCode DMPlexComputeCellwiseIntegralFEM(DM dm, Vec X, Vec F, void *user)
2267: {
2268:   DM_Plex     *mesh = (DM_Plex *)dm->data;
2269:   DM           dmF;
2270:   PetscSection sectionF;
2271:   PetscScalar *cintegral, *af;
2272:   PetscInt     Nf, f, cellHeight, cStart, cEnd, cell;

2274:   PetscFunctionBegin;
2278:   PetscCall(PetscLogEventBegin(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2279:   PetscCall(DMGetNumFields(dm, &Nf));
2280:   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
2281:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
2282:   /* TODO Introduce a loop over large chunks (right now this is a single chunk) */
2283:   PetscCall(PetscCalloc1((cEnd - cStart) * Nf, &cintegral));
2284:   PetscCall(DMPlexComputeIntegral_Internal(dm, X, cStart, cEnd, cintegral, user));
2285:   /* Put values in F*/
2286:   PetscCall(VecGetDM(F, &dmF));
2287:   PetscCall(DMGetLocalSection(dmF, &sectionF));
2288:   PetscCall(VecGetArray(F, &af));
2289:   for (cell = cStart; cell < cEnd; ++cell) {
2290:     const PetscInt c = cell - cStart;
2291:     PetscInt       dof, off;

2293:     if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, "Cell Integral", Nf, &cintegral[c * Nf]));
2294:     PetscCall(PetscSectionGetDof(sectionF, cell, &dof));
2295:     PetscCall(PetscSectionGetOffset(sectionF, cell, &off));
2296:     PetscCheck(dof == Nf, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "The number of cell dofs %" PetscInt_FMT " != %" PetscInt_FMT, dof, Nf);
2297:     for (f = 0; f < Nf; ++f) af[off + f] = cintegral[c * Nf + f];
2298:   }
2299:   PetscCall(VecRestoreArray(F, &af));
2300:   PetscCall(PetscFree(cintegral));
2301:   PetscCall(PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2302:   PetscFunctionReturn(PETSC_SUCCESS);
2303: }

2305: static PetscErrorCode DMPlexComputeBdIntegral_Internal(DM dm, Vec locX, IS pointIS, void (*func)(PetscInt, PetscInt, PetscInt, const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], PetscReal, const PetscReal[], const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]), PetscScalar *fintegral, void *user)
2306: {
2307:   DM                 plex = NULL, plexA = NULL;
2308:   DMEnclosureType    encAux;
2309:   PetscDS            prob, probAux       = NULL;
2310:   PetscSection       section, sectionAux = NULL;
2311:   Vec                locA = NULL;
2312:   DMField            coordField;
2313:   PetscInt           Nf, totDim, *uOff, *uOff_x;
2314:   PetscInt           NfAux = 0, totDimAux = 0, *aOff = NULL;
2315:   PetscScalar       *u, *a = NULL;
2316:   const PetscScalar *constants;
2317:   PetscInt           numConstants, f;

2319:   PetscFunctionBegin;
2320:   PetscCall(DMGetCoordinateField(dm, &coordField));
2321:   PetscCall(DMConvert(dm, DMPLEX, &plex));
2322:   PetscCall(DMGetDS(dm, &prob));
2323:   PetscCall(DMGetLocalSection(dm, &section));
2324:   PetscCall(PetscSectionGetNumFields(section, &Nf));
2325:   /* Determine which discretizations we have */
2326:   for (f = 0; f < Nf; ++f) {
2327:     PetscObject  obj;
2328:     PetscClassId id;

2330:     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
2331:     PetscCall(PetscObjectGetClassId(obj, &id));
2332:     PetscCheck(id != PETSCFV_CLASSID, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Not supported for FVM (field %" PetscInt_FMT ")", f);
2333:   }
2334:   /* Read DS information */
2335:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
2336:   PetscCall(PetscDSGetComponentOffsets(prob, &uOff));
2337:   PetscCall(PetscDSGetComponentDerivativeOffsets(prob, &uOff_x));
2338:   PetscCall(PetscDSGetConstants(prob, &numConstants, &constants));
2339:   /* Read Auxiliary DS information */
2340:   PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA));
2341:   if (locA) {
2342:     DM dmAux;

2344:     PetscCall(VecGetDM(locA, &dmAux));
2345:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
2346:     PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
2347:     PetscCall(DMGetDS(dmAux, &probAux));
2348:     PetscCall(PetscDSGetNumFields(probAux, &NfAux));
2349:     PetscCall(DMGetLocalSection(dmAux, &sectionAux));
2350:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
2351:     PetscCall(PetscDSGetComponentOffsets(probAux, &aOff));
2352:   }
2353:   /* Integrate over points */
2354:   {
2355:     PetscFEGeom    *fgeom, *chunkGeom = NULL;
2356:     PetscInt        maxDegree;
2357:     PetscQuadrature qGeom = NULL;
2358:     const PetscInt *points;
2359:     PetscInt        numFaces, face, Nq, field;
2360:     PetscInt        numChunks, chunkSize, chunk, Nr, offset;

2362:     PetscCall(ISGetLocalSize(pointIS, &numFaces));
2363:     PetscCall(ISGetIndices(pointIS, &points));
2364:     PetscCall(PetscCalloc2(numFaces * totDim, &u, locA ? numFaces * totDimAux : 0, &a));
2365:     PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
2366:     for (field = 0; field < Nf; ++field) {
2367:       PetscFE fe;

2369:       PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fe));
2370:       if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom));
2371:       if (!qGeom) {
2372:         PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
2373:         PetscCall(PetscObjectReference((PetscObject)qGeom));
2374:       }
2375:       PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
2376:       PetscCall(DMPlexGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
2377:       for (face = 0; face < numFaces; ++face) {
2378:         const PetscInt point = points[face], *support;
2379:         PetscScalar   *x     = NULL;
2380:         PetscInt       i;

2382:         PetscCall(DMPlexGetSupport(dm, point, &support));
2383:         PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
2384:         for (i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
2385:         PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
2386:         if (locA) {
2387:           PetscInt subp;
2388:           PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
2389:           PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
2390:           for (i = 0; i < totDimAux; ++i) a[f * totDimAux + i] = x[i];
2391:           PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
2392:         }
2393:       }
2394:       /* Get blocking */
2395:       {
2396:         PetscQuadrature q;
2397:         PetscInt        numBatches, batchSize, numBlocks, blockSize;
2398:         PetscInt        Nq, Nb;

2400:         PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
2401:         PetscCall(PetscFEGetQuadrature(fe, &q));
2402:         PetscCall(PetscQuadratureGetData(q, NULL, NULL, &Nq, NULL, NULL));
2403:         PetscCall(PetscFEGetDimension(fe, &Nb));
2404:         blockSize = Nb * Nq;
2405:         batchSize = numBlocks * blockSize;
2406:         chunkSize = numBatches * batchSize;
2407:         PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
2408:         numChunks = numFaces / chunkSize;
2409:         Nr        = numFaces % chunkSize;
2410:         offset    = numFaces - Nr;
2411:       }
2412:       /* Do integration for each field */
2413:       for (chunk = 0; chunk < numChunks; ++chunk) {
2414:         PetscCall(PetscFEGeomGetChunk(fgeom, chunk * chunkSize, (chunk + 1) * chunkSize, &chunkGeom));
2415:         PetscCall(PetscFEIntegrateBd(prob, field, func, chunkSize, chunkGeom, u, probAux, a, fintegral));
2416:         PetscCall(PetscFEGeomRestoreChunk(fgeom, 0, offset, &chunkGeom));
2417:       }
2418:       PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom));
2419:       PetscCall(PetscFEIntegrateBd(prob, field, func, Nr, chunkGeom, &u[offset * totDim], probAux, a ? &a[offset * totDimAux] : NULL, &fintegral[offset * Nf]));
2420:       PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom));
2421:       /* Cleanup data arrays */
2422:       PetscCall(DMPlexRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
2423:       PetscCall(PetscQuadratureDestroy(&qGeom));
2424:       PetscCall(PetscFree2(u, a));
2425:       PetscCall(ISRestoreIndices(pointIS, &points));
2426:     }
2427:   }
2428:   if (plex) PetscCall(DMDestroy(&plex));
2429:   if (plexA) PetscCall(DMDestroy(&plexA));
2430:   PetscFunctionReturn(PETSC_SUCCESS);
2431: }

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

2436:   Input Parameters:
2437: + dm      - The mesh
2438: . X       - Global input vector
2439: . label   - The boundary `DMLabel`
2440: . numVals - The number of label values to use, or `PETSC_DETERMINE` for all values
2441: . vals    - The label values to use, or NULL for all values
2442: . func    - The function to integrate along the boundary
2443: - user    - The user context

2445:   Output Parameter:
2446: . integral - Integral for each field

2448:   Level: developer

2450: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeIntegralFEM()`, `DMPlexComputeBdResidualFEM()`
2451: @*/
2452: PetscErrorCode DMPlexComputeBdIntegral(DM dm, Vec X, DMLabel label, PetscInt numVals, const PetscInt vals[], void (*func)(PetscInt, PetscInt, PetscInt, const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], PetscReal, const PetscReal[], const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]), PetscScalar *integral, void *user)
2453: {
2454:   Vec          locX;
2455:   PetscSection section;
2456:   DMLabel      depthLabel;
2457:   IS           facetIS;
2458:   PetscInt     dim, Nf, f, v;

2460:   PetscFunctionBegin;
2466:   PetscCall(PetscLogEventBegin(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2467:   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
2468:   PetscCall(DMGetDimension(dm, &dim));
2469:   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
2470:   PetscCall(DMGetLocalSection(dm, &section));
2471:   PetscCall(PetscSectionGetNumFields(section, &Nf));
2472:   /* Get local solution with boundary values */
2473:   PetscCall(DMGetLocalVector(dm, &locX));
2474:   PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL));
2475:   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX));
2476:   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX));
2477:   /* Loop over label values */
2478:   PetscCall(PetscArrayzero(integral, Nf));
2479:   for (v = 0; v < numVals; ++v) {
2480:     IS           pointIS;
2481:     PetscInt     numFaces, face;
2482:     PetscScalar *fintegral;

2484:     PetscCall(DMLabelGetStratumIS(label, vals[v], &pointIS));
2485:     if (!pointIS) continue; /* No points with that id on this process */
2486:     {
2487:       IS isectIS;

2489:       /* TODO: Special cases of ISIntersect where it is quick to check a priori if one is a superset of the other */
2490:       PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS));
2491:       PetscCall(ISDestroy(&pointIS));
2492:       pointIS = isectIS;
2493:     }
2494:     PetscCall(ISGetLocalSize(pointIS, &numFaces));
2495:     PetscCall(PetscCalloc1(numFaces * Nf, &fintegral));
2496:     PetscCall(DMPlexComputeBdIntegral_Internal(dm, locX, pointIS, func, fintegral, user));
2497:     /* Sum point contributions into integral */
2498:     for (f = 0; f < Nf; ++f)
2499:       for (face = 0; face < numFaces; ++face) integral[f] += fintegral[face * Nf + f];
2500:     PetscCall(PetscFree(fintegral));
2501:     PetscCall(ISDestroy(&pointIS));
2502:   }
2503:   PetscCall(DMRestoreLocalVector(dm, &locX));
2504:   PetscCall(ISDestroy(&facetIS));
2505:   PetscCall(PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2506:   PetscFunctionReturn(PETSC_SUCCESS);
2507: }

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

2512:   Input Parameters:
2513: + dmc  - The coarse mesh
2514: . dmf  - The fine mesh
2515: . isRefined - Flag indicating regular refinement, rather than the same topology
2516: - user - The user context

2518:   Output Parameter:
2519: . In  - The interpolation matrix

2521:   Level: developer

2523: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorGeneral()`, `DMPlexComputeJacobianFEM()`
2524: @*/
2525: PetscErrorCode DMPlexComputeInterpolatorNested(DM dmc, DM dmf, PetscBool isRefined, Mat In, void *user)
2526: {
2527:   DM_Plex     *mesh = (DM_Plex *)dmc->data;
2528:   const char  *name = "Interpolator";
2529:   PetscFE     *feRef;
2530:   PetscFV     *fvRef;
2531:   PetscSection fsection, fglobalSection;
2532:   PetscSection csection, cglobalSection;
2533:   PetscScalar *elemMat;
2534:   PetscInt     dim, Nf, f, fieldI, fieldJ, offsetI, offsetJ, cStart, cEnd, c;
2535:   PetscInt     cTotDim = 0, rTotDim = 0;

2537:   PetscFunctionBegin;
2538:   PetscCall(PetscLogEventBegin(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
2539:   PetscCall(DMGetDimension(dmf, &dim));
2540:   PetscCall(DMGetLocalSection(dmf, &fsection));
2541:   PetscCall(DMGetGlobalSection(dmf, &fglobalSection));
2542:   PetscCall(DMGetLocalSection(dmc, &csection));
2543:   PetscCall(DMGetGlobalSection(dmc, &cglobalSection));
2544:   PetscCall(PetscSectionGetNumFields(fsection, &Nf));
2545:   PetscCall(DMPlexGetSimplexOrBoxCells(dmc, 0, &cStart, &cEnd));
2546:   PetscCall(PetscCalloc2(Nf, &feRef, Nf, &fvRef));
2547:   for (f = 0; f < Nf; ++f) {
2548:     PetscObject  obj, objc;
2549:     PetscClassId id, idc;
2550:     PetscInt     rNb = 0, Nc = 0, cNb = 0;

2552:     PetscCall(DMGetField(dmf, f, NULL, &obj));
2553:     PetscCall(PetscObjectGetClassId(obj, &id));
2554:     if (id == PETSCFE_CLASSID) {
2555:       PetscFE fe = (PetscFE)obj;

2557:       if (isRefined) {
2558:         PetscCall(PetscFERefine(fe, &feRef[f]));
2559:       } else {
2560:         PetscCall(PetscObjectReference((PetscObject)fe));
2561:         feRef[f] = fe;
2562:       }
2563:       PetscCall(PetscFEGetDimension(feRef[f], &rNb));
2564:       PetscCall(PetscFEGetNumComponents(fe, &Nc));
2565:     } else if (id == PETSCFV_CLASSID) {
2566:       PetscFV        fv = (PetscFV)obj;
2567:       PetscDualSpace Q;

2569:       if (isRefined) {
2570:         PetscCall(PetscFVRefine(fv, &fvRef[f]));
2571:       } else {
2572:         PetscCall(PetscObjectReference((PetscObject)fv));
2573:         fvRef[f] = fv;
2574:       }
2575:       PetscCall(PetscFVGetDualSpace(fvRef[f], &Q));
2576:       PetscCall(PetscDualSpaceGetDimension(Q, &rNb));
2577:       PetscCall(PetscFVGetDualSpace(fv, &Q));
2578:       PetscCall(PetscFVGetNumComponents(fv, &Nc));
2579:     }
2580:     PetscCall(DMGetField(dmc, f, NULL, &objc));
2581:     PetscCall(PetscObjectGetClassId(objc, &idc));
2582:     if (idc == PETSCFE_CLASSID) {
2583:       PetscFE fe = (PetscFE)objc;

2585:       PetscCall(PetscFEGetDimension(fe, &cNb));
2586:     } else if (id == PETSCFV_CLASSID) {
2587:       PetscFV        fv = (PetscFV)obj;
2588:       PetscDualSpace Q;

2590:       PetscCall(PetscFVGetDualSpace(fv, &Q));
2591:       PetscCall(PetscDualSpaceGetDimension(Q, &cNb));
2592:     }
2593:     rTotDim += rNb;
2594:     cTotDim += cNb;
2595:   }
2596:   PetscCall(PetscMalloc1(rTotDim * cTotDim, &elemMat));
2597:   PetscCall(PetscArrayzero(elemMat, rTotDim * cTotDim));
2598:   for (fieldI = 0, offsetI = 0; fieldI < Nf; ++fieldI) {
2599:     PetscDualSpace   Qref;
2600:     PetscQuadrature  f;
2601:     const PetscReal *qpoints, *qweights;
2602:     PetscReal       *points;
2603:     PetscInt         npoints = 0, Nc, Np, fpdim, i, k, p, d;

2605:     /* Compose points from all dual basis functionals */
2606:     if (feRef[fieldI]) {
2607:       PetscCall(PetscFEGetDualSpace(feRef[fieldI], &Qref));
2608:       PetscCall(PetscFEGetNumComponents(feRef[fieldI], &Nc));
2609:     } else {
2610:       PetscCall(PetscFVGetDualSpace(fvRef[fieldI], &Qref));
2611:       PetscCall(PetscFVGetNumComponents(fvRef[fieldI], &Nc));
2612:     }
2613:     PetscCall(PetscDualSpaceGetDimension(Qref, &fpdim));
2614:     for (i = 0; i < fpdim; ++i) {
2615:       PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
2616:       PetscCall(PetscQuadratureGetData(f, NULL, NULL, &Np, NULL, NULL));
2617:       npoints += Np;
2618:     }
2619:     PetscCall(PetscMalloc1(npoints * dim, &points));
2620:     for (i = 0, k = 0; i < fpdim; ++i) {
2621:       PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
2622:       PetscCall(PetscQuadratureGetData(f, NULL, NULL, &Np, &qpoints, NULL));
2623:       for (p = 0; p < Np; ++p, ++k)
2624:         for (d = 0; d < dim; ++d) points[k * dim + d] = qpoints[p * dim + d];
2625:     }

2627:     for (fieldJ = 0, offsetJ = 0; fieldJ < Nf; ++fieldJ) {
2628:       PetscObject  obj;
2629:       PetscClassId id;
2630:       PetscInt     NcJ = 0, cpdim = 0, j, qNc;

2632:       PetscCall(DMGetField(dmc, fieldJ, NULL, &obj));
2633:       PetscCall(PetscObjectGetClassId(obj, &id));
2634:       if (id == PETSCFE_CLASSID) {
2635:         PetscFE         fe = (PetscFE)obj;
2636:         PetscTabulation T  = NULL;

2638:         /* Evaluate basis at points */
2639:         PetscCall(PetscFEGetNumComponents(fe, &NcJ));
2640:         PetscCall(PetscFEGetDimension(fe, &cpdim));
2641:         /* For now, fields only interpolate themselves */
2642:         if (fieldI == fieldJ) {
2643:           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);
2644:           PetscCall(PetscFECreateTabulation(fe, 1, npoints, points, 0, &T));
2645:           for (i = 0, k = 0; i < fpdim; ++i) {
2646:             PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
2647:             PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, NULL, &qweights));
2648:             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);
2649:             for (p = 0; p < Np; ++p, ++k) {
2650:               for (j = 0; j < cpdim; ++j) {
2651:                 /*
2652:                    cTotDim:            Total columns in element interpolation matrix, sum of number of dual basis functionals in each field
2653:                    offsetI, offsetJ:   Offsets into the larger element interpolation matrix for different fields
2654:                    fpdim, i, cpdim, j: Dofs for fine and coarse grids, correspond to dual space basis functionals
2655:                    qNC, Nc, Ncj, c:    Number of components in this field
2656:                    Np, p:              Number of quad points in the fine grid functional i
2657:                    k:                  i*Np + p, overall point number for the interpolation
2658:                 */
2659:                 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];
2660:               }
2661:             }
2662:           }
2663:           PetscCall(PetscTabulationDestroy(&T));
2664:         }
2665:       } else if (id == PETSCFV_CLASSID) {
2666:         PetscFV fv = (PetscFV)obj;

2668:         /* Evaluate constant function at points */
2669:         PetscCall(PetscFVGetNumComponents(fv, &NcJ));
2670:         cpdim = 1;
2671:         /* For now, fields only interpolate themselves */
2672:         if (fieldI == fieldJ) {
2673:           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);
2674:           for (i = 0, k = 0; i < fpdim; ++i) {
2675:             PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
2676:             PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, NULL, &qweights));
2677:             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);
2678:             for (p = 0; p < Np; ++p, ++k) {
2679:               for (j = 0; j < cpdim; ++j) {
2680:                 for (c = 0; c < Nc; ++c) elemMat[(offsetI + i) * cTotDim + offsetJ + j] += 1.0 * qweights[p * qNc + c];
2681:               }
2682:             }
2683:           }
2684:         }
2685:       }
2686:       offsetJ += cpdim;
2687:     }
2688:     offsetI += fpdim;
2689:     PetscCall(PetscFree(points));
2690:   }
2691:   if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(0, name, rTotDim, cTotDim, elemMat));
2692:   /* Preallocate matrix */
2693:   {
2694:     Mat          preallocator;
2695:     PetscScalar *vals;
2696:     PetscInt    *cellCIndices, *cellFIndices;
2697:     PetscInt     locRows, locCols, cell;

2699:     PetscCall(MatGetLocalSize(In, &locRows, &locCols));
2700:     PetscCall(MatCreate(PetscObjectComm((PetscObject)In), &preallocator));
2701:     PetscCall(MatSetType(preallocator, MATPREALLOCATOR));
2702:     PetscCall(MatSetSizes(preallocator, locRows, locCols, PETSC_DETERMINE, PETSC_DETERMINE));
2703:     PetscCall(MatSetUp(preallocator));
2704:     PetscCall(PetscCalloc3(rTotDim * cTotDim, &vals, cTotDim, &cellCIndices, rTotDim, &cellFIndices));
2705:     for (cell = cStart; cell < cEnd; ++cell) {
2706:       if (isRefined) {
2707:         PetscCall(DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, cell, cellCIndices, cellFIndices));
2708:         PetscCall(MatSetValues(preallocator, rTotDim, cellFIndices, cTotDim, cellCIndices, vals, INSERT_VALUES));
2709:       } else {
2710:         PetscCall(DMPlexMatSetClosureGeneral(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, preallocator, cell, vals, INSERT_VALUES));
2711:       }
2712:     }
2713:     PetscCall(PetscFree3(vals, cellCIndices, cellFIndices));
2714:     PetscCall(MatAssemblyBegin(preallocator, MAT_FINAL_ASSEMBLY));
2715:     PetscCall(MatAssemblyEnd(preallocator, MAT_FINAL_ASSEMBLY));
2716:     PetscCall(MatPreallocatorPreallocate(preallocator, PETSC_TRUE, In));
2717:     PetscCall(MatDestroy(&preallocator));
2718:   }
2719:   /* Fill matrix */
2720:   PetscCall(MatZeroEntries(In));
2721:   for (c = cStart; c < cEnd; ++c) {
2722:     if (isRefined) {
2723:       PetscCall(DMPlexMatSetClosureRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, In, c, elemMat, INSERT_VALUES));
2724:     } else {
2725:       PetscCall(DMPlexMatSetClosureGeneral(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, In, c, elemMat, INSERT_VALUES));
2726:     }
2727:   }
2728:   for (f = 0; f < Nf; ++f) PetscCall(PetscFEDestroy(&feRef[f]));
2729:   PetscCall(PetscFree2(feRef, fvRef));
2730:   PetscCall(PetscFree(elemMat));
2731:   PetscCall(MatAssemblyBegin(In, MAT_FINAL_ASSEMBLY));
2732:   PetscCall(MatAssemblyEnd(In, MAT_FINAL_ASSEMBLY));
2733:   if (mesh->printFEM > 1) {
2734:     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)In), "%s:\n", name));
2735:     PetscCall(MatChop(In, 1.0e-10));
2736:     PetscCall(MatView(In, NULL));
2737:   }
2738:   PetscCall(PetscLogEventEnd(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
2739:   PetscFunctionReturn(PETSC_SUCCESS);
2740: }

2742: PetscErrorCode DMPlexComputeMassMatrixNested(DM dmc, DM dmf, Mat mass, void *user)
2743: {
2744:   SETERRQ(PetscObjectComm((PetscObject)dmc), PETSC_ERR_SUP, "Laziness");
2745: }

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

2750:   Input Parameters:
2751: + dmf  - The fine mesh
2752: . dmc  - The coarse mesh
2753: - user - The user context

2755:   Output Parameter:
2756: . In  - The interpolation matrix

2758:   Level: developer

2760: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorNested()`, `DMPlexComputeJacobianFEM()`
2761: @*/
2762: PetscErrorCode DMPlexComputeInterpolatorGeneral(DM dmc, DM dmf, Mat In, void *user)
2763: {
2764:   DM_Plex     *mesh = (DM_Plex *)dmf->data;
2765:   const char  *name = "Interpolator";
2766:   PetscDS      prob;
2767:   Mat          interp;
2768:   PetscSection fsection, globalFSection;
2769:   PetscSection csection, globalCSection;
2770:   PetscInt     locRows, locCols;
2771:   PetscReal   *x, *v0, *J, *invJ, detJ;
2772:   PetscReal   *v0c, *Jc, *invJc, detJc;
2773:   PetscScalar *elemMat;
2774:   PetscInt     dim, Nf, field, totDim, cStart, cEnd, cell, ccell, s;

2776:   PetscFunctionBegin;
2777:   PetscCall(PetscLogEventBegin(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
2778:   PetscCall(DMGetCoordinateDim(dmc, &dim));
2779:   PetscCall(DMGetDS(dmc, &prob));
2780:   PetscCall(PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL));
2781:   PetscCall(PetscDSGetNumFields(prob, &Nf));
2782:   PetscCall(PetscMalloc3(dim, &v0, dim * dim, &J, dim * dim, &invJ));
2783:   PetscCall(PetscMalloc3(dim, &v0c, dim * dim, &Jc, dim * dim, &invJc));
2784:   PetscCall(DMGetLocalSection(dmf, &fsection));
2785:   PetscCall(DMGetGlobalSection(dmf, &globalFSection));
2786:   PetscCall(DMGetLocalSection(dmc, &csection));
2787:   PetscCall(DMGetGlobalSection(dmc, &globalCSection));
2788:   PetscCall(DMPlexGetSimplexOrBoxCells(dmf, 0, &cStart, &cEnd));
2789:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
2790:   PetscCall(PetscMalloc1(totDim, &elemMat));

2792:   PetscCall(MatGetLocalSize(In, &locRows, &locCols));
2793:   PetscCall(MatCreate(PetscObjectComm((PetscObject)In), &interp));
2794:   PetscCall(MatSetType(interp, MATPREALLOCATOR));
2795:   PetscCall(MatSetSizes(interp, locRows, locCols, PETSC_DETERMINE, PETSC_DETERMINE));
2796:   PetscCall(MatSetUp(interp));
2797:   for (s = 0; s < 2; ++s) {
2798:     for (field = 0; field < Nf; ++field) {
2799:       PetscObject      obj;
2800:       PetscClassId     id;
2801:       PetscDualSpace   Q = NULL;
2802:       PetscTabulation  T = NULL;
2803:       PetscQuadrature  f;
2804:       const PetscReal *qpoints, *qweights;
2805:       PetscInt         Nc, qNc, Np, fpdim, off, i, d;

2807:       PetscCall(PetscDSGetFieldOffset(prob, field, &off));
2808:       PetscCall(PetscDSGetDiscretization(prob, field, &obj));
2809:       PetscCall(PetscObjectGetClassId(obj, &id));
2810:       if (id == PETSCFE_CLASSID) {
2811:         PetscFE fe = (PetscFE)obj;

2813:         PetscCall(PetscFEGetDualSpace(fe, &Q));
2814:         PetscCall(PetscFEGetNumComponents(fe, &Nc));
2815:         if (s) PetscCall(PetscFECreateTabulation(fe, 1, 1, x, 0, &T));
2816:       } else if (id == PETSCFV_CLASSID) {
2817:         PetscFV fv = (PetscFV)obj;

2819:         PetscCall(PetscFVGetDualSpace(fv, &Q));
2820:         Nc = 1;
2821:       } else SETERRQ(PetscObjectComm((PetscObject)dmc), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
2822:       PetscCall(PetscDualSpaceGetDimension(Q, &fpdim));
2823:       /* For each fine grid cell */
2824:       for (cell = cStart; cell < cEnd; ++cell) {
2825:         PetscInt *findices, *cindices;
2826:         PetscInt  numFIndices, numCIndices;

2828:         PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
2829:         PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ));
2830:         PetscCheck(numFIndices == totDim, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fine indices %" PetscInt_FMT " != %" PetscInt_FMT " dual basis vecs", numFIndices, totDim);
2831:         for (i = 0; i < fpdim; ++i) {
2832:           Vec                pointVec;
2833:           PetscScalar       *pV;
2834:           PetscSF            coarseCellSF = NULL;
2835:           const PetscSFNode *coarseCells;
2836:           PetscInt           numCoarseCells, cpdim, row = findices[i + off], q, c, j;

2838:           /* Get points from the dual basis functional quadrature */
2839:           PetscCall(PetscDualSpaceGetFunctional(Q, i, &f));
2840:           PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, &qpoints, &qweights));
2841:           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);
2842:           PetscCall(VecCreateSeq(PETSC_COMM_SELF, Np * dim, &pointVec));
2843:           PetscCall(VecSetBlockSize(pointVec, dim));
2844:           PetscCall(VecGetArray(pointVec, &pV));
2845:           for (q = 0; q < Np; ++q) {
2846:             const PetscReal xi0[3] = {-1., -1., -1.};

2848:             /* Transform point to real space */
2849:             CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x);
2850:             for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d];
2851:           }
2852:           PetscCall(VecRestoreArray(pointVec, &pV));
2853:           /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
2854:           /* OPT: Read this out from preallocation information */
2855:           PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF));
2856:           /* Update preallocation info */
2857:           PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells));
2858:           PetscCheck(numCoarseCells == Np, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located");
2859:           PetscCall(VecGetArray(pointVec, &pV));
2860:           for (ccell = 0; ccell < numCoarseCells; ++ccell) {
2861:             PetscReal       pVReal[3];
2862:             const PetscReal xi0[3] = {-1., -1., -1.};

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

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

2874:               if (id == PETSCFE_CLASSID) {
2875:                 /* Evaluate coarse basis on contained point */
2876:                 PetscCall(PetscFEComputeTabulation((PetscFE)obj, 1, x, 0, T));
2877:                 PetscCall(PetscArrayzero(elemMat, cpdim));
2878:                 /* Get elemMat entries by multiplying by weight */
2879:                 for (j = 0; j < cpdim; ++j) {
2880:                   for (c = 0; c < Nc; ++c) elemMat[j] += T->T[0][j * Nc + c] * qweights[ccell * qNc + c];
2881:                 }
2882:               } else {
2883:                 for (j = 0; j < cpdim; ++j) {
2884:                   for (c = 0; c < Nc; ++c) elemMat[j] += 1.0 * qweights[ccell * qNc + c];
2885:                 }
2886:               }
2887:               if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat));
2888:             }
2889:             /* Update interpolator */
2890:             PetscCheck(numCIndices == totDim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, totDim);
2891:             PetscCall(MatSetValues(interp, 1, &row, cpdim, &cindices[off], elemMat, INSERT_VALUES));
2892:             PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
2893:           }
2894:           PetscCall(VecRestoreArray(pointVec, &pV));
2895:           PetscCall(PetscSFDestroy(&coarseCellSF));
2896:           PetscCall(VecDestroy(&pointVec));
2897:         }
2898:         PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
2899:       }
2900:       if (s && id == PETSCFE_CLASSID) PetscCall(PetscTabulationDestroy(&T));
2901:     }
2902:     if (!s) {
2903:       PetscCall(MatAssemblyBegin(interp, MAT_FINAL_ASSEMBLY));
2904:       PetscCall(MatAssemblyEnd(interp, MAT_FINAL_ASSEMBLY));
2905:       PetscCall(MatPreallocatorPreallocate(interp, PETSC_TRUE, In));
2906:       PetscCall(MatDestroy(&interp));
2907:       interp = In;
2908:     }
2909:   }
2910:   PetscCall(PetscFree3(v0, J, invJ));
2911:   PetscCall(PetscFree3(v0c, Jc, invJc));
2912:   PetscCall(PetscFree(elemMat));
2913:   PetscCall(MatAssemblyBegin(In, MAT_FINAL_ASSEMBLY));
2914:   PetscCall(MatAssemblyEnd(In, MAT_FINAL_ASSEMBLY));
2915:   PetscCall(PetscLogEventEnd(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
2916:   PetscFunctionReturn(PETSC_SUCCESS);
2917: }

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

2922:   Input Parameters:
2923: + dmf  - The fine mesh
2924: . dmc  - The coarse mesh
2925: - user - The user context

2927:   Output Parameter:
2928: . mass  - The mass matrix

2930:   Level: developer

2932: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeMassMatrixNested()`, `DMPlexComputeInterpolatorNested()`, `DMPlexComputeInterpolatorGeneral()`, `DMPlexComputeJacobianFEM()`
2933: @*/
2934: PetscErrorCode DMPlexComputeMassMatrixGeneral(DM dmc, DM dmf, Mat mass, void *user)
2935: {
2936:   DM_Plex     *mesh = (DM_Plex *)dmf->data;
2937:   const char  *name = "Mass Matrix";
2938:   PetscDS      prob;
2939:   PetscSection fsection, csection, globalFSection, globalCSection;
2940:   PetscHSetIJ  ht;
2941:   PetscLayout  rLayout;
2942:   PetscInt    *dnz, *onz;
2943:   PetscInt     locRows, rStart, rEnd;
2944:   PetscReal   *x, *v0, *J, *invJ, detJ;
2945:   PetscReal   *v0c, *Jc, *invJc, detJc;
2946:   PetscScalar *elemMat;
2947:   PetscInt     dim, Nf, field, totDim, cStart, cEnd, cell, ccell;

2949:   PetscFunctionBegin;
2950:   PetscCall(DMGetCoordinateDim(dmc, &dim));
2951:   PetscCall(DMGetDS(dmc, &prob));
2952:   PetscCall(PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL));
2953:   PetscCall(PetscDSGetNumFields(prob, &Nf));
2954:   PetscCall(PetscMalloc3(dim, &v0, dim * dim, &J, dim * dim, &invJ));
2955:   PetscCall(PetscMalloc3(dim, &v0c, dim * dim, &Jc, dim * dim, &invJc));
2956:   PetscCall(DMGetLocalSection(dmf, &fsection));
2957:   PetscCall(DMGetGlobalSection(dmf, &globalFSection));
2958:   PetscCall(DMGetLocalSection(dmc, &csection));
2959:   PetscCall(DMGetGlobalSection(dmc, &globalCSection));
2960:   PetscCall(DMPlexGetHeightStratum(dmf, 0, &cStart, &cEnd));
2961:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
2962:   PetscCall(PetscMalloc1(totDim, &elemMat));

2964:   PetscCall(MatGetLocalSize(mass, &locRows, NULL));
2965:   PetscCall(PetscLayoutCreate(PetscObjectComm((PetscObject)mass), &rLayout));
2966:   PetscCall(PetscLayoutSetLocalSize(rLayout, locRows));
2967:   PetscCall(PetscLayoutSetBlockSize(rLayout, 1));
2968:   PetscCall(PetscLayoutSetUp(rLayout));
2969:   PetscCall(PetscLayoutGetRange(rLayout, &rStart, &rEnd));
2970:   PetscCall(PetscLayoutDestroy(&rLayout));
2971:   PetscCall(PetscCalloc2(locRows, &dnz, locRows, &onz));
2972:   PetscCall(PetscHSetIJCreate(&ht));
2973:   for (field = 0; field < Nf; ++field) {
2974:     PetscObject      obj;
2975:     PetscClassId     id;
2976:     PetscQuadrature  quad;
2977:     const PetscReal *qpoints;
2978:     PetscInt         Nq, Nc, i, d;

2980:     PetscCall(PetscDSGetDiscretization(prob, field, &obj));
2981:     PetscCall(PetscObjectGetClassId(obj, &id));
2982:     if (id == PETSCFE_CLASSID) PetscCall(PetscFEGetQuadrature((PetscFE)obj, &quad));
2983:     else PetscCall(PetscFVGetQuadrature((PetscFV)obj, &quad));
2984:     PetscCall(PetscQuadratureGetData(quad, NULL, &Nc, &Nq, &qpoints, NULL));
2985:     /* For each fine grid cell */
2986:     for (cell = cStart; cell < cEnd; ++cell) {
2987:       Vec                pointVec;
2988:       PetscScalar       *pV;
2989:       PetscSF            coarseCellSF = NULL;
2990:       const PetscSFNode *coarseCells;
2991:       PetscInt           numCoarseCells, q, c;
2992:       PetscInt          *findices, *cindices;
2993:       PetscInt           numFIndices, numCIndices;

2995:       PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
2996:       PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ));
2997:       /* Get points from the quadrature */
2998:       PetscCall(VecCreateSeq(PETSC_COMM_SELF, Nq * dim, &pointVec));
2999:       PetscCall(VecSetBlockSize(pointVec, dim));
3000:       PetscCall(VecGetArray(pointVec, &pV));
3001:       for (q = 0; q < Nq; ++q) {
3002:         const PetscReal xi0[3] = {-1., -1., -1.};

3004:         /* Transform point to real space */
3005:         CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x);
3006:         for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d];
3007:       }
3008:       PetscCall(VecRestoreArray(pointVec, &pV));
3009:       /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
3010:       PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF));
3011:       PetscCall(PetscSFViewFromOptions(coarseCellSF, NULL, "-interp_sf_view"));
3012:       /* Update preallocation info */
3013:       PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells));
3014:       PetscCheck(numCoarseCells == Nq, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located");
3015:       {
3016:         PetscHashIJKey key;
3017:         PetscBool      missing;

3019:         for (i = 0; i < numFIndices; ++i) {
3020:           key.i = findices[i];
3021:           if (key.i >= 0) {
3022:             /* Get indices for coarse elements */
3023:             for (ccell = 0; ccell < numCoarseCells; ++ccell) {
3024:               PetscCall(DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3025:               for (c = 0; c < numCIndices; ++c) {
3026:                 key.j = cindices[c];
3027:                 if (key.j < 0) continue;
3028:                 PetscCall(PetscHSetIJQueryAdd(ht, key, &missing));
3029:                 if (missing) {
3030:                   if ((key.j >= rStart) && (key.j < rEnd)) ++dnz[key.i - rStart];
3031:                   else ++onz[key.i - rStart];
3032:                 }
3033:               }
3034:               PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3035:             }
3036:           }
3037:         }
3038:       }
3039:       PetscCall(PetscSFDestroy(&coarseCellSF));
3040:       PetscCall(VecDestroy(&pointVec));
3041:       PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3042:     }
3043:   }
3044:   PetscCall(PetscHSetIJDestroy(&ht));
3045:   PetscCall(MatXAIJSetPreallocation(mass, 1, dnz, onz, NULL, NULL));
3046:   PetscCall(MatSetOption(mass, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_TRUE));
3047:   PetscCall(PetscFree2(dnz, onz));
3048:   for (field = 0; field < Nf; ++field) {
3049:     PetscObject      obj;
3050:     PetscClassId     id;
3051:     PetscTabulation  T, Tfine;
3052:     PetscQuadrature  quad;
3053:     const PetscReal *qpoints, *qweights;
3054:     PetscInt         Nq, Nc, i, d;

3056:     PetscCall(PetscDSGetDiscretization(prob, field, &obj));
3057:     PetscCall(PetscObjectGetClassId(obj, &id));
3058:     if (id == PETSCFE_CLASSID) {
3059:       PetscCall(PetscFEGetQuadrature((PetscFE)obj, &quad));
3060:       PetscCall(PetscFEGetCellTabulation((PetscFE)obj, 1, &Tfine));
3061:       PetscCall(PetscFECreateTabulation((PetscFE)obj, 1, 1, x, 0, &T));
3062:     } else {
3063:       PetscCall(PetscFVGetQuadrature((PetscFV)obj, &quad));
3064:     }
3065:     PetscCall(PetscQuadratureGetData(quad, NULL, &Nc, &Nq, &qpoints, &qweights));
3066:     /* For each fine grid cell */
3067:     for (cell = cStart; cell < cEnd; ++cell) {
3068:       Vec                pointVec;
3069:       PetscScalar       *pV;
3070:       PetscSF            coarseCellSF = NULL;
3071:       const PetscSFNode *coarseCells;
3072:       PetscInt           numCoarseCells, cpdim, q, c, j;
3073:       PetscInt          *findices, *cindices;
3074:       PetscInt           numFIndices, numCIndices;

3076:       PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3077:       PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ));
3078:       /* Get points from the quadrature */
3079:       PetscCall(VecCreateSeq(PETSC_COMM_SELF, Nq * dim, &pointVec));
3080:       PetscCall(VecSetBlockSize(pointVec, dim));
3081:       PetscCall(VecGetArray(pointVec, &pV));
3082:       for (q = 0; q < Nq; ++q) {
3083:         const PetscReal xi0[3] = {-1., -1., -1.};

3085:         /* Transform point to real space */
3086:         CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x);
3087:         for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d];
3088:       }
3089:       PetscCall(VecRestoreArray(pointVec, &pV));
3090:       /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
3091:       PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF));
3092:       /* Update matrix */
3093:       PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells));
3094:       PetscCheck(numCoarseCells == Nq, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located");
3095:       PetscCall(VecGetArray(pointVec, &pV));
3096:       for (ccell = 0; ccell < numCoarseCells; ++ccell) {
3097:         PetscReal       pVReal[3];
3098:         const PetscReal xi0[3] = {-1., -1., -1.};

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

3106:         if (id == PETSCFE_CLASSID) {
3107:           PetscFE fe = (PetscFE)obj;

3109:           /* Evaluate coarse basis on contained point */
3110:           PetscCall(PetscFEGetDimension(fe, &cpdim));
3111:           PetscCall(PetscFEComputeTabulation(fe, 1, x, 0, T));
3112:           /* Get elemMat entries by multiplying by weight */
3113:           for (i = 0; i < numFIndices; ++i) {
3114:             PetscCall(PetscArrayzero(elemMat, cpdim));
3115:             for (j = 0; j < cpdim; ++j) {
3116:               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;
3117:             }
3118:             /* Update interpolator */
3119:             if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat));
3120:             PetscCheck(numCIndices == cpdim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, cpdim);
3121:             PetscCall(MatSetValues(mass, 1, &findices[i], numCIndices, cindices, elemMat, ADD_VALUES));
3122:           }
3123:         } else {
3124:           cpdim = 1;
3125:           for (i = 0; i < numFIndices; ++i) {
3126:             PetscCall(PetscArrayzero(elemMat, cpdim));
3127:             for (j = 0; j < cpdim; ++j) {
3128:               for (c = 0; c < Nc; ++c) elemMat[j] += 1.0 * 1.0 * qweights[ccell * Nc + c] * detJ;
3129:             }
3130:             /* Update interpolator */
3131:             if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat));
3132:             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));
3133:             PetscCheck(numCIndices == cpdim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, cpdim);
3134:             PetscCall(MatSetValues(mass, 1, &findices[i], numCIndices, cindices, elemMat, ADD_VALUES));
3135:           }
3136:         }
3137:         PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3138:       }
3139:       PetscCall(VecRestoreArray(pointVec, &pV));
3140:       PetscCall(PetscSFDestroy(&coarseCellSF));
3141:       PetscCall(VecDestroy(&pointVec));
3142:       PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3143:     }
3144:     if (id == PETSCFE_CLASSID) PetscCall(PetscTabulationDestroy(&T));
3145:   }
3146:   PetscCall(PetscFree3(v0, J, invJ));
3147:   PetscCall(PetscFree3(v0c, Jc, invJc));
3148:   PetscCall(PetscFree(elemMat));
3149:   PetscCall(MatAssemblyBegin(mass, MAT_FINAL_ASSEMBLY));
3150:   PetscCall(MatAssemblyEnd(mass, MAT_FINAL_ASSEMBLY));
3151:   PetscFunctionReturn(PETSC_SUCCESS);
3152: }

3154: /*@
3155:   DMPlexComputeInjectorFEM - Compute a mapping from coarse unknowns to fine unknowns

3157:   Input Parameters:
3158: + dmc  - The coarse mesh
3159: - dmf  - The fine mesh
3160: - user - The user context

3162:   Output Parameter:
3163: . sc   - The mapping

3165:   Level: developer

3167: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorNested()`, `DMPlexComputeJacobianFEM()`
3168: @*/
3169: PetscErrorCode DMPlexComputeInjectorFEM(DM dmc, DM dmf, VecScatter *sc, void *user)
3170: {
3171:   PetscDS      prob;
3172:   PetscFE     *feRef;
3173:   PetscFV     *fvRef;
3174:   Vec          fv, cv;
3175:   IS           fis, cis;
3176:   PetscSection fsection, fglobalSection, csection, cglobalSection;
3177:   PetscInt    *cmap, *cellCIndices, *cellFIndices, *cindices, *findices;
3178:   PetscInt     cTotDim, fTotDim = 0, Nf, f, field, cStart, cEnd, c, dim, d, startC, endC, offsetC, offsetF, m;
3179:   PetscBool   *needAvg;

3181:   PetscFunctionBegin;
3182:   PetscCall(PetscLogEventBegin(DMPLEX_InjectorFEM, dmc, dmf, 0, 0));
3183:   PetscCall(DMGetDimension(dmf, &dim));
3184:   PetscCall(DMGetLocalSection(dmf, &fsection));
3185:   PetscCall(DMGetGlobalSection(dmf, &fglobalSection));
3186:   PetscCall(DMGetLocalSection(dmc, &csection));
3187:   PetscCall(DMGetGlobalSection(dmc, &cglobalSection));
3188:   PetscCall(PetscSectionGetNumFields(fsection, &Nf));
3189:   PetscCall(DMPlexGetSimplexOrBoxCells(dmc, 0, &cStart, &cEnd));
3190:   PetscCall(DMGetDS(dmc, &prob));
3191:   PetscCall(PetscCalloc3(Nf, &feRef, Nf, &fvRef, Nf, &needAvg));
3192:   for (f = 0; f < Nf; ++f) {
3193:     PetscObject  obj;
3194:     PetscClassId id;
3195:     PetscInt     fNb = 0, Nc = 0;

3197:     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
3198:     PetscCall(PetscObjectGetClassId(obj, &id));
3199:     if (id == PETSCFE_CLASSID) {
3200:       PetscFE    fe = (PetscFE)obj;
3201:       PetscSpace sp;
3202:       PetscInt   maxDegree;

3204:       PetscCall(PetscFERefine(fe, &feRef[f]));
3205:       PetscCall(PetscFEGetDimension(feRef[f], &fNb));
3206:       PetscCall(PetscFEGetNumComponents(fe, &Nc));
3207:       PetscCall(PetscFEGetBasisSpace(fe, &sp));
3208:       PetscCall(PetscSpaceGetDegree(sp, NULL, &maxDegree));
3209:       if (!maxDegree) needAvg[f] = PETSC_TRUE;
3210:     } else if (id == PETSCFV_CLASSID) {
3211:       PetscFV        fv = (PetscFV)obj;
3212:       PetscDualSpace Q;

3214:       PetscCall(PetscFVRefine(fv, &fvRef[f]));
3215:       PetscCall(PetscFVGetDualSpace(fvRef[f], &Q));
3216:       PetscCall(PetscDualSpaceGetDimension(Q, &fNb));
3217:       PetscCall(PetscFVGetNumComponents(fv, &Nc));
3218:       needAvg[f] = PETSC_TRUE;
3219:     }
3220:     fTotDim += fNb;
3221:   }
3222:   PetscCall(PetscDSGetTotalDimension(prob, &cTotDim));
3223:   PetscCall(PetscMalloc1(cTotDim, &cmap));
3224:   for (field = 0, offsetC = 0, offsetF = 0; field < Nf; ++field) {
3225:     PetscFE        feC;
3226:     PetscFV        fvC;
3227:     PetscDualSpace QF, QC;
3228:     PetscInt       order = -1, NcF, NcC, fpdim, cpdim;

3230:     if (feRef[field]) {
3231:       PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&feC));
3232:       PetscCall(PetscFEGetNumComponents(feC, &NcC));
3233:       PetscCall(PetscFEGetNumComponents(feRef[field], &NcF));
3234:       PetscCall(PetscFEGetDualSpace(feRef[field], &QF));
3235:       PetscCall(PetscDualSpaceGetOrder(QF, &order));
3236:       PetscCall(PetscDualSpaceGetDimension(QF, &fpdim));
3237:       PetscCall(PetscFEGetDualSpace(feC, &QC));
3238:       PetscCall(PetscDualSpaceGetDimension(QC, &cpdim));
3239:     } else {
3240:       PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fvC));
3241:       PetscCall(PetscFVGetNumComponents(fvC, &NcC));
3242:       PetscCall(PetscFVGetNumComponents(fvRef[field], &NcF));
3243:       PetscCall(PetscFVGetDualSpace(fvRef[field], &QF));
3244:       PetscCall(PetscDualSpaceGetDimension(QF, &fpdim));
3245:       PetscCall(PetscFVGetDualSpace(fvC, &QC));
3246:       PetscCall(PetscDualSpaceGetDimension(QC, &cpdim));
3247:     }
3248:     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);
3249:     for (c = 0; c < cpdim; ++c) {
3250:       PetscQuadrature  cfunc;
3251:       const PetscReal *cqpoints, *cqweights;
3252:       PetscInt         NqcC, NpC;
3253:       PetscBool        found = PETSC_FALSE;

3255:       PetscCall(PetscDualSpaceGetFunctional(QC, c, &cfunc));
3256:       PetscCall(PetscQuadratureGetData(cfunc, NULL, &NqcC, &NpC, &cqpoints, &cqweights));
3257:       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);
3258:       PetscCheck(NpC == 1 || !feRef[field], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Do not know how to do injection for moments");
3259:       for (f = 0; f < fpdim; ++f) {
3260:         PetscQuadrature  ffunc;
3261:         const PetscReal *fqpoints, *fqweights;
3262:         PetscReal        sum = 0.0;
3263:         PetscInt         NqcF, NpF;

3265:         PetscCall(PetscDualSpaceGetFunctional(QF, f, &ffunc));
3266:         PetscCall(PetscQuadratureGetData(ffunc, NULL, &NqcF, &NpF, &fqpoints, &fqweights));
3267:         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);
3268:         if (NpC != NpF) continue;
3269:         for (d = 0; d < dim; ++d) sum += PetscAbsReal(cqpoints[d] - fqpoints[d]);
3270:         if (sum > 1.0e-9) continue;
3271:         for (d = 0; d < NcC; ++d) sum += PetscAbsReal(cqweights[d] * fqweights[d]);
3272:         if (sum < 1.0e-9) continue;
3273:         cmap[offsetC + c] = offsetF + f;
3274:         found             = PETSC_TRUE;
3275:         break;
3276:       }
3277:       if (!found) {
3278:         /* TODO We really want the average here, but some asshole put VecScatter in the interface */
3279:         if (fvRef[field] || (feRef[field] && order == 0)) {
3280:           cmap[offsetC + c] = offsetF + 0;
3281:         } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Could not locate matching functional for injection");
3282:       }
3283:     }
3284:     offsetC += cpdim;
3285:     offsetF += fpdim;
3286:   }
3287:   for (f = 0; f < Nf; ++f) {
3288:     PetscCall(PetscFEDestroy(&feRef[f]));
3289:     PetscCall(PetscFVDestroy(&fvRef[f]));
3290:   }
3291:   PetscCall(PetscFree3(feRef, fvRef, needAvg));

3293:   PetscCall(DMGetGlobalVector(dmf, &fv));
3294:   PetscCall(DMGetGlobalVector(dmc, &cv));
3295:   PetscCall(VecGetOwnershipRange(cv, &startC, &endC));
3296:   PetscCall(PetscSectionGetConstrainedStorageSize(cglobalSection, &m));
3297:   PetscCall(PetscMalloc2(cTotDim, &cellCIndices, fTotDim, &cellFIndices));
3298:   PetscCall(PetscMalloc1(m, &cindices));
3299:   PetscCall(PetscMalloc1(m, &findices));
3300:   for (d = 0; d < m; ++d) cindices[d] = findices[d] = -1;
3301:   for (c = cStart; c < cEnd; ++c) {
3302:     PetscCall(DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, c, cellCIndices, cellFIndices));
3303:     for (d = 0; d < cTotDim; ++d) {
3304:       if ((cellCIndices[d] < startC) || (cellCIndices[d] >= endC)) continue;
3305:       PetscCheck(!(findices[cellCIndices[d] - startC] >= 0) || !(findices[cellCIndices[d] - startC] != cellFIndices[cmap[d]]), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Coarse dof %" PetscInt_FMT " maps to both %" PetscInt_FMT " and %" PetscInt_FMT, cindices[cellCIndices[d] - startC], findices[cellCIndices[d] - startC], cellFIndices[cmap[d]]);
3306:       cindices[cellCIndices[d] - startC] = cellCIndices[d];
3307:       findices[cellCIndices[d] - startC] = cellFIndices[cmap[d]];
3308:     }
3309:   }
3310:   PetscCall(PetscFree(cmap));
3311:   PetscCall(PetscFree2(cellCIndices, cellFIndices));

3313:   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, m, cindices, PETSC_OWN_POINTER, &cis));
3314:   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, m, findices, PETSC_OWN_POINTER, &fis));
3315:   PetscCall(VecScatterCreate(cv, cis, fv, fis, sc));
3316:   PetscCall(ISDestroy(&cis));
3317:   PetscCall(ISDestroy(&fis));
3318:   PetscCall(DMRestoreGlobalVector(dmf, &fv));
3319:   PetscCall(DMRestoreGlobalVector(dmc, &cv));
3320:   PetscCall(PetscLogEventEnd(DMPLEX_InjectorFEM, dmc, dmf, 0, 0));
3321:   PetscFunctionReturn(PETSC_SUCCESS);
3322: }

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

3327:   Input Parameters:
3328: + dm     - The `DM`
3329: . cellIS - The cells to include
3330: . locX   - A local vector with the solution fields
3331: . locX_t - A local vector with solution field time derivatives, or NULL
3332: - locA   - A local vector with auxiliary fields, or NULL

3334:   Output Parameters:
3335: + u   - The field coefficients
3336: . u_t - The fields derivative coefficients
3337: - a   - The auxiliary field coefficients

3339:   Level: developer

3341: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
3342: @*/
3343: PetscErrorCode DMPlexGetCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
3344: {
3345:   DM              plex, plexA = NULL;
3346:   DMEnclosureType encAux;
3347:   PetscSection    section, sectionAux;
3348:   PetscDS         prob;
3349:   const PetscInt *cells;
3350:   PetscInt        cStart, cEnd, numCells, totDim, totDimAux, c;

3352:   PetscFunctionBegin;
3360:   PetscCall(DMPlexConvertPlex(dm, &plex, PETSC_FALSE));
3361:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
3362:   PetscCall(DMGetLocalSection(dm, &section));
3363:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL));
3364:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
3365:   if (locA) {
3366:     DM      dmAux;
3367:     PetscDS probAux;

3369:     PetscCall(VecGetDM(locA, &dmAux));
3370:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
3371:     PetscCall(DMPlexConvertPlex(dmAux, &plexA, PETSC_FALSE));
3372:     PetscCall(DMGetLocalSection(dmAux, &sectionAux));
3373:     PetscCall(DMGetDS(dmAux, &probAux));
3374:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
3375:   }
3376:   numCells = cEnd - cStart;
3377:   PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u));
3378:   if (locX_t) PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u_t));
3379:   else *u_t = NULL;
3380:   if (locA) PetscCall(DMGetWorkArray(dm, numCells * totDimAux, MPIU_SCALAR, a));
3381:   else *a = NULL;
3382:   for (c = cStart; c < cEnd; ++c) {
3383:     const PetscInt cell = cells ? cells[c] : c;
3384:     const PetscInt cind = c - cStart;
3385:     PetscScalar   *x = NULL, *x_t = NULL, *ul = *u, *ul_t = *u_t, *al = *a;
3386:     PetscInt       i;

3388:     PetscCall(DMPlexVecGetClosure(plex, section, locX, cell, NULL, &x));
3389:     for (i = 0; i < totDim; ++i) ul[cind * totDim + i] = x[i];
3390:     PetscCall(DMPlexVecRestoreClosure(plex, section, locX, cell, NULL, &x));
3391:     if (locX_t) {
3392:       PetscCall(DMPlexVecGetClosure(plex, section, locX_t, cell, NULL, &x_t));
3393:       for (i = 0; i < totDim; ++i) ul_t[cind * totDim + i] = x_t[i];
3394:       PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, cell, NULL, &x_t));
3395:     }
3396:     if (locA) {
3397:       PetscInt subcell;
3398:       PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, cell, &subcell));
3399:       PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subcell, NULL, &x));
3400:       for (i = 0; i < totDimAux; ++i) al[cind * totDimAux + i] = x[i];
3401:       PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subcell, NULL, &x));
3402:     }
3403:   }
3404:   PetscCall(DMDestroy(&plex));
3405:   if (locA) PetscCall(DMDestroy(&plexA));
3406:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
3407:   PetscFunctionReturn(PETSC_SUCCESS);
3408: }

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

3413:   Input Parameters:
3414: + dm     - The `DM`
3415: . cellIS - The cells to include
3416: . locX   - A local vector with the solution fields
3417: . locX_t - A local vector with solution field time derivatives, or NULL
3418: - locA   - A local vector with auxiliary fields, or NULL

3420:   Output Parameters:
3421: + u   - The field coefficients
3422: . u_t - The fields derivative coefficients
3423: - a   - The auxiliary field coefficients

3425:   Level: developer

3427: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
3428: @*/
3429: PetscErrorCode DMPlexRestoreCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
3430: {
3431:   PetscFunctionBegin;
3432:   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u));
3433:   if (locX_t) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u_t));
3434:   if (locA) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, a));
3435:   PetscFunctionReturn(PETSC_SUCCESS);
3436: }

3438: PetscErrorCode DMPlexGetHybridCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
3439: {
3440:   DM              plex, plexA = NULL;
3441:   DMEnclosureType encAux;
3442:   PetscSection    section, sectionAux;
3443:   PetscDS         ds, dsIn;
3444:   const PetscInt *cells;
3445:   PetscInt        cStart, cEnd, numCells, c, totDim, totDimAux, Nf, f;

3447:   PetscFunctionBegin;
3456:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
3457:   numCells = cEnd - cStart;
3458:   PetscCall(DMPlexConvertPlex(dm, &plex, PETSC_FALSE));
3459:   PetscCall(DMGetLocalSection(dm, &section));
3460:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn));
3461:   PetscCall(PetscDSGetNumFields(dsIn, &Nf));
3462:   PetscCall(PetscDSGetTotalDimension(dsIn, &totDim));
3463:   if (locA) {
3464:     DM      dmAux;
3465:     PetscDS probAux;

3467:     PetscCall(VecGetDM(locA, &dmAux));
3468:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
3469:     PetscCall(DMPlexConvertPlex(dmAux, &plexA, PETSC_FALSE));
3470:     PetscCall(DMGetLocalSection(dmAux, &sectionAux));
3471:     PetscCall(DMGetDS(dmAux, &probAux));
3472:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
3473:   }
3474:   PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u));
3475:   if (locX_t) PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u_t));
3476:   else {
3477:     *u_t = NULL;
3478:   }
3479:   if (locA) PetscCall(DMGetWorkArray(dm, numCells * totDimAux, MPIU_SCALAR, a));
3480:   else {
3481:     *a = NULL;
3482:   }
3483:   // Loop over cohesive cells
3484:   for (c = cStart; c < cEnd; ++c) {
3485:     const PetscInt  cell = cells ? cells[c] : c;
3486:     const PetscInt  cind = c - cStart;
3487:     PetscScalar    *xf = NULL, *xc = NULL, *x = NULL, *x_t = NULL, *ul = &(*u)[cind * totDim];
3488:     const PetscInt *cone, *ornt;
3489:     PetscInt        Nx = 0, Nxf, s;

3491:     PetscCall(DMPlexGetCone(dm, cell, &cone));
3492:     PetscCall(DMPlexGetConeOrientation(dm, cell, &ornt));
3493:     // Put in cohesive unknowns
3494:     PetscCall(DMPlexVecGetClosure(plex, section, locX, cell, &Nxf, &xf));
3495:     for (f = 0; f < Nf; ++f) {
3496:       PetscInt  fdofIn, foff, foffIn;
3497:       PetscBool cohesive;

3499:       PetscCall(PetscDSGetCohesive(dsIn, f, &cohesive));
3500:       if (!cohesive) continue;
3501:       PetscCall(PetscDSGetFieldSize(dsIn, f, &fdofIn));
3502:       PetscCall(PetscDSGetFieldOffsetCohesive(ds, f, &foff));
3503:       PetscCall(PetscDSGetFieldOffsetCohesive(dsIn, f, &foffIn));
3504:       for (PetscInt i = 0; i < fdofIn; ++i) ul[foffIn + i] = xf[foff + i];
3505:       Nx += fdofIn;
3506:     }
3507:     PetscCall(DMPlexVecRestoreClosure(plex, section, locX, cell, &Nxf, &xf));
3508:     // Loop over sides of surface
3509:     for (s = 0; s < 2; ++s) {
3510:       const PetscInt *support;
3511:       const PetscInt  face = cone[s];
3512:       PetscInt        ssize, ncell, Nxc;

3514:       // I don't think I need the face to have 0 orientation in the hybrid cell
3515:       //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]);
3516:       PetscCall(DMPlexGetSupport(dm, face, &support));
3517:       PetscCall(DMPlexGetSupportSize(dm, face, &ssize));
3518:       if (support[0] == cell) ncell = support[1];
3519:       else if (support[1] == cell) ncell = support[0];
3520:       else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", face, cell);
3521:       // Get closure of both face and cell, stick in cell for normal fields and face for cohesive fields
3522:       PetscCall(DMPlexVecGetClosure(plex, section, locX, ncell, &Nxc, &xc));
3523:       for (f = 0; f < Nf; ++f) {
3524:         PetscInt  fdofIn, foffIn;
3525:         PetscBool cohesive;

3527:         PetscCall(PetscDSGetCohesive(dsIn, f, &cohesive));
3528:         if (cohesive) continue;
3529:         PetscCall(PetscDSGetFieldSize(dsIn, f, &fdofIn));
3530:         PetscCall(PetscDSGetFieldOffsetCohesive(dsIn, f, &foffIn));
3531:         for (PetscInt i = 0; i < fdofIn; ++i) ul[foffIn + s * fdofIn + i] = xc[foffIn + i];
3532:         Nx += fdofIn;
3533:       }
3534:       PetscCall(DMPlexVecRestoreClosure(plex, section, locX, ncell, &Nxc, &xc));
3535:     }
3536:     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);

3538:     if (locX_t) {
3539:       PetscScalar *ul_t = &(*u_t)[cind * totDim];

3541:       PetscCall(DMPlexVecGetClosure(plex, section, locX_t, cell, NULL, &x_t));
3542:       for (PetscInt i = 0; i < totDim; ++i) ul_t[i] = x_t[i];
3543:       PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, cell, NULL, &x_t));
3544:     }
3545:     if (locA) {
3546:       PetscScalar *al = &(*a)[cind * totDimAux];
3547:       PetscInt     subcell;

3549:       PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, cell, &subcell));
3550:       PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subcell, &Nx, &x));
3551:       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);
3552:       for (PetscInt i = 0; i < totDimAux; ++i) al[i] = x[i];
3553:       PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subcell, &Nx, &x));
3554:     }
3555:   }
3556:   PetscCall(DMDestroy(&plex));
3557:   PetscCall(DMDestroy(&plexA));
3558:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
3559:   PetscFunctionReturn(PETSC_SUCCESS);
3560: }

3562: /*
3563:   DMPlexGetHybridFields - Get the field values for the negative side (s = 0) and positive side (s = 1) of the interfaace

3565:   Input Parameters:
3566: + dm      - The full domain DM
3567: . dmX     - An array of DM for the field, say an auxiliary DM, indexed by s
3568: . dsX     - An array of PetscDS for the field, indexed by s
3569: . cellIS  - The interface cells for which we want values
3570: . locX    - An array of local vectors with the field values, indexed by s
3571: - useCell - Flag to have values come from neighboring cell rather than endcap face

3573:   Output Parameter:
3574: . x       - An array of field values, indexed by s

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

3579:   Level: advanced

3581: .seealso: `DMPlexRestoreHybridFields()`, `DMGetWorkArray()`
3582: */
3583: static PetscErrorCode DMPlexGetHybridFields(DM dm, DM dmX[], PetscDS dsX[], IS cellIS, Vec locX[], PetscBool useCell, PetscScalar *x[])
3584: {
3585:   DM              plexX[2];
3586:   DMEnclosureType encX[2];
3587:   PetscSection    sectionX[2];
3588:   const PetscInt *cells;
3589:   PetscInt        cStart, cEnd, numCells, c, s, totDimX[2];

3591:   PetscFunctionBegin;
3593:   if (!locX[0] || !locX[1]) PetscFunctionReturn(PETSC_SUCCESS);
3598:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
3599:   numCells = cEnd - cStart;
3600:   for (s = 0; s < 2; ++s) {
3604:     PetscCall(DMPlexConvertPlex(dmX[s], &plexX[s], PETSC_FALSE));
3605:     PetscCall(DMGetEnclosureRelation(dmX[s], dm, &encX[s]));
3606:     PetscCall(DMGetLocalSection(dmX[s], &sectionX[s]));
3607:     PetscCall(PetscDSGetTotalDimension(dsX[s], &totDimX[s]));
3608:     PetscCall(DMGetWorkArray(dmX[s], numCells * totDimX[s], MPIU_SCALAR, &x[s]));
3609:   }
3610:   for (c = cStart; c < cEnd; ++c) {
3611:     const PetscInt  cell = cells ? cells[c] : c;
3612:     const PetscInt  cind = c - cStart;
3613:     const PetscInt *cone, *ornt;

3615:     PetscCall(DMPlexGetCone(dm, cell, &cone));
3616:     PetscCall(DMPlexGetConeOrientation(dm, cell, &ornt));
3617:     //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]);
3618:     for (s = 0; s < 2; ++s) {
3619:       const PetscInt tdX     = totDimX[s];
3620:       PetscScalar   *closure = NULL, *xl = &x[s][cind * tdX];
3621:       PetscInt       face = cone[s], point = face, subpoint, Nx, i;

3623:       if (useCell) {
3624:         const PetscInt *support;
3625:         PetscInt        ssize;

3627:         PetscCall(DMPlexGetSupport(dm, face, &support));
3628:         PetscCall(DMPlexGetSupportSize(dm, face, &ssize));
3629:         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);
3630:         if (support[0] == cell) point = support[1];
3631:         else if (support[1] == cell) point = support[0];
3632:         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", face, cell);
3633:       }
3634:       PetscCall(DMGetEnclosurePoint(plexX[s], dm, encX[s], point, &subpoint));
3635:       PetscCall(DMPlexVecGetOrientedClosure_Internal(plexX[s], sectionX[s], locX[s], subpoint, ornt[s], &Nx, &closure));
3636:       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);
3637:       for (i = 0; i < Nx; ++i) xl[i] = closure[i];
3638:       PetscCall(DMPlexVecRestoreClosure(plexX[s], sectionX[s], locX[s], subpoint, &Nx, &closure));
3639:     }
3640:   }
3641:   for (s = 0; s < 2; ++s) PetscCall(DMDestroy(&plexX[s]));
3642:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
3643:   PetscFunctionReturn(PETSC_SUCCESS);
3644: }

3646: static PetscErrorCode DMPlexRestoreHybridFields(DM dm, DM dmX[], PetscDS dsX[], IS cellIS, Vec locX[], PetscBool useCell, PetscScalar *x[])
3647: {
3648:   PetscFunctionBegin;
3649:   if (!locX[0] || !locX[1]) PetscFunctionReturn(PETSC_SUCCESS);
3650:   PetscCall(DMRestoreWorkArray(dmX[0], 0, MPIU_SCALAR, &x[0]));
3651:   PetscCall(DMRestoreWorkArray(dmX[1], 0, MPIU_SCALAR, &x[1]));
3652:   PetscFunctionReturn(PETSC_SUCCESS);
3653: }

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

3658:   Input Parameters:
3659: + dm     - The `DM`
3660: . fStart - The first face to include
3661: . fEnd   - The first face to exclude
3662: . locX   - A local vector with the solution fields
3663: . locX_t - A local vector with solution field time derivatives, or NULL
3664: . faceGeometry - A local vector with face geometry
3665: . cellGeometry - A local vector with cell geometry
3666: - locaGrad - A local vector with field gradients, or NULL

3668:   Output Parameters:
3669: + Nface - The number of faces with field values
3670: . uL - The field values at the left side of the face
3671: - uR - The field values at the right side of the face

3673:   Level: developer

3675: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellFields()`
3676: @*/
3677: PetscErrorCode DMPlexGetFaceFields(DM dm, PetscInt fStart, PetscInt fEnd, Vec locX, Vec locX_t, Vec faceGeometry, Vec cellGeometry, Vec locGrad, PetscInt *Nface, PetscScalar **uL, PetscScalar **uR)
3678: {
3679:   DM                 dmFace, dmCell, dmGrad = NULL;
3680:   PetscSection       section;
3681:   PetscDS            prob;
3682:   DMLabel            ghostLabel;
3683:   const PetscScalar *facegeom, *cellgeom, *x, *lgrad;
3684:   PetscBool         *isFE;
3685:   PetscInt           dim, Nf, f, Nc, numFaces = fEnd - fStart, iface, face;

3687:   PetscFunctionBegin;
3696:   PetscCall(DMGetDimension(dm, &dim));
3697:   PetscCall(DMGetDS(dm, &prob));
3698:   PetscCall(DMGetLocalSection(dm, &section));
3699:   PetscCall(PetscDSGetNumFields(prob, &Nf));
3700:   PetscCall(PetscDSGetTotalComponents(prob, &Nc));
3701:   PetscCall(PetscMalloc1(Nf, &isFE));
3702:   for (f = 0; f < Nf; ++f) {
3703:     PetscObject  obj;
3704:     PetscClassId id;

3706:     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
3707:     PetscCall(PetscObjectGetClassId(obj, &id));
3708:     if (id == PETSCFE_CLASSID) {
3709:       isFE[f] = PETSC_TRUE;
3710:     } else if (id == PETSCFV_CLASSID) {
3711:       isFE[f] = PETSC_FALSE;
3712:     } else {
3713:       isFE[f] = PETSC_FALSE;
3714:     }
3715:   }
3716:   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
3717:   PetscCall(VecGetArrayRead(locX, &x));
3718:   PetscCall(VecGetDM(faceGeometry, &dmFace));
3719:   PetscCall(VecGetArrayRead(faceGeometry, &facegeom));
3720:   PetscCall(VecGetDM(cellGeometry, &dmCell));
3721:   PetscCall(VecGetArrayRead(cellGeometry, &cellgeom));
3722:   if (locGrad) {
3723:     PetscCall(VecGetDM(locGrad, &dmGrad));
3724:     PetscCall(VecGetArrayRead(locGrad, &lgrad));
3725:   }
3726:   PetscCall(DMGetWorkArray(dm, numFaces * Nc, MPIU_SCALAR, uL));
3727:   PetscCall(DMGetWorkArray(dm, numFaces * Nc, MPIU_SCALAR, uR));
3728:   /* Right now just eat the extra work for FE (could make a cell loop) */
3729:   for (face = fStart, iface = 0; face < fEnd; ++face) {
3730:     const PetscInt  *cells;
3731:     PetscFVFaceGeom *fg;
3732:     PetscFVCellGeom *cgL, *cgR;
3733:     PetscScalar     *xL, *xR, *gL, *gR;
3734:     PetscScalar     *uLl = *uL, *uRl = *uR;
3735:     PetscInt         ghost, nsupp, nchild;

3737:     PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
3738:     PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
3739:     PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
3740:     if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
3741:     PetscCall(DMPlexPointLocalRead(dmFace, face, facegeom, &fg));
3742:     PetscCall(DMPlexGetSupport(dm, face, &cells));
3743:     PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL));
3744:     PetscCall(DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR));
3745:     for (f = 0; f < Nf; ++f) {
3746:       PetscInt off;

3748:       PetscCall(PetscDSGetComponentOffset(prob, f, &off));
3749:       if (isFE[f]) {
3750:         const PetscInt *cone;
3751:         PetscInt        comp, coneSizeL, coneSizeR, faceLocL, faceLocR, ldof, rdof, d;

3753:         xL = xR = NULL;
3754:         PetscCall(PetscSectionGetFieldComponents(section, f, &comp));
3755:         PetscCall(DMPlexVecGetClosure(dm, section, locX, cells[0], &ldof, (PetscScalar **)&xL));
3756:         PetscCall(DMPlexVecGetClosure(dm, section, locX, cells[1], &rdof, (PetscScalar **)&xR));
3757:         PetscCall(DMPlexGetCone(dm, cells[0], &cone));
3758:         PetscCall(DMPlexGetConeSize(dm, cells[0], &coneSizeL));
3759:         for (faceLocL = 0; faceLocL < coneSizeL; ++faceLocL)
3760:           if (cone[faceLocL] == face) break;
3761:         PetscCall(DMPlexGetCone(dm, cells[1], &cone));
3762:         PetscCall(DMPlexGetConeSize(dm, cells[1], &coneSizeR));
3763:         for (faceLocR = 0; faceLocR < coneSizeR; ++faceLocR)
3764:           if (cone[faceLocR] == face) break;
3765:         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]);
3766:         /* Check that FEM field has values in the right cell (sometimes its an FV ghost cell) */
3767:         /* TODO: this is a hack that might not be right for nonconforming */
3768:         if (faceLocL < coneSizeL) {
3769:           PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocL, xL, &uLl[iface * Nc + off]));
3770:           if (rdof == ldof && faceLocR < coneSizeR) PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface * Nc + off]));
3771:           else {
3772:             for (d = 0; d < comp; ++d) uRl[iface * Nc + off + d] = uLl[iface * Nc + off + d];
3773:           }
3774:         } else {
3775:           PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface * Nc + off]));
3776:           PetscCall(PetscSectionGetFieldComponents(section, f, &comp));
3777:           for (d = 0; d < comp; ++d) uLl[iface * Nc + off + d] = uRl[iface * Nc + off + d];
3778:         }
3779:         PetscCall(DMPlexVecRestoreClosure(dm, section, locX, cells[0], &ldof, (PetscScalar **)&xL));
3780:         PetscCall(DMPlexVecRestoreClosure(dm, section, locX, cells[1], &rdof, (PetscScalar **)&xR));
3781:       } else {
3782:         PetscFV  fv;
3783:         PetscInt numComp, c;

3785:         PetscCall(PetscDSGetDiscretization(prob, f, (PetscObject *)&fv));
3786:         PetscCall(PetscFVGetNumComponents(fv, &numComp));
3787:         PetscCall(DMPlexPointLocalFieldRead(dm, cells[0], f, x, &xL));
3788:         PetscCall(DMPlexPointLocalFieldRead(dm, cells[1], f, x, &xR));
3789:         if (dmGrad) {
3790:           PetscReal dxL[3], dxR[3];

3792:           PetscCall(DMPlexPointLocalRead(dmGrad, cells[0], lgrad, &gL));
3793:           PetscCall(DMPlexPointLocalRead(dmGrad, cells[1], lgrad, &gR));
3794:           DMPlex_WaxpyD_Internal(dim, -1, cgL->centroid, fg->centroid, dxL);
3795:           DMPlex_WaxpyD_Internal(dim, -1, cgR->centroid, fg->centroid, dxR);
3796:           for (c = 0; c < numComp; ++c) {
3797:             uLl[iface * Nc + off + c] = xL[c] + DMPlex_DotD_Internal(dim, &gL[c * dim], dxL);
3798:             uRl[iface * Nc + off + c] = xR[c] + DMPlex_DotD_Internal(dim, &gR[c * dim], dxR);
3799:           }
3800:         } else {
3801:           for (c = 0; c < numComp; ++c) {
3802:             uLl[iface * Nc + off + c] = xL[c];
3803:             uRl[iface * Nc + off + c] = xR[c];
3804:           }
3805:         }
3806:       }
3807:     }
3808:     ++iface;
3809:   }
3810:   *Nface = iface;
3811:   PetscCall(VecRestoreArrayRead(locX, &x));
3812:   PetscCall(VecRestoreArrayRead(faceGeometry, &facegeom));
3813:   PetscCall(VecRestoreArrayRead(cellGeometry, &cellgeom));
3814:   if (locGrad) PetscCall(VecRestoreArrayRead(locGrad, &lgrad));
3815:   PetscCall(PetscFree(isFE));
3816:   PetscFunctionReturn(PETSC_SUCCESS);
3817: }

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

3822:   Input Parameters:
3823: + dm     - The `DM`
3824: . fStart - The first face to include
3825: . fEnd   - The first face to exclude
3826: . locX   - A local vector with the solution fields
3827: . locX_t - A local vector with solution field time derivatives, or NULL
3828: . faceGeometry - A local vector with face geometry
3829: . cellGeometry - A local vector with cell geometry
3830: - locaGrad - A local vector with field gradients, or NULL

3832:   Output Parameters:
3833: + Nface - The number of faces with field values
3834: . uL - The field values at the left side of the face
3835: - uR - The field values at the right side of the face

3837:   Level: developer

3839: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
3840: @*/
3841: PetscErrorCode DMPlexRestoreFaceFields(DM dm, PetscInt fStart, PetscInt fEnd, Vec locX, Vec locX_t, Vec faceGeometry, Vec cellGeometry, Vec locGrad, PetscInt *Nface, PetscScalar **uL, PetscScalar **uR)
3842: {
3843:   PetscFunctionBegin;
3844:   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uL));
3845:   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uR));
3846:   PetscFunctionReturn(PETSC_SUCCESS);
3847: }

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

3852:   Input Parameters:
3853: + dm     - The `DM`
3854: . fStart - The first face to include
3855: . fEnd   - The first face to exclude
3856: . faceGeometry - A local vector with face geometry
3857: - cellGeometry - A local vector with cell geometry

3859:   Output Parameters:
3860: + Nface - The number of faces with field values
3861: . fgeom - The extract the face centroid and normal
3862: - vol   - The cell volume

3864:   Level: developer

3866: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellFields()`
3867: @*/
3868: PetscErrorCode DMPlexGetFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom **fgeom, PetscReal **vol)
3869: {
3870:   DM                 dmFace, dmCell;
3871:   DMLabel            ghostLabel;
3872:   const PetscScalar *facegeom, *cellgeom;
3873:   PetscInt           dim, numFaces = fEnd - fStart, iface, face;

3875:   PetscFunctionBegin;
3881:   PetscCall(DMGetDimension(dm, &dim));
3882:   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
3883:   PetscCall(VecGetDM(faceGeometry, &dmFace));
3884:   PetscCall(VecGetArrayRead(faceGeometry, &facegeom));
3885:   PetscCall(VecGetDM(cellGeometry, &dmCell));
3886:   PetscCall(VecGetArrayRead(cellGeometry, &cellgeom));
3887:   PetscCall(PetscMalloc1(numFaces, fgeom));
3888:   PetscCall(DMGetWorkArray(dm, numFaces * 2, MPIU_SCALAR, vol));
3889:   for (face = fStart, iface = 0; face < fEnd; ++face) {
3890:     const PetscInt  *cells;
3891:     PetscFVFaceGeom *fg;
3892:     PetscFVCellGeom *cgL, *cgR;
3893:     PetscFVFaceGeom *fgeoml = *fgeom;
3894:     PetscReal       *voll   = *vol;
3895:     PetscInt         ghost, d, nchild, nsupp;

3897:     PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
3898:     PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
3899:     PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
3900:     if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
3901:     PetscCall(DMPlexPointLocalRead(dmFace, face, facegeom, &fg));
3902:     PetscCall(DMPlexGetSupport(dm, face, &cells));
3903:     PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL));
3904:     PetscCall(DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR));
3905:     for (d = 0; d < dim; ++d) {
3906:       fgeoml[iface].centroid[d] = fg->centroid[d];
3907:       fgeoml[iface].normal[d]   = fg->normal[d];
3908:     }
3909:     voll[iface * 2 + 0] = cgL->volume;
3910:     voll[iface * 2 + 1] = cgR->volume;
3911:     ++iface;
3912:   }
3913:   *Nface = iface;
3914:   PetscCall(VecRestoreArrayRead(faceGeometry, &facegeom));
3915:   PetscCall(VecRestoreArrayRead(cellGeometry, &cellgeom));
3916:   PetscFunctionReturn(PETSC_SUCCESS);
3917: }

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

3922:   Input Parameters:
3923: + dm     - The `DM`
3924: . fStart - The first face to include
3925: . fEnd   - The first face to exclude
3926: . faceGeometry - A local vector with face geometry
3927: - cellGeometry - A local vector with cell geometry

3929:   Output Parameters:
3930: + Nface - The number of faces with field values
3931: . fgeom - The extract the face centroid and normal
3932: - vol   - The cell volume

3934:   Level: developer

3936: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
3937: @*/
3938: PetscErrorCode DMPlexRestoreFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom **fgeom, PetscReal **vol)
3939: {
3940:   PetscFunctionBegin;
3941:   PetscCall(PetscFree(*fgeom));
3942:   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_REAL, vol));
3943:   PetscFunctionReturn(PETSC_SUCCESS);
3944: }

3946: PetscErrorCode DMSNESGetFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
3947: {
3948:   char           composeStr[33] = {0};
3949:   PetscObjectId  id;
3950:   PetscContainer container;

3952:   PetscFunctionBegin;
3953:   PetscCall(PetscObjectGetId((PetscObject)quad, &id));
3954:   PetscCall(PetscSNPrintf(composeStr, 32, "DMSNESGetFEGeom_%" PetscInt64_FMT "\n", id));
3955:   PetscCall(PetscObjectQuery((PetscObject)pointIS, composeStr, (PetscObject *)&container));
3956:   if (container) {
3957:     PetscCall(PetscContainerGetPointer(container, (void **)geom));
3958:   } else {
3959:     PetscCall(DMFieldCreateFEGeom(coordField, pointIS, quad, faceData, geom));
3960:     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &container));
3961:     PetscCall(PetscContainerSetPointer(container, (void *)*geom));
3962:     PetscCall(PetscContainerSetUserDestroy(container, PetscContainerUserDestroy_PetscFEGeom));
3963:     PetscCall(PetscObjectCompose((PetscObject)pointIS, composeStr, (PetscObject)container));
3964:     PetscCall(PetscContainerDestroy(&container));
3965:   }
3966:   PetscFunctionReturn(PETSC_SUCCESS);
3967: }

3969: PetscErrorCode DMSNESRestoreFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
3970: {
3971:   PetscFunctionBegin;
3972:   *geom = NULL;
3973:   PetscFunctionReturn(PETSC_SUCCESS);
3974: }

3976: PetscErrorCode DMPlexComputeResidual_Patch_Internal(DM dm, PetscSection section, IS cellIS, PetscReal t, Vec locX, Vec locX_t, Vec locF, void *user)
3977: {
3978:   DM_Plex        *mesh       = (DM_Plex *)dm->data;
3979:   const char     *name       = "Residual";
3980:   DM              dmAux      = NULL;
3981:   DMLabel         ghostLabel = NULL;
3982:   PetscDS         prob       = NULL;
3983:   PetscDS         probAux    = NULL;
3984:   PetscBool       useFEM     = PETSC_FALSE;
3985:   PetscBool       isImplicit = (locX_t || t == PETSC_MIN_REAL) ? PETSC_TRUE : PETSC_FALSE;
3986:   DMField         coordField = NULL;
3987:   Vec             locA;
3988:   PetscScalar    *u = NULL, *u_t, *a, *uL = NULL, *uR = NULL;
3989:   IS              chunkIS;
3990:   const PetscInt *cells;
3991:   PetscInt        cStart, cEnd, numCells;
3992:   PetscInt        Nf, f, totDim, totDimAux, numChunks, cellChunkSize, chunk, fStart, fEnd;
3993:   PetscInt        maxDegree = PETSC_MAX_INT;
3994:   PetscFormKey    key;
3995:   PetscQuadrature affineQuad = NULL, *quads = NULL;
3996:   PetscFEGeom    *affineGeom = NULL, **geoms = NULL;

3998:   PetscFunctionBegin;
3999:   PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
4000:   /* FEM+FVM */
4001:   /* 1: Get sizes from dm and dmAux */
4002:   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
4003:   PetscCall(DMGetDS(dm, &prob));
4004:   PetscCall(PetscDSGetNumFields(prob, &Nf));
4005:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
4006:   PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA));
4007:   if (locA) {
4008:     PetscCall(VecGetDM(locA, &dmAux));
4009:     PetscCall(DMGetDS(dmAux, &probAux));
4010:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
4011:   }
4012:   /* 2: Get geometric data */
4013:   for (f = 0; f < Nf; ++f) {
4014:     PetscObject  obj;
4015:     PetscClassId id;
4016:     PetscBool    fimp;

4018:     PetscCall(PetscDSGetImplicit(prob, f, &fimp));
4019:     if (isImplicit != fimp) continue;
4020:     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4021:     PetscCall(PetscObjectGetClassId(obj, &id));
4022:     if (id == PETSCFE_CLASSID) useFEM = PETSC_TRUE;
4023:     PetscCheck(id != PETSCFV_CLASSID, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Use of FVM with PCPATCH not yet implemented");
4024:   }
4025:   if (useFEM) {
4026:     PetscCall(DMGetCoordinateField(dm, &coordField));
4027:     PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
4028:     if (maxDegree <= 1) {
4029:       PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad));
4030:       if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
4031:     } else {
4032:       PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
4033:       for (f = 0; f < Nf; ++f) {
4034:         PetscObject  obj;
4035:         PetscClassId id;
4036:         PetscBool    fimp;

4038:         PetscCall(PetscDSGetImplicit(prob, f, &fimp));
4039:         if (isImplicit != fimp) continue;
4040:         PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4041:         PetscCall(PetscObjectGetClassId(obj, &id));
4042:         if (id == PETSCFE_CLASSID) {
4043:           PetscFE fe = (PetscFE)obj;

4045:           PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
4046:           PetscCall(PetscObjectReference((PetscObject)quads[f]));
4047:           PetscCall(DMSNESGetFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
4048:         }
4049:       }
4050:     }
4051:   }
4052:   /* Loop over chunks */
4053:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
4054:   PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
4055:   if (useFEM) PetscCall(ISCreate(PETSC_COMM_SELF, &chunkIS));
4056:   numCells      = cEnd - cStart;
4057:   numChunks     = 1;
4058:   cellChunkSize = numCells / numChunks;
4059:   numChunks     = PetscMin(1, numCells);
4060:   key.label     = NULL;
4061:   key.value     = 0;
4062:   key.part      = 0;
4063:   for (chunk = 0; chunk < numChunks; ++chunk) {
4064:     PetscScalar     *elemVec, *fluxL = NULL, *fluxR = NULL;
4065:     PetscReal       *vol   = NULL;
4066:     PetscFVFaceGeom *fgeom = NULL;
4067:     PetscInt         cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
4068:     PetscInt         numFaces = 0;

4070:     /* Extract field coefficients */
4071:     if (useFEM) {
4072:       PetscCall(ISGetPointSubrange(chunkIS, cS, cE, cells));
4073:       PetscCall(DMPlexGetCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
4074:       PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
4075:       PetscCall(PetscArrayzero(elemVec, numCells * totDim));
4076:     }
4077:     /* TODO We will interlace both our field coefficients (u, u_t, uL, uR, etc.) and our output (elemVec, fL, fR). I think this works */
4078:     /* Loop over fields */
4079:     for (f = 0; f < Nf; ++f) {
4080:       PetscObject  obj;
4081:       PetscClassId id;
4082:       PetscBool    fimp;
4083:       PetscInt     numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;

4085:       key.field = f;
4086:       PetscCall(PetscDSGetImplicit(prob, f, &fimp));
4087:       if (isImplicit != fimp) continue;
4088:       PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4089:       PetscCall(PetscObjectGetClassId(obj, &id));
4090:       if (id == PETSCFE_CLASSID) {
4091:         PetscFE         fe        = (PetscFE)obj;
4092:         PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[f];
4093:         PetscFEGeom    *chunkGeom = NULL;
4094:         PetscQuadrature quad      = affineQuad ? affineQuad : quads[f];
4095:         PetscInt        Nq, Nb;

4097:         PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
4098:         PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
4099:         PetscCall(PetscFEGetDimension(fe, &Nb));
4100:         blockSize = Nb;
4101:         batchSize = numBlocks * blockSize;
4102:         PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
4103:         numChunks = numCells / (numBatches * batchSize);
4104:         Ne        = numChunks * numBatches * batchSize;
4105:         Nr        = numCells % (numBatches * batchSize);
4106:         offset    = numCells - Nr;
4107:         /* Integrate FE residual to get elemVec (need fields at quadrature points) */
4108:         /*   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) */
4109:         PetscCall(PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom));
4110:         PetscCall(PetscFEIntegrateResidual(prob, key, Ne, chunkGeom, u, u_t, probAux, a, t, elemVec));
4111:         PetscCall(PetscFEGeomGetChunk(geom, offset, numCells, &chunkGeom));
4112:         PetscCall(PetscFEIntegrateResidual(prob, key, Nr, chunkGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, &a[offset * totDimAux], t, &elemVec[offset * totDim]));
4113:         PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &chunkGeom));
4114:       } else if (id == PETSCFV_CLASSID) {
4115:         PetscFV fv = (PetscFV)obj;

4117:         Ne = numFaces;
4118:         /* Riemann solve over faces (need fields at face centroids) */
4119:         /*   We need to evaluate FE fields at those coordinates */
4120:         PetscCall(PetscFVIntegrateRHSFunction(fv, prob, f, Ne, fgeom, vol, uL, uR, fluxL, fluxR));
4121:       } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
4122:     }
4123:     /* Loop over domain */
4124:     if (useFEM) {
4125:       /* Add elemVec to locX */
4126:       for (c = cS; c < cE; ++c) {
4127:         const PetscInt cell = cells ? cells[c] : c;
4128:         const PetscInt cind = c - cStart;

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

4134:           PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
4135:           if (ghostVal > 0) continue;
4136:         }
4137:         PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind * totDim], ADD_ALL_VALUES));
4138:       }
4139:     }
4140:     /* Handle time derivative */
4141:     if (locX_t) {
4142:       PetscScalar *x_t, *fa;

4144:       PetscCall(VecGetArray(locF, &fa));
4145:       PetscCall(VecGetArray(locX_t, &x_t));
4146:       for (f = 0; f < Nf; ++f) {
4147:         PetscFV      fv;
4148:         PetscObject  obj;
4149:         PetscClassId id;
4150:         PetscInt     pdim, d;

4152:         PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4153:         PetscCall(PetscObjectGetClassId(obj, &id));
4154:         if (id != PETSCFV_CLASSID) continue;
4155:         fv = (PetscFV)obj;
4156:         PetscCall(PetscFVGetNumComponents(fv, &pdim));
4157:         for (c = cS; c < cE; ++c) {
4158:           const PetscInt cell = cells ? cells[c] : c;
4159:           PetscScalar   *u_t, *r;

4161:           if (ghostLabel) {
4162:             PetscInt ghostVal;

4164:             PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
4165:             if (ghostVal > 0) continue;
4166:           }
4167:           PetscCall(DMPlexPointLocalFieldRead(dm, cell, f, x_t, &u_t));
4168:           PetscCall(DMPlexPointLocalFieldRef(dm, cell, f, fa, &r));
4169:           for (d = 0; d < pdim; ++d) r[d] += u_t[d];
4170:         }
4171:       }
4172:       PetscCall(VecRestoreArray(locX_t, &x_t));
4173:       PetscCall(VecRestoreArray(locF, &fa));
4174:     }
4175:     if (useFEM) {
4176:       PetscCall(DMPlexRestoreCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
4177:       PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
4178:     }
4179:   }
4180:   if (useFEM) PetscCall(ISDestroy(&chunkIS));
4181:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
4182:   /* TODO Could include boundary residual here (see DMPlexComputeResidual_Internal) */
4183:   if (useFEM) {
4184:     if (maxDegree <= 1) {
4185:       PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
4186:       PetscCall(PetscQuadratureDestroy(&affineQuad));
4187:     } else {
4188:       for (f = 0; f < Nf; ++f) {
4189:         PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
4190:         PetscCall(PetscQuadratureDestroy(&quads[f]));
4191:       }
4192:       PetscCall(PetscFree2(quads, geoms));
4193:     }
4194:   }
4195:   PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
4196:   PetscFunctionReturn(PETSC_SUCCESS);
4197: }

4199: /*
4200:   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

4202:   X   - The local solution vector
4203:   X_t - The local solution time derivative vector, or NULL
4204: */
4205: PetscErrorCode DMPlexComputeJacobian_Patch_Internal(DM dm, PetscSection section, PetscSection globalSection, IS cellIS, PetscReal t, PetscReal X_tShift, Vec X, Vec X_t, Mat Jac, Mat JacP, void *ctx)
4206: {
4207:   DM_Plex        *mesh = (DM_Plex *)dm->data;
4208:   const char     *name = "Jacobian", *nameP = "JacobianPre";
4209:   DM              dmAux = NULL;
4210:   PetscDS         prob, probAux = NULL;
4211:   PetscSection    sectionAux = NULL;
4212:   Vec             A;
4213:   DMField         coordField;
4214:   PetscFEGeom    *cgeomFEM;
4215:   PetscQuadrature qGeom = NULL;
4216:   Mat             J = Jac, JP = JacP;
4217:   PetscScalar    *work, *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL, *elemMatP = NULL, *elemMatD = NULL;
4218:   PetscBool       hasJac, hasPrec, hasDyn, assembleJac, *isFE, hasFV = PETSC_FALSE;
4219:   const PetscInt *cells;
4220:   PetscFormKey    key;
4221:   PetscInt        Nf, fieldI, fieldJ, maxDegree, numCells, cStart, cEnd, numChunks, chunkSize, chunk, totDim, totDimAux = 0, sz, wsz, off = 0, offCell = 0;

4223:   PetscFunctionBegin;
4224:   PetscCall(ISGetLocalSize(cellIS, &numCells));
4225:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
4226:   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
4227:   PetscCall(DMGetDS(dm, &prob));
4228:   PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &A));
4229:   if (A) {
4230:     PetscCall(VecGetDM(A, &dmAux));
4231:     PetscCall(DMGetLocalSection(dmAux, &sectionAux));
4232:     PetscCall(DMGetDS(dmAux, &probAux));
4233:   }
4234:   /* Get flags */
4235:   PetscCall(PetscDSGetNumFields(prob, &Nf));
4236:   PetscCall(DMGetWorkArray(dm, Nf, MPIU_BOOL, &isFE));
4237:   for (fieldI = 0; fieldI < Nf; ++fieldI) {
4238:     PetscObject  disc;
4239:     PetscClassId id;
4240:     PetscCall(PetscDSGetDiscretization(prob, fieldI, &disc));
4241:     PetscCall(PetscObjectGetClassId(disc, &id));
4242:     if (id == PETSCFE_CLASSID) {
4243:       isFE[fieldI] = PETSC_TRUE;
4244:     } else if (id == PETSCFV_CLASSID) {
4245:       hasFV        = PETSC_TRUE;
4246:       isFE[fieldI] = PETSC_FALSE;
4247:     }
4248:   }
4249:   PetscCall(PetscDSHasJacobian(prob, &hasJac));
4250:   PetscCall(PetscDSHasJacobianPreconditioner(prob, &hasPrec));
4251:   PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
4252:   assembleJac = hasJac && hasPrec && (Jac != JacP) ? PETSC_TRUE : PETSC_FALSE;
4253:   hasDyn      = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
4254:   if (hasFV) PetscCall(MatSetOption(JP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE)); /* No allocated space for FV stuff, so ignore the zero entries */
4255:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
4256:   if (probAux) PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
4257:   /* Compute batch sizes */
4258:   if (isFE[0]) {
4259:     PetscFE         fe;
4260:     PetscQuadrature q;
4261:     PetscInt        numQuadPoints, numBatches, batchSize, numBlocks, blockSize, Nb;

4263:     PetscCall(PetscDSGetDiscretization(prob, 0, (PetscObject *)&fe));
4264:     PetscCall(PetscFEGetQuadrature(fe, &q));
4265:     PetscCall(PetscQuadratureGetData(q, NULL, NULL, &numQuadPoints, NULL, NULL));
4266:     PetscCall(PetscFEGetDimension(fe, &Nb));
4267:     PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
4268:     blockSize = Nb * numQuadPoints;
4269:     batchSize = numBlocks * blockSize;
4270:     chunkSize = numBatches * batchSize;
4271:     numChunks = numCells / chunkSize + numCells % chunkSize;
4272:     PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
4273:   } else {
4274:     chunkSize = numCells;
4275:     numChunks = 1;
4276:   }
4277:   /* Get work space */
4278:   wsz = (((X ? 1 : 0) + (X_t ? 1 : 0) + (dmAux ? 1 : 0)) * totDim + ((hasJac ? 1 : 0) + (hasPrec ? 1 : 0) + (hasDyn ? 1 : 0)) * totDim * totDim) * chunkSize;
4279:   PetscCall(DMGetWorkArray(dm, wsz, MPIU_SCALAR, &work));
4280:   PetscCall(PetscArrayzero(work, wsz));
4281:   off      = 0;
4282:   u        = X ? (sz = chunkSize * totDim, off += sz, work + off - sz) : NULL;
4283:   u_t      = X_t ? (sz = chunkSize * totDim, off += sz, work + off - sz) : NULL;
4284:   a        = dmAux ? (sz = chunkSize * totDimAux, off += sz, work + off - sz) : NULL;
4285:   elemMat  = hasJac ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL;
4286:   elemMatP = hasPrec ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL;
4287:   elemMatD = hasDyn ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL;
4288:   PetscCheck(off == wsz, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Error is workspace size %" PetscInt_FMT " should be %" PetscInt_FMT, off, wsz);
4289:   /* Setup geometry */
4290:   PetscCall(DMGetCoordinateField(dm, &coordField));
4291:   PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
4292:   if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
4293:   if (!qGeom) {
4294:     PetscFE fe;

4296:     PetscCall(PetscDSGetDiscretization(prob, 0, (PetscObject *)&fe));
4297:     PetscCall(PetscFEGetQuadrature(fe, &qGeom));
4298:     PetscCall(PetscObjectReference((PetscObject)qGeom));
4299:   }
4300:   PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
4301:   /* Compute volume integrals */
4302:   if (assembleJac) PetscCall(MatZeroEntries(J));
4303:   PetscCall(MatZeroEntries(JP));
4304:   key.label = NULL;
4305:   key.value = 0;
4306:   key.part  = 0;
4307:   for (chunk = 0; chunk < numChunks; ++chunk, offCell += chunkSize) {
4308:     const PetscInt Ncell = PetscMin(chunkSize, numCells - offCell);
4309:     PetscInt       c;

4311:     /* Extract values */
4312:     for (c = 0; c < Ncell; ++c) {
4313:       const PetscInt cell = cells ? cells[c + offCell] : c + offCell;
4314:       PetscScalar   *x = NULL, *x_t = NULL;
4315:       PetscInt       i;

4317:       if (X) {
4318:         PetscCall(DMPlexVecGetClosure(dm, section, X, cell, NULL, &x));
4319:         for (i = 0; i < totDim; ++i) u[c * totDim + i] = x[i];
4320:         PetscCall(DMPlexVecRestoreClosure(dm, section, X, cell, NULL, &x));
4321:       }
4322:       if (X_t) {
4323:         PetscCall(DMPlexVecGetClosure(dm, section, X_t, cell, NULL, &x_t));
4324:         for (i = 0; i < totDim; ++i) u_t[c * totDim + i] = x_t[i];
4325:         PetscCall(DMPlexVecRestoreClosure(dm, section, X_t, cell, NULL, &x_t));
4326:       }
4327:       if (dmAux) {
4328:         PetscCall(DMPlexVecGetClosure(dmAux, sectionAux, A, cell, NULL, &x));
4329:         for (i = 0; i < totDimAux; ++i) a[c * totDimAux + i] = x[i];
4330:         PetscCall(DMPlexVecRestoreClosure(dmAux, sectionAux, A, cell, NULL, &x));
4331:       }
4332:     }
4333:     for (fieldI = 0; fieldI < Nf; ++fieldI) {
4334:       PetscFE fe;
4335:       PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
4336:       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
4337:         key.field = fieldI * Nf + fieldJ;
4338:         if (hasJac) PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMat));
4339:         if (hasPrec) PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatP));
4340:         if (hasDyn) PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatD));
4341:       }
4342:       /* For finite volume, add the identity */
4343:       if (!isFE[fieldI]) {
4344:         PetscFV  fv;
4345:         PetscInt eOffset = 0, Nc, fc, foff;

4347:         PetscCall(PetscDSGetFieldOffset(prob, fieldI, &foff));
4348:         PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fv));
4349:         PetscCall(PetscFVGetNumComponents(fv, &Nc));
4350:         for (c = 0; c < chunkSize; ++c, eOffset += totDim * totDim) {
4351:           for (fc = 0; fc < Nc; ++fc) {
4352:             const PetscInt i = foff + fc;
4353:             if (hasJac) elemMat[eOffset + i * totDim + i] = 1.0;
4354:             if (hasPrec) elemMatP[eOffset + i * totDim + i] = 1.0;
4355:           }
4356:         }
4357:       }
4358:     }
4359:     /*   Add contribution from X_t */
4360:     if (hasDyn) {
4361:       for (c = 0; c < chunkSize * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
4362:     }
4363:     /* Insert values into matrix */
4364:     for (c = 0; c < Ncell; ++c) {
4365:       const PetscInt cell = cells ? cells[c + offCell] : c + offCell;
4366:       if (mesh->printFEM > 1) {
4367:         if (hasJac) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[(c - cStart) * totDim * totDim]));
4368:         if (hasPrec) PetscCall(DMPrintCellMatrix(cell, nameP, totDim, totDim, &elemMatP[(c - cStart) * totDim * totDim]));
4369:       }
4370:       if (assembleJac) PetscCall(DMPlexMatSetClosure(dm, section, globalSection, Jac, cell, &elemMat[(c - cStart) * totDim * totDim], ADD_VALUES));
4371:       PetscCall(DMPlexMatSetClosure(dm, section, globalSection, JP, cell, &elemMat[(c - cStart) * totDim * totDim], ADD_VALUES));
4372:     }
4373:   }
4374:   /* Cleanup */
4375:   PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
4376:   PetscCall(PetscQuadratureDestroy(&qGeom));
4377:   if (hasFV) PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE));
4378:   PetscCall(DMRestoreWorkArray(dm, Nf, MPIU_BOOL, &isFE));
4379:   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));
4380:   /* Compute boundary integrals */
4381:   /* PetscCall(DMPlexComputeBdJacobian_Internal(dm, X, X_t, t, X_tShift, Jac, JacP, ctx)); */
4382:   /* Assemble matrix */
4383:   if (assembleJac) {
4384:     PetscCall(MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY));
4385:     PetscCall(MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY));
4386:   }
4387:   PetscCall(MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY));
4388:   PetscCall(MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY));
4389:   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
4390:   PetscFunctionReturn(PETSC_SUCCESS);
4391: }

4393: /******** FEM Assembly Function ********/

4395: static PetscErrorCode DMConvertPlex_Internal(DM dm, DM *plex, PetscBool copy)
4396: {
4397:   PetscBool isPlex;

4399:   PetscFunctionBegin;
4400:   PetscCall(PetscObjectTypeCompare((PetscObject)dm, DMPLEX, &isPlex));
4401:   if (isPlex) {
4402:     *plex = dm;
4403:     PetscCall(PetscObjectReference((PetscObject)dm));
4404:   } else {
4405:     PetscCall(PetscObjectQuery((PetscObject)dm, "dm_plex", (PetscObject *)plex));
4406:     if (!*plex) {
4407:       PetscCall(DMConvert(dm, DMPLEX, plex));
4408:       PetscCall(PetscObjectCompose((PetscObject)dm, "dm_plex", (PetscObject)*plex));
4409:       if (copy) PetscCall(DMCopyAuxiliaryVec(dm, *plex));
4410:     } else {
4411:       PetscCall(PetscObjectReference((PetscObject)*plex));
4412:     }
4413:   }
4414:   PetscFunctionReturn(PETSC_SUCCESS);
4415: }

4417: /*@
4418:   DMPlexGetGeometryFVM - Return precomputed geometric data

4420:   Collective

4422:   Input Parameter:
4423: . dm - The `DM`

4425:   Output Parameters:
4426: + facegeom - The values precomputed from face geometry
4427: . cellgeom - The values precomputed from cell geometry
4428: - minRadius - The minimum radius over the mesh of an inscribed sphere in a cell

4430:   Level: developer

4432: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMTSSetRHSFunctionLocal()`
4433: @*/
4434: PetscErrorCode DMPlexGetGeometryFVM(DM dm, Vec *facegeom, Vec *cellgeom, PetscReal *minRadius)
4435: {
4436:   DM plex;

4438:   PetscFunctionBegin;
4440:   PetscCall(DMConvertPlex_Internal(dm, &plex, PETSC_TRUE));
4441:   PetscCall(DMPlexGetDataFVM(plex, NULL, cellgeom, facegeom, NULL));
4442:   if (minRadius) PetscCall(DMPlexGetMinRadius(plex, minRadius));
4443:   PetscCall(DMDestroy(&plex));
4444:   PetscFunctionReturn(PETSC_SUCCESS);
4445: }

4447: /*@
4448:   DMPlexGetGradientDM - Return gradient data layout

4450:   Collective

4452:   Input Parameters:
4453: + dm - The `DM`
4454: - fv - The `PetscFV`

4456:   Output Parameter:
4457: . dmGrad - The layout for gradient values

4459:   Level: developer

4461: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetGeometryFVM()`
4462: @*/
4463: PetscErrorCode DMPlexGetGradientDM(DM dm, PetscFV fv, DM *dmGrad)
4464: {
4465:   DM        plex;
4466:   PetscBool computeGradients;

4468:   PetscFunctionBegin;
4472:   PetscCall(PetscFVGetComputeGradients(fv, &computeGradients));
4473:   if (!computeGradients) {
4474:     *dmGrad = NULL;
4475:     PetscFunctionReturn(PETSC_SUCCESS);
4476:   }
4477:   PetscCall(DMConvertPlex_Internal(dm, &plex, PETSC_TRUE));
4478:   PetscCall(DMPlexGetDataFVM(plex, fv, NULL, NULL, dmGrad));
4479:   PetscCall(DMDestroy(&plex));
4480:   PetscFunctionReturn(PETSC_SUCCESS);
4481: }

4483: static PetscErrorCode DMPlexComputeBdResidual_Single_Internal(DM dm, PetscReal t, PetscWeakForm wf, PetscFormKey key, Vec locX, Vec locX_t, Vec locF, DMField coordField, IS facetIS)
4484: {
4485:   DM_Plex        *mesh = (DM_Plex *)dm->data;
4486:   DM              plex = NULL, plexA = NULL;
4487:   DMEnclosureType encAux;
4488:   PetscDS         prob, probAux       = NULL;
4489:   PetscSection    section, sectionAux = NULL;
4490:   Vec             locA = NULL;
4491:   PetscScalar    *u = NULL, *u_t = NULL, *a = NULL, *elemVec = NULL;
4492:   PetscInt        totDim, totDimAux = 0;

4494:   PetscFunctionBegin;
4495:   PetscCall(DMConvert(dm, DMPLEX, &plex));
4496:   PetscCall(DMGetLocalSection(dm, &section));
4497:   PetscCall(DMGetDS(dm, &prob));
4498:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
4499:   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &locA));
4500:   if (locA) {
4501:     DM dmAux;

4503:     PetscCall(VecGetDM(locA, &dmAux));
4504:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
4505:     PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
4506:     PetscCall(DMGetDS(plexA, &probAux));
4507:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
4508:     PetscCall(DMGetLocalSection(plexA, &sectionAux));
4509:   }
4510:   {
4511:     PetscFEGeom    *fgeom;
4512:     PetscInt        maxDegree;
4513:     PetscQuadrature qGeom = NULL;
4514:     IS              pointIS;
4515:     const PetscInt *points;
4516:     PetscInt        numFaces, face, Nq;

4518:     PetscCall(DMLabelGetStratumIS(key.label, key.value, &pointIS));
4519:     if (!pointIS) goto end; /* No points with that id on this process */
4520:     {
4521:       IS isectIS;

4523:       /* TODO: Special cases of ISIntersect where it is quick to check a priori if one is a superset of the other */
4524:       PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS));
4525:       PetscCall(ISDestroy(&pointIS));
4526:       pointIS = isectIS;
4527:     }
4528:     PetscCall(ISGetLocalSize(pointIS, &numFaces));
4529:     PetscCall(ISGetIndices(pointIS, &points));
4530:     PetscCall(PetscMalloc4(numFaces * totDim, &u, locX_t ? numFaces * totDim : 0, &u_t, numFaces * totDim, &elemVec, locA ? numFaces * totDimAux : 0, &a));
4531:     PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
4532:     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom));
4533:     if (!qGeom) {
4534:       PetscFE fe;

4536:       PetscCall(PetscDSGetDiscretization(prob, key.field, (PetscObject *)&fe));
4537:       PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
4538:       PetscCall(PetscObjectReference((PetscObject)qGeom));
4539:     }
4540:     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
4541:     PetscCall(DMSNESGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
4542:     for (face = 0; face < numFaces; ++face) {
4543:       const PetscInt point = points[face], *support;
4544:       PetscScalar   *x     = NULL;
4545:       PetscInt       i;

4547:       PetscCall(DMPlexGetSupport(dm, point, &support));
4548:       PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
4549:       for (i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
4550:       PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
4551:       if (locX_t) {
4552:         PetscCall(DMPlexVecGetClosure(plex, section, locX_t, support[0], NULL, &x));
4553:         for (i = 0; i < totDim; ++i) u_t[face * totDim + i] = x[i];
4554:         PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, support[0], NULL, &x));
4555:       }
4556:       if (locA) {
4557:         PetscInt subp;

4559:         PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
4560:         PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
4561:         for (i = 0; i < totDimAux; ++i) a[face * totDimAux + i] = x[i];
4562:         PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
4563:       }
4564:     }
4565:     PetscCall(PetscArrayzero(elemVec, numFaces * totDim));
4566:     {
4567:       PetscFE      fe;
4568:       PetscInt     Nb;
4569:       PetscFEGeom *chunkGeom = NULL;
4570:       /* Conforming batches */
4571:       PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
4572:       /* Remainder */
4573:       PetscInt Nr, offset;

4575:       PetscCall(PetscDSGetDiscretization(prob, key.field, (PetscObject *)&fe));
4576:       PetscCall(PetscFEGetDimension(fe, &Nb));
4577:       PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
4578:       /* TODO: documentation is unclear about what is going on with these numbers: how should Nb / Nq factor in ? */
4579:       blockSize = Nb;
4580:       batchSize = numBlocks * blockSize;
4581:       PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
4582:       numChunks = numFaces / (numBatches * batchSize);
4583:       Ne        = numChunks * numBatches * batchSize;
4584:       Nr        = numFaces % (numBatches * batchSize);
4585:       offset    = numFaces - Nr;
4586:       PetscCall(PetscFEGeomGetChunk(fgeom, 0, offset, &chunkGeom));
4587:       PetscCall(PetscFEIntegrateBdResidual(prob, wf, key, Ne, chunkGeom, u, u_t, probAux, a, t, elemVec));
4588:       PetscCall(PetscFEGeomRestoreChunk(fgeom, 0, offset, &chunkGeom));
4589:       PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom));
4590:       PetscCall(PetscFEIntegrateBdResidual(prob, wf, key, Nr, chunkGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, a ? &a[offset * totDimAux] : NULL, t, &elemVec[offset * totDim]));
4591:       PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom));
4592:     }
4593:     for (face = 0; face < numFaces; ++face) {
4594:       const PetscInt point = points[face], *support;

4596:       if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(point, "BdResidual", totDim, &elemVec[face * totDim]));
4597:       PetscCall(DMPlexGetSupport(plex, point, &support));
4598:       PetscCall(DMPlexVecSetClosure(plex, NULL, locF, support[0], &elemVec[face * totDim], ADD_ALL_VALUES));
4599:     }
4600:     PetscCall(DMSNESRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
4601:     PetscCall(PetscQuadratureDestroy(&qGeom));
4602:     PetscCall(ISRestoreIndices(pointIS, &points));
4603:     PetscCall(ISDestroy(&pointIS));
4604:     PetscCall(PetscFree4(u, u_t, elemVec, a));
4605:   }
4606: end:
4607:   PetscCall(DMDestroy(&plex));
4608:   PetscCall(DMDestroy(&plexA));
4609:   PetscFunctionReturn(PETSC_SUCCESS);
4610: }

4612: PetscErrorCode DMPlexComputeBdResidualSingle(DM dm, PetscReal t, PetscWeakForm wf, PetscFormKey key, Vec locX, Vec locX_t, Vec locF)
4613: {
4614:   DMField  coordField;
4615:   DMLabel  depthLabel;
4616:   IS       facetIS;
4617:   PetscInt dim;

4619:   PetscFunctionBegin;
4620:   PetscCall(DMGetDimension(dm, &dim));
4621:   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
4622:   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
4623:   PetscCall(DMGetCoordinateField(dm, &coordField));
4624:   PetscCall(DMPlexComputeBdResidual_Single_Internal(dm, t, wf, key, locX, locX_t, locF, coordField, facetIS));
4625:   PetscCall(ISDestroy(&facetIS));
4626:   PetscFunctionReturn(PETSC_SUCCESS);
4627: }

4629: PetscErrorCode DMPlexComputeBdResidual_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
4630: {
4631:   PetscDS  prob;
4632:   PetscInt numBd, bd;
4633:   DMField  coordField = NULL;
4634:   IS       facetIS    = NULL;
4635:   DMLabel  depthLabel;
4636:   PetscInt dim;

4638:   PetscFunctionBegin;
4639:   PetscCall(DMGetDS(dm, &prob));
4640:   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
4641:   PetscCall(DMGetDimension(dm, &dim));
4642:   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
4643:   PetscCall(PetscDSGetNumBoundary(prob, &numBd));
4644:   for (bd = 0; bd < numBd; ++bd) {
4645:     PetscWeakForm           wf;
4646:     DMBoundaryConditionType type;
4647:     DMLabel                 label;
4648:     const PetscInt         *values;
4649:     PetscInt                field, numValues, v;
4650:     PetscObject             obj;
4651:     PetscClassId            id;
4652:     PetscFormKey            key;

4654:     PetscCall(PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &field, NULL, NULL, NULL, NULL, NULL));
4655:     if (type & DM_BC_ESSENTIAL) continue;
4656:     PetscCall(PetscDSGetDiscretization(prob, field, &obj));
4657:     PetscCall(PetscObjectGetClassId(obj, &id));
4658:     if (id != PETSCFE_CLASSID) continue;
4659:     if (!facetIS) {
4660:       DMLabel  depthLabel;
4661:       PetscInt dim;

4663:       PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
4664:       PetscCall(DMGetDimension(dm, &dim));
4665:       PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
4666:     }
4667:     PetscCall(DMGetCoordinateField(dm, &coordField));
4668:     for (v = 0; v < numValues; ++v) {
4669:       key.label = label;
4670:       key.value = values[v];
4671:       key.field = field;
4672:       key.part  = 0;
4673:       PetscCall(DMPlexComputeBdResidual_Single_Internal(dm, t, wf, key, locX, locX_t, locF, coordField, facetIS));
4674:     }
4675:   }
4676:   PetscCall(ISDestroy(&facetIS));
4677:   PetscFunctionReturn(PETSC_SUCCESS);
4678: }

4680: PetscErrorCode DMPlexComputeResidual_Internal(DM dm, PetscFormKey key, IS cellIS, PetscReal time, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
4681: {
4682:   DM_Plex         *mesh       = (DM_Plex *)dm->data;
4683:   const char      *name       = "Residual";
4684:   DM               dmAux      = NULL;
4685:   DM               dmGrad     = NULL;
4686:   DMLabel          ghostLabel = NULL;
4687:   PetscDS          ds         = NULL;
4688:   PetscDS          dsAux      = NULL;
4689:   PetscSection     section    = NULL;
4690:   PetscBool        useFEM     = PETSC_FALSE;
4691:   PetscBool        useFVM     = PETSC_FALSE;
4692:   PetscBool        isImplicit = (locX_t || time == PETSC_MIN_REAL) ? PETSC_TRUE : PETSC_FALSE;
4693:   PetscFV          fvm        = NULL;
4694:   PetscFVCellGeom *cgeomFVM   = NULL;
4695:   PetscFVFaceGeom *fgeomFVM   = NULL;
4696:   DMField          coordField = NULL;
4697:   Vec              locA, cellGeometryFVM = NULL, faceGeometryFVM = NULL, grad, locGrad = NULL;
4698:   PetscScalar     *u = NULL, *u_t, *a, *uL, *uR;
4699:   IS               chunkIS;
4700:   const PetscInt  *cells;
4701:   PetscInt         cStart, cEnd, numCells;
4702:   PetscInt         Nf, f, totDim, totDimAux, numChunks, cellChunkSize, faceChunkSize, chunk, fStart, fEnd;
4703:   PetscInt         maxDegree  = PETSC_MAX_INT;
4704:   PetscQuadrature  affineQuad = NULL, *quads = NULL;
4705:   PetscFEGeom     *affineGeom = NULL, **geoms = NULL;

4707:   PetscFunctionBegin;
4708:   if (!cellIS) PetscFunctionReturn(PETSC_SUCCESS);
4709:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
4710:   if (cStart >= cEnd) PetscFunctionReturn(PETSC_SUCCESS);
4711:   PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
4712:   /* TODO The places where we have to use isFE are probably the member functions for the PetscDisc class */
4713:   /* TODO The FVM geometry is over-manipulated. Make the precalc functions return exactly what we need */
4714:   /* FEM+FVM */
4715:   PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
4716:   /* 1: Get sizes from dm and dmAux */
4717:   PetscCall(DMGetLocalSection(dm, &section));
4718:   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
4719:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, NULL));
4720:   PetscCall(PetscDSGetNumFields(ds, &Nf));
4721:   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
4722:   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &locA));
4723:   if (locA) {
4724:     PetscInt subcell;
4725:     PetscCall(VecGetDM(locA, &dmAux));
4726:     PetscCall(DMGetEnclosurePoint(dmAux, dm, DM_ENC_UNKNOWN, cells ? cells[cStart] : cStart, &subcell));
4727:     PetscCall(DMGetCellDS(dmAux, subcell, &dsAux, NULL));
4728:     PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
4729:   }
4730:   /* 2: Get geometric data */
4731:   for (f = 0; f < Nf; ++f) {
4732:     PetscObject  obj;
4733:     PetscClassId id;
4734:     PetscBool    fimp;

4736:     PetscCall(PetscDSGetImplicit(ds, f, &fimp));
4737:     if (isImplicit != fimp) continue;
4738:     PetscCall(PetscDSGetDiscretization(ds, f, &obj));
4739:     PetscCall(PetscObjectGetClassId(obj, &id));
4740:     if (id == PETSCFE_CLASSID) useFEM = PETSC_TRUE;
4741:     if (id == PETSCFV_CLASSID) {
4742:       useFVM = PETSC_TRUE;
4743:       fvm    = (PetscFV)obj;
4744:     }
4745:   }
4746:   if (useFEM) {
4747:     PetscCall(DMGetCoordinateField(dm, &coordField));
4748:     PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
4749:     if (maxDegree <= 1) {
4750:       PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad));
4751:       if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
4752:     } else {
4753:       PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
4754:       for (f = 0; f < Nf; ++f) {
4755:         PetscObject  obj;
4756:         PetscClassId id;
4757:         PetscBool    fimp;

4759:         PetscCall(PetscDSGetImplicit(ds, f, &fimp));
4760:         if (isImplicit != fimp) continue;
4761:         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
4762:         PetscCall(PetscObjectGetClassId(obj, &id));
4763:         if (id == PETSCFE_CLASSID) {
4764:           PetscFE fe = (PetscFE)obj;

4766:           PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
4767:           PetscCall(PetscObjectReference((PetscObject)quads[f]));
4768:           PetscCall(DMSNESGetFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
4769:         }
4770:       }
4771:     }
4772:   }
4773:   if (useFVM) {
4774:     PetscCall(DMPlexGetGeometryFVM(dm, &faceGeometryFVM, &cellGeometryFVM, NULL));
4775:     PetscCall(VecGetArrayRead(faceGeometryFVM, (const PetscScalar **)&fgeomFVM));
4776:     PetscCall(VecGetArrayRead(cellGeometryFVM, (const PetscScalar **)&cgeomFVM));
4777:     /* Reconstruct and limit cell gradients */
4778:     PetscCall(DMPlexGetGradientDM(dm, fvm, &dmGrad));
4779:     if (dmGrad) {
4780:       PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
4781:       PetscCall(DMGetGlobalVector(dmGrad, &grad));
4782:       PetscCall(DMPlexReconstructGradients_Internal(dm, fvm, fStart, fEnd, faceGeometryFVM, cellGeometryFVM, locX, grad));
4783:       /* Communicate gradient values */
4784:       PetscCall(DMGetLocalVector(dmGrad, &locGrad));
4785:       PetscCall(DMGlobalToLocalBegin(dmGrad, grad, INSERT_VALUES, locGrad));
4786:       PetscCall(DMGlobalToLocalEnd(dmGrad, grad, INSERT_VALUES, locGrad));
4787:       PetscCall(DMRestoreGlobalVector(dmGrad, &grad));
4788:     }
4789:     /* Handle non-essential (e.g. outflow) boundary values */
4790:     PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_FALSE, locX, time, faceGeometryFVM, cellGeometryFVM, locGrad));
4791:   }
4792:   /* Loop over chunks */
4793:   if (useFEM) PetscCall(ISCreate(PETSC_COMM_SELF, &chunkIS));
4794:   numCells      = cEnd - cStart;
4795:   numChunks     = 1;
4796:   cellChunkSize = numCells / numChunks;
4797:   faceChunkSize = (fEnd - fStart) / numChunks;
4798:   numChunks     = PetscMin(1, numCells);
4799:   for (chunk = 0; chunk < numChunks; ++chunk) {
4800:     PetscScalar     *elemVec, *fluxL, *fluxR;
4801:     PetscReal       *vol;
4802:     PetscFVFaceGeom *fgeom;
4803:     PetscInt         cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
4804:     PetscInt         fS = fStart + chunk * faceChunkSize, fE = PetscMin(fS + faceChunkSize, fEnd), numFaces = 0, face;

4806:     /* Extract field coefficients */
4807:     if (useFEM) {
4808:       PetscCall(ISGetPointSubrange(chunkIS, cS, cE, cells));
4809:       PetscCall(DMPlexGetCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
4810:       PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
4811:       PetscCall(PetscArrayzero(elemVec, numCells * totDim));
4812:     }
4813:     if (useFVM) {
4814:       PetscCall(DMPlexGetFaceFields(dm, fS, fE, locX, locX_t, faceGeometryFVM, cellGeometryFVM, locGrad, &numFaces, &uL, &uR));
4815:       PetscCall(DMPlexGetFaceGeometry(dm, fS, fE, faceGeometryFVM, cellGeometryFVM, &numFaces, &fgeom, &vol));
4816:       PetscCall(DMGetWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxL));
4817:       PetscCall(DMGetWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxR));
4818:       PetscCall(PetscArrayzero(fluxL, numFaces * totDim));
4819:       PetscCall(PetscArrayzero(fluxR, numFaces * totDim));
4820:     }
4821:     /* TODO We will interlace both our field coefficients (u, u_t, uL, uR, etc.) and our output (elemVec, fL, fR). I think this works */
4822:     /* Loop over fields */
4823:     for (f = 0; f < Nf; ++f) {
4824:       PetscObject  obj;
4825:       PetscClassId id;
4826:       PetscBool    fimp;
4827:       PetscInt     numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;

4829:       key.field = f;
4830:       PetscCall(PetscDSGetImplicit(ds, f, &fimp));
4831:       if (isImplicit != fimp) continue;
4832:       PetscCall(PetscDSGetDiscretization(ds, f, &obj));
4833:       PetscCall(PetscObjectGetClassId(obj, &id));
4834:       if (id == PETSCFE_CLASSID) {
4835:         PetscFE         fe        = (PetscFE)obj;
4836:         PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[f];
4837:         PetscFEGeom    *chunkGeom = NULL;
4838:         PetscQuadrature quad      = affineQuad ? affineQuad : quads[f];
4839:         PetscInt        Nq, Nb;

4841:         PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
4842:         PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
4843:         PetscCall(PetscFEGetDimension(fe, &Nb));
4844:         blockSize = Nb;
4845:         batchSize = numBlocks * blockSize;
4846:         PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
4847:         numChunks = numCells / (numBatches * batchSize);
4848:         Ne        = numChunks * numBatches * batchSize;
4849:         Nr        = numCells % (numBatches * batchSize);
4850:         offset    = numCells - Nr;
4851:         /* Integrate FE residual to get elemVec (need fields at quadrature points) */
4852:         /*   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) */
4853:         PetscCall(PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom));
4854:         PetscCall(PetscFEIntegrateResidual(ds, key, Ne, chunkGeom, u, u_t, dsAux, a, t, elemVec));
4855:         PetscCall(PetscFEGeomGetChunk(geom, offset, numCells, &chunkGeom));
4856:         PetscCall(PetscFEIntegrateResidual(ds, key, Nr, chunkGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, dsAux, &a[offset * totDimAux], t, &elemVec[offset * totDim]));
4857:         PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &chunkGeom));
4858:       } else if (id == PETSCFV_CLASSID) {
4859:         PetscFV fv = (PetscFV)obj;

4861:         Ne = numFaces;
4862:         /* Riemann solve over faces (need fields at face centroids) */
4863:         /*   We need to evaluate FE fields at those coordinates */
4864:         PetscCall(PetscFVIntegrateRHSFunction(fv, ds, f, Ne, fgeom, vol, uL, uR, fluxL, fluxR));
4865:       } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
4866:     }
4867:     /* Loop over domain */
4868:     if (useFEM) {
4869:       /* Add elemVec to locX */
4870:       for (c = cS; c < cE; ++c) {
4871:         const PetscInt cell = cells ? cells[c] : c;
4872:         const PetscInt cind = c - cStart;

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

4878:           PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
4879:           if (ghostVal > 0) continue;
4880:         }
4881:         PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind * totDim], ADD_ALL_VALUES));
4882:       }
4883:     }
4884:     if (useFVM) {
4885:       PetscScalar *fa;
4886:       PetscInt     iface;

4888:       PetscCall(VecGetArray(locF, &fa));
4889:       for (f = 0; f < Nf; ++f) {
4890:         PetscFV      fv;
4891:         PetscObject  obj;
4892:         PetscClassId id;
4893:         PetscInt     foff, pdim;

4895:         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
4896:         PetscCall(PetscDSGetFieldOffset(ds, f, &foff));
4897:         PetscCall(PetscObjectGetClassId(obj, &id));
4898:         if (id != PETSCFV_CLASSID) continue;
4899:         fv = (PetscFV)obj;
4900:         PetscCall(PetscFVGetNumComponents(fv, &pdim));
4901:         /* Accumulate fluxes to cells */
4902:         for (face = fS, iface = 0; face < fE; ++face) {
4903:           const PetscInt *scells;
4904:           PetscScalar    *fL = NULL, *fR = NULL;
4905:           PetscInt        ghost, d, nsupp, nchild;

4907:           PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
4908:           PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
4909:           PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
4910:           if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
4911:           PetscCall(DMPlexGetSupport(dm, face, &scells));
4912:           PetscCall(DMLabelGetValue(ghostLabel, scells[0], &ghost));
4913:           if (ghost <= 0) PetscCall(DMPlexPointLocalFieldRef(dm, scells[0], f, fa, &fL));
4914:           PetscCall(DMLabelGetValue(ghostLabel, scells[1], &ghost));
4915:           if (ghost <= 0) PetscCall(DMPlexPointLocalFieldRef(dm, scells[1], f, fa, &fR));
4916:           for (d = 0; d < pdim; ++d) {
4917:             if (fL) fL[d] -= fluxL[iface * totDim + foff + d];
4918:             if (fR) fR[d] += fluxR[iface * totDim + foff + d];
4919:           }
4920:           ++iface;
4921:         }
4922:       }
4923:       PetscCall(VecRestoreArray(locF, &fa));
4924:     }
4925:     /* Handle time derivative */
4926:     if (locX_t) {
4927:       PetscScalar *x_t, *fa;

4929:       PetscCall(VecGetArray(locF, &fa));
4930:       PetscCall(VecGetArray(locX_t, &x_t));
4931:       for (f = 0; f < Nf; ++f) {
4932:         PetscFV      fv;
4933:         PetscObject  obj;
4934:         PetscClassId id;
4935:         PetscInt     pdim, d;

4937:         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
4938:         PetscCall(PetscObjectGetClassId(obj, &id));
4939:         if (id != PETSCFV_CLASSID) continue;
4940:         fv = (PetscFV)obj;
4941:         PetscCall(PetscFVGetNumComponents(fv, &pdim));
4942:         for (c = cS; c < cE; ++c) {
4943:           const PetscInt cell = cells ? cells[c] : c;
4944:           PetscScalar   *u_t, *r;

4946:           if (ghostLabel) {
4947:             PetscInt ghostVal;

4949:             PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
4950:             if (ghostVal > 0) continue;
4951:           }
4952:           PetscCall(DMPlexPointLocalFieldRead(dm, cell, f, x_t, &u_t));
4953:           PetscCall(DMPlexPointLocalFieldRef(dm, cell, f, fa, &r));
4954:           for (d = 0; d < pdim; ++d) r[d] += u_t[d];
4955:         }
4956:       }
4957:       PetscCall(VecRestoreArray(locX_t, &x_t));
4958:       PetscCall(VecRestoreArray(locF, &fa));
4959:     }
4960:     if (useFEM) {
4961:       PetscCall(DMPlexRestoreCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
4962:       PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
4963:     }
4964:     if (useFVM) {
4965:       PetscCall(DMPlexRestoreFaceFields(dm, fS, fE, locX, locX_t, faceGeometryFVM, cellGeometryFVM, locGrad, &numFaces, &uL, &uR));
4966:       PetscCall(DMPlexRestoreFaceGeometry(dm, fS, fE, faceGeometryFVM, cellGeometryFVM, &numFaces, &fgeom, &vol));
4967:       PetscCall(DMRestoreWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxL));
4968:       PetscCall(DMRestoreWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxR));
4969:       if (dmGrad) PetscCall(DMRestoreLocalVector(dmGrad, &locGrad));
4970:     }
4971:   }
4972:   if (useFEM) PetscCall(ISDestroy(&chunkIS));
4973:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));

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

4978:     if (maxDegree <= 1) {
4979:       PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
4980:       PetscCall(PetscQuadratureDestroy(&affineQuad));
4981:     } else {
4982:       for (f = 0; f < Nf; ++f) {
4983:         PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
4984:         PetscCall(PetscQuadratureDestroy(&quads[f]));
4985:       }
4986:       PetscCall(PetscFree2(quads, geoms));
4987:     }
4988:   }

4990:   /* FEM */
4991:   /* 1: Get sizes from dm and dmAux */
4992:   /* 2: Get geometric data */
4993:   /* 3: Handle boundary values */
4994:   /* 4: Loop over domain */
4995:   /*   Extract coefficients */
4996:   /* Loop over fields */
4997:   /*   Set tiling for FE*/
4998:   /*   Integrate FE residual to get elemVec */
4999:   /*     Loop over subdomain */
5000:   /*       Loop over quad points */
5001:   /*         Transform coords to real space */
5002:   /*         Evaluate field and aux fields at point */
5003:   /*         Evaluate residual at point */
5004:   /*         Transform residual to real space */
5005:   /*       Add residual to elemVec */
5006:   /* Loop over domain */
5007:   /*   Add elemVec to locX */

5009:   /* FVM */
5010:   /* Get geometric data */
5011:   /* If using gradients */
5012:   /*   Compute gradient data */
5013:   /*   Loop over domain faces */
5014:   /*     Count computational faces */
5015:   /*     Reconstruct cell gradient */
5016:   /*   Loop over domain cells */
5017:   /*     Limit cell gradients */
5018:   /* Handle boundary values */
5019:   /* Loop over domain faces */
5020:   /*   Read out field, centroid, normal, volume for each side of face */
5021:   /* Riemann solve over faces */
5022:   /* Loop over domain faces */
5023:   /*   Accumulate fluxes to cells */
5024:   /* TODO Change printFEM to printDisc here */
5025:   if (mesh->printFEM) {
5026:     Vec          locFbc;
5027:     PetscInt     pStart, pEnd, p, maxDof;
5028:     PetscScalar *zeroes;

5030:     PetscCall(VecDuplicate(locF, &locFbc));
5031:     PetscCall(VecCopy(locF, locFbc));
5032:     PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5033:     PetscCall(PetscSectionGetMaxDof(section, &maxDof));
5034:     PetscCall(PetscCalloc1(maxDof, &zeroes));
5035:     for (p = pStart; p < pEnd; p++) PetscCall(VecSetValuesSection(locFbc, section, p, zeroes, INSERT_BC_VALUES));
5036:     PetscCall(PetscFree(zeroes));
5037:     PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc));
5038:     PetscCall(VecDestroy(&locFbc));
5039:   }
5040:   PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
5041:   PetscFunctionReturn(PETSC_SUCCESS);
5042: }

5044: /*
5045:   1) Allow multiple kernels for BdResidual for hybrid DS

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

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

5052:   4) Run both kernels for each non-hybrid field with correct dsAux, and then hybrid field as before
5053: */
5054: PetscErrorCode DMPlexComputeResidual_Hybrid_Internal(DM dm, PetscFormKey key[], IS cellIS, PetscReal time, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
5055: {
5056:   DM_Plex        *mesh       = (DM_Plex *)dm->data;
5057:   const char     *name       = "Hybrid Residual";
5058:   DM              dmAux[3]   = {NULL, NULL, NULL};
5059:   DMLabel         ghostLabel = NULL;
5060:   PetscDS         ds         = NULL;
5061:   PetscDS         dsIn       = NULL;
5062:   PetscDS         dsAux[3]   = {NULL, NULL, NULL};
5063:   Vec             locA[3]    = {NULL, NULL, NULL};
5064:   DM              dmScale[3] = {NULL, NULL, NULL};
5065:   PetscDS         dsScale[3] = {NULL, NULL, NULL};
5066:   Vec             locS[3]    = {NULL, NULL, NULL};
5067:   PetscSection    section    = NULL;
5068:   DMField         coordField = NULL;
5069:   PetscScalar    *a[3]       = {NULL, NULL, NULL};
5070:   PetscScalar    *s[3]       = {NULL, NULL, NULL};
5071:   PetscScalar    *u          = NULL, *u_t;
5072:   PetscScalar    *elemVecNeg, *elemVecPos, *elemVecCoh;
5073:   IS              chunkIS;
5074:   const PetscInt *cells;
5075:   PetscInt       *faces;
5076:   PetscInt        cStart, cEnd, numCells;
5077:   PetscInt        Nf, f, totDim, totDimIn, totDimAux[3], totDimScale, numChunks, cellChunkSize, chunk;
5078:   PetscInt        maxDegree  = PETSC_MAX_INT;
5079:   PetscQuadrature affineQuad = NULL, *quads = NULL;
5080:   PetscFEGeom    *affineGeom = NULL, **geoms = NULL;

5082:   PetscFunctionBegin;
5083:   if (!cellIS) PetscFunctionReturn(PETSC_SUCCESS);
5084:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
5085:   PetscCall(ISGetLocalSize(cellIS, &numCells));
5086:   if (cStart >= cEnd) PetscFunctionReturn(PETSC_SUCCESS);
5087:   if ((key[0].label == key[1].label) && (key[0].value == key[1].value) && (key[0].part == key[1].part)) {
5088:     const char *name;
5089:     PetscCall(PetscObjectGetName((PetscObject)key[0].label, &name));
5090:     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);
5091:   }
5092:   PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
5093:   /* TODO The places where we have to use isFE are probably the member functions for the PetscDisc class */
5094:   /* FEM */
5095:   /* 1: Get sizes from dm and dmAux */
5096:   PetscCall(DMGetSection(dm, &section));
5097:   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
5098:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn));
5099:   PetscCall(PetscDSGetNumFields(ds, &Nf));
5100:   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
5101:   PetscCall(PetscDSGetTotalDimension(dsIn, &totDimIn));
5102:   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, key[2].value, key[2].part, &locA[2]));
5103:   if (locA[2]) {
5104:     const PetscInt cellStart = cells ? cells[cStart] : cStart;

5106:     PetscCall(VecGetDM(locA[2], &dmAux[2]));
5107:     PetscCall(DMGetCellDS(dmAux[2], cellStart, &dsAux[2], NULL));
5108:     PetscCall(PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]));
5109:     {
5110:       const PetscInt *cone;
5111:       PetscInt        c;

5113:       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
5114:       for (c = 0; c < 2; ++c) {
5115:         const PetscInt *support;
5116:         PetscInt        ssize, s;

5118:         PetscCall(DMPlexGetSupport(dm, cone[c], &support));
5119:         PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
5120:         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);
5121:         if (support[0] == cellStart) s = 1;
5122:         else if (support[1] == cellStart) s = 0;
5123:         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
5124:         PetscCall(DMGetAuxiliaryVec(dm, key[c].label, key[c].value, key[c].part, &locA[c]));
5125:         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);
5126:         if (locA[c]) PetscCall(VecGetDM(locA[c], &dmAux[c]));
5127:         else dmAux[c] = dmAux[2];
5128:         PetscCall(DMGetCellDS(dmAux[c], support[s], &dsAux[c], NULL));
5129:         PetscCall(PetscDSGetTotalDimension(dsAux[c], &totDimAux[c]));
5130:       }
5131:     }
5132:   }
5133:   /* Handle mass matrix scaling
5134:        The field in key[2] is the field to be scaled, and the scaling field is the first in the dsScale */
5135:   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, -key[2].value, key[2].part, &locS[2]));
5136:   if (locS[2]) {
5137:     PetscInt Nb, Nbs;

5139:     PetscCall(VecGetDM(locS[2], &dmScale[2]));
5140:     PetscCall(DMGetCellDS(dmScale[2], cells ? cells[cStart] : cStart, &dsScale[2], NULL));
5141:     locS[1] = locS[0] = locS[2];
5142:     dmScale[1] = dmScale[0] = dmScale[2];
5143:     dsScale[1] = dsScale[0] = dsScale[2];
5144:     PetscCall(PetscDSGetTotalDimension(dsScale[2], &totDimScale));
5145:     // BRAD: This is not set correctly
5146:     key[2].field = 2;
5147:     PetscCall(PetscDSGetFieldSize(ds, key[2].field, &Nb));
5148:     PetscCall(PetscDSGetFieldSize(dsScale[2], 0, &Nbs));
5149:     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);
5150:   }
5151:   /* 2: Setup geometric data */
5152:   PetscCall(DMGetCoordinateField(dm, &coordField));
5153:   PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
5154:   if (maxDegree > 1) {
5155:     PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
5156:     for (f = 0; f < Nf; ++f) {
5157:       PetscFE fe;

5159:       PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
5160:       if (fe) {
5161:         PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
5162:         PetscCall(PetscObjectReference((PetscObject)quads[f]));
5163:       }
5164:     }
5165:   }
5166:   /* Loop over chunks */
5167:   cellChunkSize = numCells;
5168:   numChunks     = !numCells ? 0 : PetscCeilReal(((PetscReal)numCells) / cellChunkSize);
5169:   PetscCall(PetscCalloc1(2 * cellChunkSize, &faces));
5170:   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 2 * cellChunkSize, faces, PETSC_USE_POINTER, &chunkIS));
5171:   /* Extract field coefficients */
5172:   /* NOTE This needs the end cap faces to have identical orientations */
5173:   PetscCall(DMPlexGetHybridCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
5174:   PetscCall(DMPlexGetHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
5175:   PetscCall(DMPlexGetHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_FALSE, s));
5176:   PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecNeg));
5177:   PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecPos));
5178:   PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecCoh));
5179:   for (chunk = 0; chunk < numChunks; ++chunk) {
5180:     PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;

5182:     PetscCall(PetscArrayzero(elemVecNeg, cellChunkSize * totDim));
5183:     PetscCall(PetscArrayzero(elemVecPos, cellChunkSize * totDim));
5184:     PetscCall(PetscArrayzero(elemVecCoh, cellChunkSize * totDim));
5185:     /* Get faces */
5186:     for (c = cS; c < cE; ++c) {
5187:       const PetscInt  cell = cells ? cells[c] : c;
5188:       const PetscInt *cone;
5189:       PetscCall(DMPlexGetCone(dm, cell, &cone));
5190:       faces[(c - cS) * 2 + 0] = cone[0];
5191:       faces[(c - cS) * 2 + 1] = cone[1];
5192:     }
5193:     PetscCall(ISGeneralSetIndices(chunkIS, 2 * cellChunkSize, faces, PETSC_USE_POINTER));
5194:     /* Get geometric data */
5195:     if (maxDegree <= 1) {
5196:       if (!affineQuad) PetscCall(DMFieldCreateDefaultQuadrature(coordField, chunkIS, &affineQuad));
5197:       if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, affineQuad, PETSC_TRUE, &affineGeom));
5198:     } else {
5199:       for (f = 0; f < Nf; ++f) {
5200:         if (quads[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, quads[f], PETSC_TRUE, &geoms[f]));
5201:       }
5202:     }
5203:     /* Loop over fields */
5204:     for (f = 0; f < Nf; ++f) {
5205:       PetscFE         fe;
5206:       PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[f];
5207:       PetscFEGeom    *chunkGeom = NULL, *remGeom = NULL;
5208:       PetscQuadrature quad = affineQuad ? affineQuad : quads[f];
5209:       PetscInt        numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb;
5210:       PetscBool       isCohesiveField;

5212:       PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
5213:       if (!fe) continue;
5214:       PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5215:       PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
5216:       PetscCall(PetscFEGetDimension(fe, &Nb));
5217:       blockSize = Nb;
5218:       batchSize = numBlocks * blockSize;
5219:       PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5220:       numChunks = numCells / (numBatches * batchSize);
5221:       Ne        = numChunks * numBatches * batchSize;
5222:       Nr        = numCells % (numBatches * batchSize);
5223:       offset    = numCells - Nr;
5224:       PetscCall(PetscFEGeomGetChunk(geom, 0, offset * 2, &chunkGeom));
5225:       PetscCall(PetscFEGeomGetChunk(geom, offset * 2, numCells * 2, &remGeom));
5226:       PetscCall(PetscDSGetCohesive(ds, f, &isCohesiveField));
5227:       chunkGeom->isCohesive = remGeom->isCohesive = PETSC_TRUE;
5228:       key[0].field                                = f;
5229:       key[1].field                                = f;
5230:       key[2].field                                = f;
5231:       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, elemVecNeg));
5232:       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[0], 0, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[0], &a[0][offset * totDimAux[0]], t, &elemVecNeg[offset * totDim]));
5233:       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, elemVecPos));
5234:       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[1], 1, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[1], &a[1][offset * totDimAux[1]], t, &elemVecPos[offset * totDim]));
5235:       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, elemVecCoh));
5236:       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[2], 2, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[2], &a[2][offset * totDimAux[2]], t, &elemVecCoh[offset * totDim]));
5237:       PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &remGeom));
5238:       PetscCall(PetscFEGeomRestoreChunk(geom, 0, offset, &chunkGeom));
5239:     }
5240:     /* Add elemVec to locX */
5241:     for (c = cS; c < cE; ++c) {
5242:       const PetscInt cell = cells ? cells[c] : c;
5243:       const PetscInt cind = c - cStart;
5244:       PetscInt       i;

5246:       /* Scale element values */
5247:       if (locS[0]) {
5248:         PetscInt  Nb, off = cind * totDim, soff = cind * totDimScale;
5249:         PetscBool cohesive;

5251:         for (f = 0; f < Nf; ++f) {
5252:           PetscCall(PetscDSGetFieldSize(ds, f, &Nb));
5253:           PetscCall(PetscDSGetCohesive(ds, f, &cohesive));
5254:           if (f == key[2].field) {
5255:             PetscCheck(cohesive, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Scaling should not happen for face fields");
5256:             // No cohesive scaling field is currently input
5257:             for (i = 0; i < Nb; ++i) elemVecCoh[off + i] += s[0][soff + i] * elemVecNeg[off + i] + s[1][soff + i] * elemVecPos[off + i];
5258:             off += Nb;
5259:           } else {
5260:             const PetscInt N = cohesive ? Nb : Nb * 2;

5262:             for (i = 0; i < N; ++i) elemVecCoh[off + i] += elemVecNeg[off + i] + elemVecPos[off + i];
5263:             off += N;
5264:           }
5265:         }
5266:       } else {
5267:         for (i = cind * totDim; i < (cind + 1) * totDim; ++i) elemVecCoh[i] += elemVecNeg[i] + elemVecPos[i];
5268:       }
5269:       if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, name, totDim, &elemVecCoh[cind * totDim]));
5270:       if (ghostLabel) {
5271:         PetscInt ghostVal;

5273:         PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
5274:         if (ghostVal > 0) continue;
5275:       }
5276:       PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVecCoh[cind * totDim], ADD_ALL_VALUES));
5277:     }
5278:   }
5279:   PetscCall(DMPlexRestoreCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
5280:   PetscCall(DMPlexRestoreHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
5281:   PetscCall(DMPlexRestoreHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_FALSE, s));
5282:   PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecNeg));
5283:   PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecPos));
5284:   PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecCoh));
5285:   PetscCall(PetscFree(faces));
5286:   PetscCall(ISDestroy(&chunkIS));
5287:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
5288:   if (maxDegree <= 1) {
5289:     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
5290:     PetscCall(PetscQuadratureDestroy(&affineQuad));
5291:   } else {
5292:     for (f = 0; f < Nf; ++f) {
5293:       if (geoms) PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
5294:       if (quads) PetscCall(PetscQuadratureDestroy(&quads[f]));
5295:     }
5296:     PetscCall(PetscFree2(quads, geoms));
5297:   }
5298:   if (mesh->printFEM) {
5299:     Vec          locFbc;
5300:     PetscInt     pStart, pEnd, p, maxDof;
5301:     PetscScalar *zeroes;

5303:     PetscCall(VecDuplicate(locF, &locFbc));
5304:     PetscCall(VecCopy(locF, locFbc));
5305:     PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5306:     PetscCall(PetscSectionGetMaxDof(section, &maxDof));
5307:     PetscCall(PetscCalloc1(maxDof, &zeroes));
5308:     for (p = pStart; p < pEnd; p++) PetscCall(VecSetValuesSection(locFbc, section, p, zeroes, INSERT_BC_VALUES));
5309:     PetscCall(PetscFree(zeroes));
5310:     PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc));
5311:     PetscCall(VecDestroy(&locFbc));
5312:   }
5313:   PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
5314:   PetscFunctionReturn(PETSC_SUCCESS);
5315: }

5317: PetscErrorCode DMPlexComputeBdJacobian_Single_Internal(DM dm, PetscReal t, PetscWeakForm wf, DMLabel label, PetscInt numValues, const PetscInt values[], PetscInt fieldI, Vec locX, Vec locX_t, PetscReal X_tShift, Mat Jac, Mat JacP, DMField coordField, IS facetIS)
5318: {
5319:   DM_Plex        *mesh = (DM_Plex *)dm->data;
5320:   DM              plex = NULL, plexA = NULL, tdm;
5321:   DMEnclosureType encAux;
5322:   PetscDS         prob, probAux       = NULL;
5323:   PetscSection    section, sectionAux = NULL;
5324:   PetscSection    globalSection;
5325:   Vec             locA = NULL, tv;
5326:   PetscScalar    *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL;
5327:   PetscInt        v;
5328:   PetscInt        Nf, totDim, totDimAux = 0;
5329:   PetscBool       transform;

5331:   PetscFunctionBegin;
5332:   PetscCall(DMConvert(dm, DMPLEX, &plex));
5333:   PetscCall(DMHasBasisTransform(dm, &transform));
5334:   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
5335:   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
5336:   PetscCall(DMGetLocalSection(dm, &section));
5337:   PetscCall(DMGetDS(dm, &prob));
5338:   PetscCall(PetscDSGetNumFields(prob, &Nf));
5339:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
5340:   PetscCall(DMGetAuxiliaryVec(dm, label, values[0], 0, &locA));
5341:   if (locA) {
5342:     DM dmAux;

5344:     PetscCall(VecGetDM(locA, &dmAux));
5345:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
5346:     PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
5347:     PetscCall(DMGetDS(plexA, &probAux));
5348:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
5349:     PetscCall(DMGetLocalSection(plexA, &sectionAux));
5350:   }

5352:   PetscCall(DMGetGlobalSection(dm, &globalSection));
5353:   for (v = 0; v < numValues; ++v) {
5354:     PetscFEGeom    *fgeom;
5355:     PetscInt        maxDegree;
5356:     PetscQuadrature qGeom = NULL;
5357:     IS              pointIS;
5358:     const PetscInt *points;
5359:     PetscFormKey    key;
5360:     PetscInt        numFaces, face, Nq;

5362:     key.label = label;
5363:     key.value = values[v];
5364:     key.part  = 0;
5365:     PetscCall(DMLabelGetStratumIS(label, values[v], &pointIS));
5366:     if (!pointIS) continue; /* No points with that id on this process */
5367:     {
5368:       IS isectIS;

5370:       /* TODO: Special cases of ISIntersect where it is quick to check a prior if one is a superset of the other */
5371:       PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS));
5372:       PetscCall(ISDestroy(&pointIS));
5373:       pointIS = isectIS;
5374:     }
5375:     PetscCall(ISGetLocalSize(pointIS, &numFaces));
5376:     PetscCall(ISGetIndices(pointIS, &points));
5377:     PetscCall(PetscMalloc4(numFaces * totDim, &u, locX_t ? numFaces * totDim : 0, &u_t, numFaces * totDim * totDim, &elemMat, locA ? numFaces * totDimAux : 0, &a));
5378:     PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
5379:     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom));
5380:     if (!qGeom) {
5381:       PetscFE fe;

5383:       PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
5384:       PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
5385:       PetscCall(PetscObjectReference((PetscObject)qGeom));
5386:     }
5387:     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
5388:     PetscCall(DMSNESGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
5389:     for (face = 0; face < numFaces; ++face) {
5390:       const PetscInt point = points[face], *support;
5391:       PetscScalar   *x     = NULL;
5392:       PetscInt       i;

5394:       PetscCall(DMPlexGetSupport(dm, point, &support));
5395:       PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
5396:       for (i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
5397:       PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
5398:       if (locX_t) {
5399:         PetscCall(DMPlexVecGetClosure(plex, section, locX_t, support[0], NULL, &x));
5400:         for (i = 0; i < totDim; ++i) u_t[face * totDim + i] = x[i];
5401:         PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, support[0], NULL, &x));
5402:       }
5403:       if (locA) {
5404:         PetscInt subp;
5405:         PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
5406:         PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
5407:         for (i = 0; i < totDimAux; ++i) a[face * totDimAux + i] = x[i];
5408:         PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
5409:       }
5410:     }
5411:     PetscCall(PetscArrayzero(elemMat, numFaces * totDim * totDim));
5412:     {
5413:       PetscFE  fe;
5414:       PetscInt Nb;
5415:       /* Conforming batches */
5416:       PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
5417:       /* Remainder */
5418:       PetscFEGeom *chunkGeom = NULL;
5419:       PetscInt     fieldJ, Nr, offset;

5421:       PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
5422:       PetscCall(PetscFEGetDimension(fe, &Nb));
5423:       PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5424:       blockSize = Nb;
5425:       batchSize = numBlocks * blockSize;
5426:       PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5427:       numChunks = numFaces / (numBatches * batchSize);
5428:       Ne        = numChunks * numBatches * batchSize;
5429:       Nr        = numFaces % (numBatches * batchSize);
5430:       offset    = numFaces - Nr;
5431:       PetscCall(PetscFEGeomGetChunk(fgeom, 0, offset, &chunkGeom));
5432:       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5433:         key.field = fieldI * Nf + fieldJ;
5434:         PetscCall(PetscFEIntegrateBdJacobian(prob, wf, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat));
5435:       }
5436:       PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom));
5437:       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5438:         key.field = fieldI * Nf + fieldJ;
5439:         PetscCall(PetscFEIntegrateBdJacobian(prob, wf, key, Nr, chunkGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, a ? &a[offset * totDimAux] : NULL, t, X_tShift, &elemMat[offset * totDim * totDim]));
5440:       }
5441:       PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom));
5442:     }
5443:     for (face = 0; face < numFaces; ++face) {
5444:       const PetscInt point = points[face], *support;

5446:       /* Transform to global basis before insertion in Jacobian */
5447:       PetscCall(DMPlexGetSupport(plex, point, &support));
5448:       if (transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, support[0], PETSC_TRUE, totDim, &elemMat[face * totDim * totDim]));
5449:       if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(point, "BdJacobian", totDim, totDim, &elemMat[face * totDim * totDim]));
5450:       PetscCall(DMPlexMatSetClosure(plex, section, globalSection, JacP, support[0], &elemMat[face * totDim * totDim], ADD_VALUES));
5451:     }
5452:     PetscCall(DMSNESRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
5453:     PetscCall(PetscQuadratureDestroy(&qGeom));
5454:     PetscCall(ISRestoreIndices(pointIS, &points));
5455:     PetscCall(ISDestroy(&pointIS));
5456:     PetscCall(PetscFree4(u, u_t, elemMat, a));
5457:   }
5458:   if (plex) PetscCall(DMDestroy(&plex));
5459:   if (plexA) PetscCall(DMDestroy(&plexA));
5460:   PetscFunctionReturn(PETSC_SUCCESS);
5461: }

5463: PetscErrorCode DMPlexComputeBdJacobianSingle(DM dm, PetscReal t, PetscWeakForm wf, DMLabel label, PetscInt numValues, const PetscInt values[], PetscInt field, Vec locX, Vec locX_t, PetscReal X_tShift, Mat Jac, Mat JacP)
5464: {
5465:   DMField  coordField;
5466:   DMLabel  depthLabel;
5467:   IS       facetIS;
5468:   PetscInt dim;

5470:   PetscFunctionBegin;
5471:   PetscCall(DMGetDimension(dm, &dim));
5472:   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5473:   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
5474:   PetscCall(DMGetCoordinateField(dm, &coordField));
5475:   PetscCall(DMPlexComputeBdJacobian_Single_Internal(dm, t, wf, label, numValues, values, field, locX, locX_t, X_tShift, Jac, JacP, coordField, facetIS));
5476:   PetscCall(ISDestroy(&facetIS));
5477:   PetscFunctionReturn(PETSC_SUCCESS);
5478: }

5480: PetscErrorCode DMPlexComputeBdJacobian_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, PetscReal X_tShift, Mat Jac, Mat JacP, void *user)
5481: {
5482:   PetscDS  prob;
5483:   PetscInt dim, numBd, bd;
5484:   DMLabel  depthLabel;
5485:   DMField  coordField = NULL;
5486:   IS       facetIS;

5488:   PetscFunctionBegin;
5489:   PetscCall(DMGetDS(dm, &prob));
5490:   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5491:   PetscCall(DMGetDimension(dm, &dim));
5492:   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
5493:   PetscCall(PetscDSGetNumBoundary(prob, &numBd));
5494:   PetscCall(DMGetCoordinateField(dm, &coordField));
5495:   for (bd = 0; bd < numBd; ++bd) {
5496:     PetscWeakForm           wf;
5497:     DMBoundaryConditionType type;
5498:     DMLabel                 label;
5499:     const PetscInt         *values;
5500:     PetscInt                fieldI, numValues;
5501:     PetscObject             obj;
5502:     PetscClassId            id;

5504:     PetscCall(PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &fieldI, NULL, NULL, NULL, NULL, NULL));
5505:     if (type & DM_BC_ESSENTIAL) continue;
5506:     PetscCall(PetscDSGetDiscretization(prob, fieldI, &obj));
5507:     PetscCall(PetscObjectGetClassId(obj, &id));
5508:     if (id != PETSCFE_CLASSID) continue;
5509:     PetscCall(DMPlexComputeBdJacobian_Single_Internal(dm, t, wf, label, numValues, values, fieldI, locX, locX_t, X_tShift, Jac, JacP, coordField, facetIS));
5510:   }
5511:   PetscCall(ISDestroy(&facetIS));
5512:   PetscFunctionReturn(PETSC_SUCCESS);
5513: }

5515: PetscErrorCode DMPlexComputeJacobian_Internal(DM dm, PetscFormKey key, IS cellIS, PetscReal t, PetscReal X_tShift, Vec X, Vec X_t, Mat Jac, Mat JacP, void *user)
5516: {
5517:   DM_Plex        *mesh  = (DM_Plex *)dm->data;
5518:   const char     *name  = "Jacobian";
5519:   DM              dmAux = NULL, plex, tdm;
5520:   DMEnclosureType encAux;
5521:   Vec             A, tv;
5522:   DMField         coordField;
5523:   PetscDS         prob, probAux = NULL;
5524:   PetscSection    section, globalSection, sectionAux;
5525:   PetscScalar    *elemMat, *elemMatP, *elemMatD, *u, *u_t, *a = NULL;
5526:   const PetscInt *cells;
5527:   PetscInt        Nf, fieldI, fieldJ;
5528:   PetscInt        totDim, totDimAux = 0, cStart, cEnd, numCells, c;
5529:   PetscBool       hasJac = PETSC_FALSE, hasPrec = PETSC_FALSE, hasDyn, hasFV = PETSC_FALSE, transform;

5531:   PetscFunctionBegin;
5532:   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
5533:   if (!cellIS) goto end;
5534:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
5535:   PetscCall(ISGetLocalSize(cellIS, &numCells));
5536:   if (cStart >= cEnd) goto end;
5537:   PetscCall(DMHasBasisTransform(dm, &transform));
5538:   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
5539:   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
5540:   PetscCall(DMGetLocalSection(dm, &section));
5541:   PetscCall(DMGetGlobalSection(dm, &globalSection));
5542:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL));
5543:   PetscCall(PetscDSGetNumFields(prob, &Nf));
5544:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
5545:   PetscCall(PetscDSHasJacobian(prob, &hasJac));
5546:   PetscCall(PetscDSHasJacobianPreconditioner(prob, &hasPrec));
5547:   /* user passed in the same matrix, avoid double contributions and
5548:      only assemble the Jacobian */
5549:   if (hasJac && Jac == JacP) hasPrec = PETSC_FALSE;
5550:   PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
5551:   hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
5552:   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &A));
5553:   if (A) {
5554:     PetscCall(VecGetDM(A, &dmAux));
5555:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
5556:     PetscCall(DMConvert(dmAux, DMPLEX, &plex));
5557:     PetscCall(DMGetLocalSection(plex, &sectionAux));
5558:     PetscCall(DMGetDS(dmAux, &probAux));
5559:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
5560:   }
5561:   PetscCall(PetscMalloc5(numCells * totDim, &u, X_t ? numCells * totDim : 0, &u_t, hasJac ? numCells * totDim * totDim : 0, &elemMat, hasPrec ? numCells * totDim * totDim : 0, &elemMatP, hasDyn ? numCells * totDim * totDim : 0, &elemMatD));
5562:   if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
5563:   PetscCall(DMGetCoordinateField(dm, &coordField));
5564:   for (c = cStart; c < cEnd; ++c) {
5565:     const PetscInt cell = cells ? cells[c] : c;
5566:     const PetscInt cind = c - cStart;
5567:     PetscScalar   *x = NULL, *x_t = NULL;
5568:     PetscInt       i;

5570:     PetscCall(DMPlexVecGetClosure(dm, section, X, cell, NULL, &x));
5571:     for (i = 0; i < totDim; ++i) u[cind * totDim + i] = x[i];
5572:     PetscCall(DMPlexVecRestoreClosure(dm, section, X, cell, NULL, &x));
5573:     if (X_t) {
5574:       PetscCall(DMPlexVecGetClosure(dm, section, X_t, cell, NULL, &x_t));
5575:       for (i = 0; i < totDim; ++i) u_t[cind * totDim + i] = x_t[i];
5576:       PetscCall(DMPlexVecRestoreClosure(dm, section, X_t, cell, NULL, &x_t));
5577:     }
5578:     if (dmAux) {
5579:       PetscInt subcell;
5580:       PetscCall(DMGetEnclosurePoint(dmAux, dm, encAux, cell, &subcell));
5581:       PetscCall(DMPlexVecGetClosure(plex, sectionAux, A, subcell, NULL, &x));
5582:       for (i = 0; i < totDimAux; ++i) a[cind * totDimAux + i] = x[i];
5583:       PetscCall(DMPlexVecRestoreClosure(plex, sectionAux, A, subcell, NULL, &x));
5584:     }
5585:   }
5586:   if (hasJac) PetscCall(PetscArrayzero(elemMat, numCells * totDim * totDim));
5587:   if (hasPrec) PetscCall(PetscArrayzero(elemMatP, numCells * totDim * totDim));
5588:   if (hasDyn) PetscCall(PetscArrayzero(elemMatD, numCells * totDim * totDim));
5589:   for (fieldI = 0; fieldI < Nf; ++fieldI) {
5590:     PetscClassId    id;
5591:     PetscFE         fe;
5592:     PetscQuadrature qGeom = NULL;
5593:     PetscInt        Nb;
5594:     /* Conforming batches */
5595:     PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
5596:     /* Remainder */
5597:     PetscInt     Nr, offset, Nq;
5598:     PetscInt     maxDegree;
5599:     PetscFEGeom *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL;

5601:     PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
5602:     PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
5603:     if (id == PETSCFV_CLASSID) {
5604:       hasFV = PETSC_TRUE;
5605:       continue;
5606:     }
5607:     PetscCall(PetscFEGetDimension(fe, &Nb));
5608:     PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5609:     PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
5610:     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
5611:     if (!qGeom) {
5612:       PetscCall(PetscFEGetQuadrature(fe, &qGeom));
5613:       PetscCall(PetscObjectReference((PetscObject)qGeom));
5614:     }
5615:     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
5616:     PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
5617:     blockSize = Nb;
5618:     batchSize = numBlocks * blockSize;
5619:     PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5620:     numChunks = numCells / (numBatches * batchSize);
5621:     Ne        = numChunks * numBatches * batchSize;
5622:     Nr        = numCells % (numBatches * batchSize);
5623:     offset    = numCells - Nr;
5624:     PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
5625:     PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &remGeom));
5626:     for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5627:       key.field = fieldI * Nf + fieldJ;
5628:       if (hasJac) {
5629:         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat));
5630:         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Nr, remGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, &a[offset * totDimAux], t, X_tShift, &elemMat[offset * totDim * totDim]));
5631:       }
5632:       if (hasPrec) {
5633:         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatP));
5634:         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, key, Nr, remGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, &a[offset * totDimAux], t, X_tShift, &elemMatP[offset * totDim * totDim]));
5635:       }
5636:       if (hasDyn) {
5637:         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatD));
5638:         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Nr, remGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, &a[offset * totDimAux], t, X_tShift, &elemMatD[offset * totDim * totDim]));
5639:       }
5640:     }
5641:     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &remGeom));
5642:     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, 0, offset, &chunkGeom));
5643:     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
5644:     PetscCall(PetscQuadratureDestroy(&qGeom));
5645:   }
5646:   /*   Add contribution from X_t */
5647:   if (hasDyn) {
5648:     for (c = 0; c < numCells * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
5649:   }
5650:   if (hasFV) {
5651:     PetscClassId id;
5652:     PetscFV      fv;
5653:     PetscInt     offsetI, NcI, NbI = 1, fc, f;

5655:     for (fieldI = 0; fieldI < Nf; ++fieldI) {
5656:       PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fv));
5657:       PetscCall(PetscDSGetFieldOffset(prob, fieldI, &offsetI));
5658:       PetscCall(PetscObjectGetClassId((PetscObject)fv, &id));
5659:       if (id != PETSCFV_CLASSID) continue;
5660:       /* Put in the identity */
5661:       PetscCall(PetscFVGetNumComponents(fv, &NcI));
5662:       for (c = cStart; c < cEnd; ++c) {
5663:         const PetscInt cind    = c - cStart;
5664:         const PetscInt eOffset = cind * totDim * totDim;
5665:         for (fc = 0; fc < NcI; ++fc) {
5666:           for (f = 0; f < NbI; ++f) {
5667:             const PetscInt i = offsetI + f * NcI + fc;
5668:             if (hasPrec) {
5669:               if (hasJac) elemMat[eOffset + i * totDim + i] = 1.0;
5670:               elemMatP[eOffset + i * totDim + i] = 1.0;
5671:             } else {
5672:               elemMat[eOffset + i * totDim + i] = 1.0;
5673:             }
5674:           }
5675:         }
5676:       }
5677:     }
5678:     /* No allocated space for FV stuff, so ignore the zero entries */
5679:     PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE));
5680:   }
5681:   /* Insert values into matrix */
5682:   for (c = cStart; c < cEnd; ++c) {
5683:     const PetscInt cell = cells ? cells[c] : c;
5684:     const PetscInt cind = c - cStart;

5686:     /* Transform to global basis before insertion in Jacobian */
5687:     if (transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, cell, PETSC_TRUE, totDim, &elemMat[cind * totDim * totDim]));
5688:     if (hasPrec) {
5689:       if (hasJac) {
5690:         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
5691:         PetscCall(DMPlexMatSetClosure(dm, section, globalSection, Jac, cell, &elemMat[cind * totDim * totDim], ADD_VALUES));
5692:       }
5693:       if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatP[cind * totDim * totDim]));
5694:       PetscCall(DMPlexMatSetClosure(dm, section, globalSection, JacP, cell, &elemMatP[cind * totDim * totDim], ADD_VALUES));
5695:     } else {
5696:       if (hasJac) {
5697:         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
5698:         PetscCall(DMPlexMatSetClosure(dm, section, globalSection, JacP, cell, &elemMat[cind * totDim * totDim], ADD_VALUES));
5699:       }
5700:     }
5701:   }
5702:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
5703:   if (hasFV) PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE));
5704:   PetscCall(PetscFree5(u, u_t, elemMat, elemMatP, elemMatD));
5705:   if (dmAux) {
5706:     PetscCall(PetscFree(a));
5707:     PetscCall(DMDestroy(&plex));
5708:   }
5709:   /* Compute boundary integrals */
5710:   PetscCall(DMPlexComputeBdJacobian_Internal(dm, X, X_t, t, X_tShift, Jac, JacP, user));
5711:   /* Assemble matrix */
5712: end : {
5713:   PetscBool assOp = hasJac && hasPrec ? PETSC_TRUE : PETSC_FALSE, gassOp;

5715:   PetscCall(MPIU_Allreduce(&assOp, &gassOp, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
5716:   if (hasJac && hasPrec) {
5717:     PetscCall(MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY));
5718:     PetscCall(MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY));
5719:   }
5720: }
5721:   PetscCall(MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY));
5722:   PetscCall(MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY));
5723:   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
5724:   PetscFunctionReturn(PETSC_SUCCESS);
5725: }

5727: PetscErrorCode DMPlexComputeJacobian_Hybrid_Internal(DM dm, PetscFormKey key[], IS cellIS, PetscReal t, PetscReal X_tShift, Vec locX, Vec locX_t, Mat Jac, Mat JacP, void *user)
5728: {
5729:   DM_Plex        *mesh          = (DM_Plex *)dm->data;
5730:   const char     *name          = "Hybrid Jacobian";
5731:   DM              dmAux[3]      = {NULL, NULL, NULL};
5732:   DMLabel         ghostLabel    = NULL;
5733:   DM              plex          = NULL;
5734:   DM              plexA         = NULL;
5735:   PetscDS         ds            = NULL;
5736:   PetscDS         dsIn          = NULL;
5737:   PetscDS         dsAux[3]      = {NULL, NULL, NULL};
5738:   Vec             locA[3]       = {NULL, NULL, NULL};
5739:   DM              dmScale[3]    = {NULL, NULL, NULL};
5740:   PetscDS         dsScale[3]    = {NULL, NULL, NULL};
5741:   Vec             locS[3]       = {NULL, NULL, NULL};
5742:   PetscSection    section       = NULL;
5743:   PetscSection    sectionAux[3] = {NULL, NULL, NULL};
5744:   DMField         coordField    = NULL;
5745:   PetscScalar    *a[3]          = {NULL, NULL, NULL};
5746:   PetscScalar    *s[3]          = {NULL, NULL, NULL};
5747:   PetscScalar    *u             = NULL, *u_t;
5748:   PetscScalar    *elemMatNeg, *elemMatPos, *elemMatCoh;
5749:   PetscScalar    *elemMatNegP, *elemMatPosP, *elemMatCohP;
5750:   PetscSection    globalSection;
5751:   IS              chunkIS;
5752:   const PetscInt *cells;
5753:   PetscInt       *faces;
5754:   PetscInt        cStart, cEnd, numCells;
5755:   PetscInt        Nf, fieldI, fieldJ, totDim, totDimIn, totDimAux[3], totDimScale, numChunks, cellChunkSize, chunk;
5756:   PetscInt        maxDegree  = PETSC_MAX_INT;
5757:   PetscQuadrature affineQuad = NULL, *quads = NULL;
5758:   PetscFEGeom    *affineGeom = NULL, **geoms = NULL;
5759:   PetscBool       hasBdJac, hasBdPrec;

5761:   PetscFunctionBegin;
5762:   if (!cellIS) PetscFunctionReturn(PETSC_SUCCESS);
5763:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
5764:   PetscCall(ISGetLocalSize(cellIS, &numCells));
5765:   if (cStart >= cEnd) PetscFunctionReturn(PETSC_SUCCESS);
5766:   if ((key[0].label == key[1].label) && (key[0].value == key[1].value) && (key[0].part == key[1].part)) {
5767:     const char *name;
5768:     PetscCall(PetscObjectGetName((PetscObject)key[0].label, &name));
5769:     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);
5770:   }
5771:   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
5772:   PetscCall(DMConvert(dm, DMPLEX, &plex));
5773:   PetscCall(DMGetSection(dm, &section));
5774:   PetscCall(DMGetGlobalSection(dm, &globalSection));
5775:   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
5776:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn));
5777:   PetscCall(PetscDSGetNumFields(ds, &Nf));
5778:   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
5779:   PetscCall(PetscDSGetTotalDimension(dsIn, &totDimIn));
5780:   PetscCall(PetscDSHasBdJacobian(ds, &hasBdJac));
5781:   PetscCall(PetscDSHasBdJacobianPreconditioner(ds, &hasBdPrec));
5782:   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, key[2].value, key[2].part, &locA[2]));
5783:   if (locA[2]) {
5784:     const PetscInt cellStart = cells ? cells[cStart] : cStart;

5786:     PetscCall(VecGetDM(locA[2], &dmAux[2]));
5787:     PetscCall(DMConvert(dmAux[2], DMPLEX, &plexA));
5788:     PetscCall(DMGetSection(dmAux[2], &sectionAux[2]));
5789:     PetscCall(DMGetCellDS(dmAux[2], cellStart, &dsAux[2], NULL));
5790:     PetscCall(PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]));
5791:     {
5792:       const PetscInt *cone;
5793:       PetscInt        c;

5795:       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
5796:       for (c = 0; c < 2; ++c) {
5797:         const PetscInt *support;
5798:         PetscInt        ssize, s;

5800:         PetscCall(DMPlexGetSupport(dm, cone[c], &support));
5801:         PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
5802:         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);
5803:         if (support[0] == cellStart) s = 1;
5804:         else if (support[1] == cellStart) s = 0;
5805:         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
5806:         PetscCall(DMGetAuxiliaryVec(dm, key[c].label, key[c].value, key[c].part, &locA[c]));
5807:         if (locA[c]) PetscCall(VecGetDM(locA[c], &dmAux[c]));
5808:         else dmAux[c] = dmAux[2];
5809:         PetscCall(DMGetCellDS(dmAux[c], support[s], &dsAux[c], NULL));
5810:         PetscCall(PetscDSGetTotalDimension(dsAux[c], &totDimAux[c]));
5811:       }
5812:     }
5813:   }
5814:   /* Handle mass matrix scaling
5815:        The field in key[2] is the field to be scaled, and the scaling field is the first in the dsScale */
5816:   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, -key[2].value, key[2].part, &locS[2]));
5817:   if (locS[2]) {
5818:     PetscInt Nb, Nbs;

5820:     PetscCall(VecGetDM(locS[2], &dmScale[2]));
5821:     PetscCall(DMGetCellDS(dmScale[2], cells ? cells[cStart] : cStart, &dsScale[2], NULL));
5822:     locS[1] = locS[0] = locS[2];
5823:     dmScale[1] = dmScale[0] = dmScale[2];
5824:     dsScale[1] = dsScale[0] = dsScale[2];
5825:     PetscCall(PetscDSGetTotalDimension(dsScale[2], &totDimScale));
5826:     // BRAD: This is not set correctly
5827:     key[2].field = 2;
5828:     PetscCall(PetscDSGetFieldSize(ds, key[2].field, &Nb));
5829:     PetscCall(PetscDSGetFieldSize(dsScale[2], 0, &Nbs));
5830:     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);
5831:   }
5832:   /* 2: Setup geometric data */
5833:   PetscCall(DMGetCoordinateField(dm, &coordField));
5834:   PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
5835:   if (maxDegree > 1) {
5836:     PetscInt f;
5837:     PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
5838:     for (f = 0; f < Nf; ++f) {
5839:       PetscFE fe;

5841:       PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
5842:       if (fe) {
5843:         PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
5844:         PetscCall(PetscObjectReference((PetscObject)quads[f]));
5845:       }
5846:     }
5847:   }
5848:   /* Loop over chunks */
5849:   cellChunkSize = numCells;
5850:   numChunks     = !numCells ? 0 : PetscCeilReal(((PetscReal)numCells) / cellChunkSize);
5851:   PetscCall(PetscCalloc1(2 * cellChunkSize, &faces));
5852:   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 1 * cellChunkSize, faces, PETSC_USE_POINTER, &chunkIS));
5853:   /* Extract field coefficients */
5854:   /* NOTE This needs the end cap faces to have identical orientations */
5855:   PetscCall(DMPlexGetHybridCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
5856:   PetscCall(DMPlexGetHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
5857:   PetscCall(DMPlexGetHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_FALSE, s));
5858:   PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNeg));
5859:   PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPos));
5860:   PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCoh));
5861:   PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNegP));
5862:   PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPosP));
5863:   PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCohP));
5864:   for (chunk = 0; chunk < numChunks; ++chunk) {
5865:     PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;

5867:     if (hasBdJac) {
5868:       PetscCall(PetscArrayzero(elemMatNeg, cellChunkSize * totDim * totDim));
5869:       PetscCall(PetscArrayzero(elemMatPos, cellChunkSize * totDim * totDim));
5870:       PetscCall(PetscArrayzero(elemMatCoh, cellChunkSize * totDim * totDim));
5871:     }
5872:     if (hasBdPrec) {
5873:       PetscCall(PetscArrayzero(elemMatNegP, cellChunkSize * totDim * totDim));
5874:       PetscCall(PetscArrayzero(elemMatPosP, cellChunkSize * totDim * totDim));
5875:       PetscCall(PetscArrayzero(elemMatCohP, cellChunkSize * totDim * totDim));
5876:     }
5877:     /* Get faces */
5878:     for (c = cS; c < cE; ++c) {
5879:       const PetscInt  cell = cells ? cells[c] : c;
5880:       const PetscInt *cone;
5881:       PetscCall(DMPlexGetCone(plex, cell, &cone));
5882:       faces[(c - cS) * 2 + 0] = cone[0];
5883:       faces[(c - cS) * 2 + 1] = cone[1];
5884:     }
5885:     PetscCall(ISGeneralSetIndices(chunkIS, 2 * cellChunkSize, faces, PETSC_USE_POINTER));
5886:     if (maxDegree <= 1) {
5887:       if (!affineQuad) PetscCall(DMFieldCreateDefaultQuadrature(coordField, chunkIS, &affineQuad));
5888:       if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, affineQuad, PETSC_TRUE, &affineGeom));
5889:     } else {
5890:       PetscInt f;
5891:       for (f = 0; f < Nf; ++f) {
5892:         if (quads[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, quads[f], PETSC_TRUE, &geoms[f]));
5893:       }
5894:     }

5896:     for (fieldI = 0; fieldI < Nf; ++fieldI) {
5897:       PetscFE         feI;
5898:       PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[fieldI];
5899:       PetscFEGeom    *chunkGeom = NULL, *remGeom = NULL;
5900:       PetscQuadrature quad = affineQuad ? affineQuad : quads[fieldI];
5901:       PetscInt        numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb;
5902:       PetscBool       isCohesiveField;

5904:       PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&feI));
5905:       if (!feI) continue;
5906:       PetscCall(PetscFEGetTileSizes(feI, NULL, &numBlocks, NULL, &numBatches));
5907:       PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
5908:       PetscCall(PetscFEGetDimension(feI, &Nb));
5909:       blockSize = Nb;
5910:       batchSize = numBlocks * blockSize;
5911:       PetscCall(PetscFESetTileSizes(feI, blockSize, numBlocks, batchSize, numBatches));
5912:       numChunks = numCells / (numBatches * batchSize);
5913:       Ne        = numChunks * numBatches * batchSize;
5914:       Nr        = numCells % (numBatches * batchSize);
5915:       offset    = numCells - Nr;
5916:       PetscCall(PetscFEGeomGetChunk(geom, 0, offset * 2, &chunkGeom));
5917:       PetscCall(PetscFEGeomGetChunk(geom, offset * 2, numCells * 2, &remGeom));
5918:       PetscCall(PetscDSGetCohesive(ds, fieldI, &isCohesiveField));
5919:       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5920:         PetscFE feJ;

5922:         PetscCall(PetscDSGetDiscretization(ds, fieldJ, (PetscObject *)&feJ));
5923:         if (!feJ) continue;
5924:         key[0].field = fieldI * Nf + fieldJ;
5925:         key[1].field = fieldI * Nf + fieldJ;
5926:         key[2].field = fieldI * Nf + fieldJ;
5927:         if (hasBdJac) {
5928:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, X_tShift, elemMatNeg));
5929:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[0], 0, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[0], &a[0][offset * totDimAux[0]], t, X_tShift, &elemMatNeg[offset * totDim * totDim]));
5930:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, X_tShift, elemMatPos));
5931:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[1], 1, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[1], &a[1][offset * totDimAux[1]], t, X_tShift, &elemMatPos[offset * totDim * totDim]));
5932:         }
5933:         if (hasBdPrec) {
5934:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, X_tShift, elemMatNegP));
5935:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[0], 0, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[0], &a[0][offset * totDimAux[0]], t, X_tShift, &elemMatNegP[offset * totDim * totDim]));
5936:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, X_tShift, elemMatPosP));
5937:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[1], 1, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[1], &a[1][offset * totDimAux[1]], t, X_tShift, &elemMatPosP[offset * totDim * totDim]));
5938:         }
5939:         if (hasBdJac) {
5940:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, X_tShift, elemMatCoh));
5941:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[2], 2, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[2], &a[2][offset * totDimAux[2]], t, X_tShift, &elemMatCoh[offset * totDim * totDim]));
5942:         }
5943:         if (hasBdPrec) {
5944:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, X_tShift, elemMatCohP));
5945:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[2], 2, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[2], &a[2][offset * totDimAux[2]], t, X_tShift, &elemMatCohP[offset * totDim * totDim]));
5946:         }
5947:       }
5948:       PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &remGeom));
5949:       PetscCall(PetscFEGeomRestoreChunk(geom, 0, offset, &chunkGeom));
5950:     }
5951:     /* Insert values into matrix */
5952:     for (c = cS; c < cE; ++c) {
5953:       const PetscInt cell = cells ? cells[c] : c;
5954:       const PetscInt cind = c - cS, coff = cind * totDim * totDim;
5955:       PetscInt       i, j;

5957:       /* Scale element values */
5958:       if (locS[0]) {
5959:         PetscInt  Nb, soff = cind * totDimScale, off = 0;
5960:         PetscBool cohesive;

5962:         for (fieldI = 0; fieldI < Nf; ++fieldI) {
5963:           PetscCall(PetscDSGetFieldSize(ds, fieldI, &Nb));
5964:           PetscCall(PetscDSGetCohesive(ds, fieldI, &cohesive));

5966:           if (fieldI == key[2].field) {
5967:             PetscCheck(cohesive, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Scaling should not happen for face fields");
5968:             // No cohesive scaling field is currently input
5969:             for (i = 0; i < Nb; ++i)
5970:               for (j = 0; j < totDim; ++j) elemMatCoh[coff + (off + i) * totDim + j] += s[0][soff + i] * elemMatNeg[coff + (off + i) * totDim + j] + s[1][soff + i] * elemMatPos[coff + (off + i) * totDim + j];
5971:             off += Nb;
5972:           } else {
5973:             const PetscInt N = cohesive ? Nb : Nb * 2;

5975:             for (i = 0; i < N; ++i)
5976:               for (j = 0; j < totDim; ++j) elemMatCoh[coff + (off + i) * totDim + j] += elemMatNeg[coff + (off + i) * totDim + j] + elemMatPos[coff + (off + i) * totDim + j];
5977:             off += N;
5978:           }
5979:         }
5980:       } else {
5981:         for (i = 0; i < totDim * totDim; ++i) elemMatCoh[coff + i] += elemMatNeg[coff + i] + elemMatPos[coff + i];
5982:       }
5983:       if (hasBdPrec) {
5984:         if (hasBdJac) {
5985:           if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCoh[cind * totDim * totDim]));
5986:           PetscCall(DMPlexMatSetClosure(plex, section, globalSection, Jac, cell, &elemMatCoh[cind * totDim * totDim], ADD_VALUES));
5987:         }
5988:         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCohP[cind * totDim * totDim]));
5989:         PetscCall(DMPlexMatSetClosure(plex, section, globalSection, JacP, cell, &elemMatCohP[cind * totDim * totDim], ADD_VALUES));
5990:       } else if (hasBdJac) {
5991:         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCoh[cind * totDim * totDim]));
5992:         PetscCall(DMPlexMatSetClosure(plex, section, globalSection, JacP, cell, &elemMatCoh[cind * totDim * totDim], ADD_VALUES));
5993:       }
5994:     }
5995:   }
5996:   PetscCall(DMPlexRestoreCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
5997:   PetscCall(DMPlexRestoreHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
5998:   PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNeg));
5999:   PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPos));
6000:   PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCoh));
6001:   PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNegP));
6002:   PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPosP));
6003:   PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCohP));
6004:   PetscCall(PetscFree(faces));
6005:   PetscCall(ISDestroy(&chunkIS));
6006:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
6007:   if (maxDegree <= 1) {
6008:     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
6009:     PetscCall(PetscQuadratureDestroy(&affineQuad));
6010:   } else {
6011:     PetscInt f;
6012:     for (f = 0; f < Nf; ++f) {
6013:       if (geoms) PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
6014:       if (quads) PetscCall(PetscQuadratureDestroy(&quads[f]));
6015:     }
6016:     PetscCall(PetscFree2(quads, geoms));
6017:   }
6018:   if (dmAux[2]) PetscCall(DMDestroy(&plexA));
6019:   PetscCall(DMDestroy(&plex));
6020:   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
6021:   PetscFunctionReturn(PETSC_SUCCESS);
6022: }

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

6027:   Input Parameters:
6028: + dm     - The mesh
6029: . key    - The PetscWeakFormKey indcating where integration should happen
6030: . cellIS - The cells to integrate over
6031: . t      - The time
6032: . X_tShift - The multiplier for the Jacobian with repsect to X_t
6033: . X      - Local solution vector
6034: . X_t    - Time-derivative of the local solution vector
6035: . Y      - Local input vector
6036: - user   - the user context

6038:   Output Parameter:
6039: . Z - Local output vector

6041:   Note:
6042:   We form the residual one batch of elements at a time. This allows us to offload work onto an accelerator,
6043:   like a GPU, or vectorize on a multicore machine.
6044: */
6045: PetscErrorCode DMPlexComputeJacobian_Action_Internal(DM dm, PetscFormKey key, IS cellIS, PetscReal t, PetscReal X_tShift, Vec X, Vec X_t, Vec Y, Vec Z, void *user)
6046: {
6047:   DM_Plex        *mesh  = (DM_Plex *)dm->data;
6048:   const char     *name  = "Jacobian";
6049:   DM              dmAux = NULL, plex, plexAux = NULL;
6050:   DMEnclosureType encAux;
6051:   Vec             A;
6052:   DMField         coordField;
6053:   PetscDS         prob, probAux = NULL;
6054:   PetscQuadrature quad;
6055:   PetscSection    section, globalSection, sectionAux;
6056:   PetscScalar    *elemMat, *elemMatD, *u, *u_t, *a = NULL, *y, *z;
6057:   const PetscInt *cells;
6058:   PetscInt        Nf, fieldI, fieldJ;
6059:   PetscInt        totDim, totDimAux = 0, cStart, cEnd, numCells, c;
6060:   PetscBool       hasDyn;

6062:   PetscFunctionBegin;
6063:   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
6064:   PetscCall(DMConvert(dm, DMPLEX, &plex));
6065:   if (!cellIS) {
6066:     PetscInt depth;

6068:     PetscCall(DMPlexGetDepth(plex, &depth));
6069:     PetscCall(DMGetStratumIS(plex, "dim", depth, &cellIS));
6070:     if (!cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, &cellIS));
6071:   } else {
6072:     PetscCall(PetscObjectReference((PetscObject)cellIS));
6073:   }
6074:   PetscCall(ISGetLocalSize(cellIS, &numCells));
6075:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
6076:   PetscCall(DMGetLocalSection(dm, &section));
6077:   PetscCall(DMGetGlobalSection(dm, &globalSection));
6078:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL));
6079:   PetscCall(PetscDSGetNumFields(prob, &Nf));
6080:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
6081:   PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
6082:   hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
6083:   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &A));
6084:   if (A) {
6085:     PetscCall(VecGetDM(A, &dmAux));
6086:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
6087:     PetscCall(DMConvert(dmAux, DMPLEX, &plexAux));
6088:     PetscCall(DMGetLocalSection(plexAux, &sectionAux));
6089:     PetscCall(DMGetDS(dmAux, &probAux));
6090:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
6091:   }
6092:   PetscCall(VecSet(Z, 0.0));
6093:   PetscCall(PetscMalloc6(numCells * totDim, &u, X_t ? numCells * totDim : 0, &u_t, numCells * totDim * totDim, &elemMat, hasDyn ? numCells * totDim * totDim : 0, &elemMatD, numCells * totDim, &y, totDim, &z));
6094:   if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
6095:   PetscCall(DMGetCoordinateField(dm, &coordField));
6096:   for (c = cStart; c < cEnd; ++c) {
6097:     const PetscInt cell = cells ? cells[c] : c;
6098:     const PetscInt cind = c - cStart;
6099:     PetscScalar   *x = NULL, *x_t = NULL;
6100:     PetscInt       i;

6102:     PetscCall(DMPlexVecGetClosure(plex, section, X, cell, NULL, &x));
6103:     for (i = 0; i < totDim; ++i) u[cind * totDim + i] = x[i];
6104:     PetscCall(DMPlexVecRestoreClosure(plex, section, X, cell, NULL, &x));
6105:     if (X_t) {
6106:       PetscCall(DMPlexVecGetClosure(plex, section, X_t, cell, NULL, &x_t));
6107:       for (i = 0; i < totDim; ++i) u_t[cind * totDim + i] = x_t[i];
6108:       PetscCall(DMPlexVecRestoreClosure(plex, section, X_t, cell, NULL, &x_t));
6109:     }
6110:     if (dmAux) {
6111:       PetscInt subcell;
6112:       PetscCall(DMGetEnclosurePoint(dmAux, dm, encAux, cell, &subcell));
6113:       PetscCall(DMPlexVecGetClosure(plexAux, sectionAux, A, subcell, NULL, &x));
6114:       for (i = 0; i < totDimAux; ++i) a[cind * totDimAux + i] = x[i];
6115:       PetscCall(DMPlexVecRestoreClosure(plexAux, sectionAux, A, subcell, NULL, &x));
6116:     }
6117:     PetscCall(DMPlexVecGetClosure(plex, section, Y, cell, NULL, &x));
6118:     for (i = 0; i < totDim; ++i) y[cind * totDim + i] = x[i];
6119:     PetscCall(DMPlexVecRestoreClosure(plex, section, Y, cell, NULL, &x));
6120:   }
6121:   PetscCall(PetscArrayzero(elemMat, numCells * totDim * totDim));
6122:   if (hasDyn) PetscCall(PetscArrayzero(elemMatD, numCells * totDim * totDim));
6123:   for (fieldI = 0; fieldI < Nf; ++fieldI) {
6124:     PetscFE  fe;
6125:     PetscInt Nb;
6126:     /* Conforming batches */
6127:     PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
6128:     /* Remainder */
6129:     PetscInt        Nr, offset, Nq;
6130:     PetscQuadrature qGeom = NULL;
6131:     PetscInt        maxDegree;
6132:     PetscFEGeom    *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL;

6134:     PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
6135:     PetscCall(PetscFEGetQuadrature(fe, &quad));
6136:     PetscCall(PetscFEGetDimension(fe, &Nb));
6137:     PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
6138:     PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
6139:     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
6140:     if (!qGeom) {
6141:       PetscCall(PetscFEGetQuadrature(fe, &qGeom));
6142:       PetscCall(PetscObjectReference((PetscObject)qGeom));
6143:     }
6144:     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
6145:     PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
6146:     blockSize = Nb;
6147:     batchSize = numBlocks * blockSize;
6148:     PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
6149:     numChunks = numCells / (numBatches * batchSize);
6150:     Ne        = numChunks * numBatches * batchSize;
6151:     Nr        = numCells % (numBatches * batchSize);
6152:     offset    = numCells - Nr;
6153:     PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
6154:     PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &remGeom));
6155:     for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
6156:       key.field = fieldI * Nf + fieldJ;
6157:       PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat));
6158:       PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Nr, remGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, &a[offset * totDimAux], t, X_tShift, &elemMat[offset * totDim * totDim]));
6159:       if (hasDyn) {
6160:         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatD));
6161:         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Nr, remGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, &a[offset * totDimAux], t, X_tShift, &elemMatD[offset * totDim * totDim]));
6162:       }
6163:     }
6164:     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &remGeom));
6165:     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, 0, offset, &chunkGeom));
6166:     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
6167:     PetscCall(PetscQuadratureDestroy(&qGeom));
6168:   }
6169:   if (hasDyn) {
6170:     for (c = 0; c < numCells * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
6171:   }
6172:   for (c = cStart; c < cEnd; ++c) {
6173:     const PetscInt     cell = cells ? cells[c] : c;
6174:     const PetscInt     cind = c - cStart;
6175:     const PetscBLASInt M = totDim, one = 1;
6176:     const PetscScalar  a = 1.0, b = 0.0;

6178:     PetscCallBLAS("BLASgemv", BLASgemv_("N", &M, &M, &a, &elemMat[cind * totDim * totDim], &M, &y[cind * totDim], &one, &b, z, &one));
6179:     if (mesh->printFEM > 1) {
6180:       PetscCall(DMPrintCellMatrix(c, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
6181:       PetscCall(DMPrintCellVector(c, "Y", totDim, &y[cind * totDim]));
6182:       PetscCall(DMPrintCellVector(c, "Z", totDim, z));
6183:     }
6184:     PetscCall(DMPlexVecSetClosure(dm, section, Z, cell, z, ADD_VALUES));
6185:   }
6186:   PetscCall(PetscFree6(u, u_t, elemMat, elemMatD, y, z));
6187:   if (mesh->printFEM) {
6188:     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)Z), "Z:\n"));
6189:     PetscCall(VecView(Z, NULL));
6190:   }
6191:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
6192:   PetscCall(PetscFree(a));
6193:   PetscCall(ISDestroy(&cellIS));
6194:   PetscCall(DMDestroy(&plexAux));
6195:   PetscCall(DMDestroy(&plex));
6196:   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
6197:   PetscFunctionReturn(PETSC_SUCCESS);
6198: }