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: [](chapter_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: [](chapter_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 on dm

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: [](chapter_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;

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

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

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

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

242:   Collective on dm

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

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

254:   Level: advanced

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

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

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

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

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

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

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

324:   Level: advanced

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

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

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

342:   Input Parameters:
343: . dm - the `DMPLEX` object

345:   Output Parameters:
346: . height - the maximum projection height

348:   Level: intermediate

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

672:   Output Parameters:
673: . lv - A local vector with values in the local basis

675:   Level: developer

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

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

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

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

698:   Output Parameters:
699: . lv - A local vector with values in the global basis

701:   Level: developer

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

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

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

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

727:   Level: developer

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

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

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

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

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

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

779:   Level: developer

781: .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMLabel`, `DMPlexInsertBoundaryValuesEssentialField()`, `DMPlexInsertBoundaryValuesEssentialBdField()`, `DMAddBoundary()`
782: @*/
783: 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)
784: {
785:   PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal x[], PetscInt, PetscScalar *u, void *ctx);
786:   void   **ctxs;
787:   PetscInt numFields;

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

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

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

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

818:   Level: developer

820: .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexInsertBoundaryValuesEssential()`, `DMPlexInsertBoundaryValuesEssentialBdField()`, `DMAddBoundary()`
821: @*/
822: 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)
823: {
824:   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[]);
825:   void   **ctxs;
826:   PetscInt numFields;

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

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

841:   Collective on dm

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

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

859:   Level: developer

861: .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMProjectBdFieldLabelLocal()`, `DMPlexInsertBoundaryValuesEssential()`, `DMPlexInsertBoundaryValuesEssentialField()`, `DMAddBoundary()`
862: @*/
863: 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)
864: {
865:   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[]);
866:   void   **ctxs;
867:   PetscInt numFields;

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

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

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

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

900:   Level: developer

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

905: .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexInsertBoundaryValuesEssential()`, `DMPlexInsertBoundaryValuesEssentialField()`, `DMAddBoundary()`
906: @*/
907: 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)
908: {
909:   PetscDS            prob;
910:   PetscSF            sf;
911:   DM                 dmFace, dmCell, dmGrad;
912:   const PetscScalar *facegeom, *cellgeom = NULL, *grad;
913:   const PetscInt    *leaves;
914:   PetscScalar       *x, *fx;
915:   PetscInt           dim, nleaves, loc, fStart, fEnd, pdim, i;
916:   PetscErrorCode     ierru = PETSC_SUCCESS;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1128:   Not Collective

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

1138:   Output Parameters:
1139: . locX - Solution updated with boundary values

1141:   Level: intermediate

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

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

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

1168:   Output Parameters:
1169: . locX_t - Solution updated with boundary values

1171:   Level: developer

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

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

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

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

1204:   Collective on dm

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

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

1216:   Level: developer

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

1235:   PetscFunctionBegin;
1236:   PetscCall(DMGetDimension(dm, &dim));
1237:   PetscCall(DMGetCoordinateDim(dm, &coordDim));
1238:   fegeom.dimEmbed = coordDim;
1239:   PetscCall(DMGetLocalSection(dm, &section));
1240:   PetscCall(PetscSectionGetNumFields(section, &numFields));
1241:   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
1242:   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
1243:   PetscCall(DMHasBasisTransform(dm, &transform));
1244:   for (field = 0; field < numFields; ++field) {
1245:     PetscObject  obj;
1246:     PetscClassId id;
1247:     PetscInt     Nc;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1612: /*@C
1613:   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.

1615:   Collective on dm

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

1624:   Output Parameter:
1625: . D - A Vec which holds the difference ||u - u_h||_2 for each cell

1627:   Level: developer

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

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

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

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

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

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

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

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

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

1737: /*@
1738:   DMPlexComputeClementInterpolant - This function computes the L2 projection of the cellwise values of a function u onto P1, and stores it in a Vec.

1740:   Collective on dm

1742:   Input Parameters:
1743: + dm - The `DM`
1744: - locX  - The coefficient vector u_h

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

1749:   Level: developer

1751:   Note:
1752:   $ 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

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

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

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

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

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

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

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

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

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

1869: /*@
1870:   DMPlexComputeGradientClementInterpolant - This function computes the L2 projection of the cellwise gradient of a function u onto P1, and stores it in a Vec.

1872:   Collective on dm

1874:   Input Parameters:
1875: + dm - The `DM`
1876: - locX  - The coefficient vector u_h

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

1881:   Level: developer

1883:   Note:
1884:   $\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

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

1900:   PetscFunctionBegin;
1901:   PetscCall(PetscCitationsRegister(ClementCitation, &Clementcite));
1902:   PetscCall(VecGetDM(locC, &dmC));
1903:   PetscCall(VecSet(locC, 0.0));
1904:   PetscCall(DMGetDimension(dm, &dim));
1905:   PetscCall(DMGetCoordinateDim(dm, &coordDim));
1906:   fegeom.dimEmbed = coordDim;
1907:   PetscCall(DMGetNumFields(dm, &numFields));
1908:   PetscCheck(numFields, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fields is zero!");
1909:   for (field = 0; field < numFields; ++field) {
1910:     PetscObject  obj;
1911:     PetscClassId id;
1912:     PetscInt     Nc;

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

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

1924:       PetscCall(PetscFVGetQuadrature(fv, &quad));
1925:       PetscCall(PetscFVGetNumComponents(fv, &Nc));
1926:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1927:     numComponents += Nc;
1928:   }
1929:   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
1930:   PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
1931:   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));
1932:   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1933:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
1934:   for (v = vStart; v < vEnd; ++v) {
1935:     PetscScalar volsum = 0.0;
1936:     PetscInt   *star   = NULL;
1937:     PetscInt    starSize, st, d, fc;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2173:           PetscCall(DMPlexPointLocalRead(dmGrad, c, lgrad, &u_x));
2174:           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);
2175:           cintegral[c * Nf + f] += PetscRealPart(lint) * cgeomFVM[c].volume;
2176:         }
2177:       }
2178:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
2179:   }
2180:   /* Cleanup data arrays */
2181:   if (useFVM) {
2182:     PetscCall(VecRestoreArrayRead(locGrad, &lgrad));
2183:     PetscCall(VecRestoreArrayRead(cellGeometryFVM, (const PetscScalar **)&cgeomFVM));
2184:     PetscCall(DMRestoreLocalVector(dmGrad, &locGrad));
2185:     PetscCall(VecDestroy(&faceGeometryFVM));
2186:     PetscCall(VecDestroy(&cellGeometryFVM));
2187:     PetscCall(DMDestroy(&dmGrad));
2188:   }
2189:   if (dmAux) PetscCall(PetscFree(a));
2190:   PetscCall(PetscFree(u));
2191:   /* Cleanup */
2192:   if (affineQuad) PetscCall(PetscFEGeomDestroy(&cgeomFEM));
2193:   PetscCall(PetscQuadratureDestroy(&affineQuad));
2194:   PetscCall(ISDestroy(&cellIS));
2195:   PetscCall(DMRestoreLocalVector(dm, &locX));
2196:   PetscFunctionReturn(PETSC_SUCCESS);
2197: }

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

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

2207:   Output Parameter:
2208: . integral - Integral for each field

2210:   Level: developer

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

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

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

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

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

2257:   Output Parameter:
2258: . integral - Cellwise integrals for each field

2260:   Level: developer

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

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

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

2303: 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)
2304: {
2305:   DM                 plex = NULL, plexA = NULL;
2306:   DMEnclosureType    encAux;
2307:   PetscDS            prob, probAux       = NULL;
2308:   PetscSection       section, sectionAux = NULL;
2309:   Vec                locA = NULL;
2310:   DMField            coordField;
2311:   PetscInt           Nf, totDim, *uOff, *uOff_x;
2312:   PetscInt           NfAux = 0, totDimAux = 0, *aOff = NULL;
2313:   PetscScalar       *u, *a = NULL;
2314:   const PetscScalar *constants;
2315:   PetscInt           numConstants, f;

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

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

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

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

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

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

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

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

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

2443:   Output Parameter:
2444: . integral - Integral for each field

2446:   Level: developer

2448: .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexComputeIntegralFEM()`, `DMPlexComputeBdResidualFEM()`
2449: @*/
2450: 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)
2451: {
2452:   Vec          locX;
2453:   PetscSection section;
2454:   DMLabel      depthLabel;
2455:   IS           facetIS;
2456:   PetscInt     dim, Nf, f, v;

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

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

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

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

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

2516:   Output Parameter:
2517: . In  - The interpolation matrix

2519:   Level: developer

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2753:   Output Parameter:
2754: . In  - The interpolation matrix

2756:   Level: developer

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2925:   Output Parameter:
2926: . mass  - The mass matrix

2928:   Level: developer

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

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

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

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

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

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

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

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

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

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

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

3104:         if (id == PETSCFE_CLASSID) {
3105:           PetscFE fe = (PetscFE)obj;

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

3152: /*@
3153:   DMPlexComputeInjectorFEM - Compute a mapping from coarse unknowns to fine unknowns

3155:   Input Parameters:
3156: + dmc  - The coarse mesh
3157: - dmf  - The fine mesh
3158: - user - The user context

3160:   Output Parameter:
3161: . sc   - The mapping

3163:   Level: developer

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

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

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

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

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

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

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

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

3291:   PetscCall(DMGetGlobalVector(dmf, &fv));
3292:   PetscCall(DMGetGlobalVector(dmc, &cv));
3293:   PetscCall(VecGetOwnershipRange(cv, &startC, &endC));
3294:   PetscCall(PetscSectionGetConstrainedStorageSize(cglobalSection, &m));
3295:   PetscCall(PetscMalloc2(cTotDim, &cellCIndices, fTotDim, &cellFIndices));
3296:   PetscCall(PetscMalloc1(m, &cindices));
3297:   PetscCall(PetscMalloc1(m, &findices));
3298:   for (d = 0; d < m; ++d) cindices[d] = findices[d] = -1;
3299:   for (c = cStart; c < cEnd; ++c) {
3300:     PetscCall(DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, c, cellCIndices, cellFIndices));
3301:     for (d = 0; d < cTotDim; ++d) {
3302:       if ((cellCIndices[d] < startC) || (cellCIndices[d] >= endC)) continue;
3303:       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]]);
3304:       cindices[cellCIndices[d] - startC] = cellCIndices[d];
3305:       findices[cellCIndices[d] - startC] = cellFIndices[cmap[d]];
3306:     }
3307:   }
3308:   PetscCall(PetscFree(cmap));
3309:   PetscCall(PetscFree2(cellCIndices, cellFIndices));

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

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

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

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

3337:   Level: developer

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

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

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

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

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

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

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

3423:   Level: developer

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

3436: /*
3437:   Get the auxiliary field vectors for the negative side (s = 0) and positive side (s = 1) of the interfaace
3438: */
3439: static PetscErrorCode DMPlexGetHybridAuxFields(DM dm, DM dmAux[], PetscDS dsAux[], IS cellIS, Vec locA[], PetscScalar *a[])
3440: {
3441:   DM              plexA[2];
3442:   DMEnclosureType encAux[2];
3443:   PetscSection    sectionAux[2];
3444:   const PetscInt *cells;
3445:   PetscInt        cStart, cEnd, numCells, c, s, totDimAux[2];

3447:   PetscFunctionBegin;
3449:   if (!locA[0] || !locA[1]) PetscFunctionReturn(PETSC_SUCCESS);
3453:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
3454:   numCells = cEnd - cStart;
3455:   for (s = 0; s < 2; ++s) {
3459:     PetscCall(DMPlexConvertPlex(dmAux[s], &plexA[s], PETSC_FALSE));
3460:     PetscCall(DMGetEnclosureRelation(dmAux[s], dm, &encAux[s]));
3461:     PetscCall(DMGetLocalSection(dmAux[s], &sectionAux[s]));
3462:     PetscCall(PetscDSGetTotalDimension(dsAux[s], &totDimAux[s]));
3463:     PetscCall(DMGetWorkArray(dmAux[s], numCells * totDimAux[s], MPIU_SCALAR, &a[s]));
3464:   }
3465:   for (c = cStart; c < cEnd; ++c) {
3466:     const PetscInt  cell = cells ? cells[c] : c;
3467:     const PetscInt  cind = c - cStart;
3468:     const PetscInt *cone, *ornt;

3470:     PetscCall(DMPlexGetCone(dm, cell, &cone));
3471:     PetscCall(DMPlexGetConeOrientation(dm, cell, &ornt));
3472:     for (s = 0; s < 2; ++s) {
3473:       const PetscInt *support;
3474:       PetscScalar    *x = NULL, *al = a[s];
3475:       const PetscInt  tdA = totDimAux[s];
3476:       PetscInt        ssize, scell;
3477:       PetscInt        subface, Na, i;

3479:       PetscCall(DMPlexGetSupport(dm, cone[s], &support));
3480:       PetscCall(DMPlexGetSupportSize(dm, cone[s], &ssize));
3481:       PetscCheck(ssize == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " from cell %" PetscInt_FMT " has support size %" PetscInt_FMT " != 2", cone[s], cell, ssize);
3482:       if (support[0] == cell) scell = support[1];
3483:       else if (support[1] == cell) scell = support[0];
3484:       else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[s], cell);

3486:       PetscCall(DMGetEnclosurePoint(plexA[s], dm, encAux[s], scell, &subface));
3487:       PetscCall(DMPlexVecGetClosure(plexA[s], sectionAux[s], locA[s], subface, &Na, &x));
3488:       for (i = 0; i < Na; ++i) al[cind * tdA + i] = x[i];
3489:       PetscCall(DMPlexVecRestoreClosure(plexA[s], sectionAux[s], locA[s], subface, &Na, &x));
3490:     }
3491:   }
3492:   for (s = 0; s < 2; ++s) PetscCall(DMDestroy(&plexA[s]));
3493:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
3494:   PetscFunctionReturn(PETSC_SUCCESS);
3495: }

3497: static PetscErrorCode DMPlexRestoreHybridAuxFields(DM dmAux[], PetscDS dsAux[], IS cellIS, Vec locA[], PetscScalar *a[])
3498: {
3499:   PetscFunctionBegin;
3500:   if (!locA[0] || !locA[1]) PetscFunctionReturn(PETSC_SUCCESS);
3501:   PetscCall(DMRestoreWorkArray(dmAux[0], 0, MPIU_SCALAR, &a[0]));
3502:   PetscCall(DMRestoreWorkArray(dmAux[1], 0, MPIU_SCALAR, &a[1]));
3503:   PetscFunctionReturn(PETSC_SUCCESS);
3504: }

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

3509:   Input Parameters:
3510: + dm     - The `DM`
3511: . fStart - The first face to include
3512: . fEnd   - The first face to exclude
3513: . locX   - A local vector with the solution fields
3514: . locX_t - A local vector with solution field time derivatives, or NULL
3515: . faceGeometry - A local vector with face geometry
3516: . cellGeometry - A local vector with cell geometry
3517: - locaGrad - A local vector with field gradients, or NULL

3519:   Output Parameters:
3520: + Nface - The number of faces with field values
3521: . uL - The field values at the left side of the face
3522: - uR - The field values at the right side of the face

3524:   Level: developer

3526: .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellFields()`
3527: @*/
3528: 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)
3529: {
3530:   DM                 dmFace, dmCell, dmGrad = NULL;
3531:   PetscSection       section;
3532:   PetscDS            prob;
3533:   DMLabel            ghostLabel;
3534:   const PetscScalar *facegeom, *cellgeom, *x, *lgrad;
3535:   PetscBool         *isFE;
3536:   PetscInt           dim, Nf, f, Nc, numFaces = fEnd - fStart, iface, face;

3538:   PetscFunctionBegin;
3547:   PetscCall(DMGetDimension(dm, &dim));
3548:   PetscCall(DMGetDS(dm, &prob));
3549:   PetscCall(DMGetLocalSection(dm, &section));
3550:   PetscCall(PetscDSGetNumFields(prob, &Nf));
3551:   PetscCall(PetscDSGetTotalComponents(prob, &Nc));
3552:   PetscCall(PetscMalloc1(Nf, &isFE));
3553:   for (f = 0; f < Nf; ++f) {
3554:     PetscObject  obj;
3555:     PetscClassId id;

3557:     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
3558:     PetscCall(PetscObjectGetClassId(obj, &id));
3559:     if (id == PETSCFE_CLASSID) {
3560:       isFE[f] = PETSC_TRUE;
3561:     } else if (id == PETSCFV_CLASSID) {
3562:       isFE[f] = PETSC_FALSE;
3563:     } else {
3564:       isFE[f] = PETSC_FALSE;
3565:     }
3566:   }
3567:   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
3568:   PetscCall(VecGetArrayRead(locX, &x));
3569:   PetscCall(VecGetDM(faceGeometry, &dmFace));
3570:   PetscCall(VecGetArrayRead(faceGeometry, &facegeom));
3571:   PetscCall(VecGetDM(cellGeometry, &dmCell));
3572:   PetscCall(VecGetArrayRead(cellGeometry, &cellgeom));
3573:   if (locGrad) {
3574:     PetscCall(VecGetDM(locGrad, &dmGrad));
3575:     PetscCall(VecGetArrayRead(locGrad, &lgrad));
3576:   }
3577:   PetscCall(DMGetWorkArray(dm, numFaces * Nc, MPIU_SCALAR, uL));
3578:   PetscCall(DMGetWorkArray(dm, numFaces * Nc, MPIU_SCALAR, uR));
3579:   /* Right now just eat the extra work for FE (could make a cell loop) */
3580:   for (face = fStart, iface = 0; face < fEnd; ++face) {
3581:     const PetscInt  *cells;
3582:     PetscFVFaceGeom *fg;
3583:     PetscFVCellGeom *cgL, *cgR;
3584:     PetscScalar     *xL, *xR, *gL, *gR;
3585:     PetscScalar     *uLl = *uL, *uRl = *uR;
3586:     PetscInt         ghost, nsupp, nchild;

3588:     PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
3589:     PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
3590:     PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
3591:     if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
3592:     PetscCall(DMPlexPointLocalRead(dmFace, face, facegeom, &fg));
3593:     PetscCall(DMPlexGetSupport(dm, face, &cells));
3594:     PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL));
3595:     PetscCall(DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR));
3596:     for (f = 0; f < Nf; ++f) {
3597:       PetscInt off;

3599:       PetscCall(PetscDSGetComponentOffset(prob, f, &off));
3600:       if (isFE[f]) {
3601:         const PetscInt *cone;
3602:         PetscInt        comp, coneSizeL, coneSizeR, faceLocL, faceLocR, ldof, rdof, d;

3604:         xL = xR = NULL;
3605:         PetscCall(PetscSectionGetFieldComponents(section, f, &comp));
3606:         PetscCall(DMPlexVecGetClosure(dm, section, locX, cells[0], &ldof, (PetscScalar **)&xL));
3607:         PetscCall(DMPlexVecGetClosure(dm, section, locX, cells[1], &rdof, (PetscScalar **)&xR));
3608:         PetscCall(DMPlexGetCone(dm, cells[0], &cone));
3609:         PetscCall(DMPlexGetConeSize(dm, cells[0], &coneSizeL));
3610:         for (faceLocL = 0; faceLocL < coneSizeL; ++faceLocL)
3611:           if (cone[faceLocL] == face) break;
3612:         PetscCall(DMPlexGetCone(dm, cells[1], &cone));
3613:         PetscCall(DMPlexGetConeSize(dm, cells[1], &coneSizeR));
3614:         for (faceLocR = 0; faceLocR < coneSizeR; ++faceLocR)
3615:           if (cone[faceLocR] == face) break;
3616:         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]);
3617:         /* Check that FEM field has values in the right cell (sometimes its an FV ghost cell) */
3618:         /* TODO: this is a hack that might not be right for nonconforming */
3619:         if (faceLocL < coneSizeL) {
3620:           PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocL, xL, &uLl[iface * Nc + off]));
3621:           if (rdof == ldof && faceLocR < coneSizeR) PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface * Nc + off]));
3622:           else {
3623:             for (d = 0; d < comp; ++d) uRl[iface * Nc + off + d] = uLl[iface * Nc + off + d];
3624:           }
3625:         } else {
3626:           PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface * Nc + off]));
3627:           PetscCall(PetscSectionGetFieldComponents(section, f, &comp));
3628:           for (d = 0; d < comp; ++d) uLl[iface * Nc + off + d] = uRl[iface * Nc + off + d];
3629:         }
3630:         PetscCall(DMPlexVecRestoreClosure(dm, section, locX, cells[0], &ldof, (PetscScalar **)&xL));
3631:         PetscCall(DMPlexVecRestoreClosure(dm, section, locX, cells[1], &rdof, (PetscScalar **)&xR));
3632:       } else {
3633:         PetscFV  fv;
3634:         PetscInt numComp, c;

3636:         PetscCall(PetscDSGetDiscretization(prob, f, (PetscObject *)&fv));
3637:         PetscCall(PetscFVGetNumComponents(fv, &numComp));
3638:         PetscCall(DMPlexPointLocalFieldRead(dm, cells[0], f, x, &xL));
3639:         PetscCall(DMPlexPointLocalFieldRead(dm, cells[1], f, x, &xR));
3640:         if (dmGrad) {
3641:           PetscReal dxL[3], dxR[3];

3643:           PetscCall(DMPlexPointLocalRead(dmGrad, cells[0], lgrad, &gL));
3644:           PetscCall(DMPlexPointLocalRead(dmGrad, cells[1], lgrad, &gR));
3645:           DMPlex_WaxpyD_Internal(dim, -1, cgL->centroid, fg->centroid, dxL);
3646:           DMPlex_WaxpyD_Internal(dim, -1, cgR->centroid, fg->centroid, dxR);
3647:           for (c = 0; c < numComp; ++c) {
3648:             uLl[iface * Nc + off + c] = xL[c] + DMPlex_DotD_Internal(dim, &gL[c * dim], dxL);
3649:             uRl[iface * Nc + off + c] = xR[c] + DMPlex_DotD_Internal(dim, &gR[c * dim], dxR);
3650:           }
3651:         } else {
3652:           for (c = 0; c < numComp; ++c) {
3653:             uLl[iface * Nc + off + c] = xL[c];
3654:             uRl[iface * Nc + off + c] = xR[c];
3655:           }
3656:         }
3657:       }
3658:     }
3659:     ++iface;
3660:   }
3661:   *Nface = iface;
3662:   PetscCall(VecRestoreArrayRead(locX, &x));
3663:   PetscCall(VecRestoreArrayRead(faceGeometry, &facegeom));
3664:   PetscCall(VecRestoreArrayRead(cellGeometry, &cellgeom));
3665:   if (locGrad) PetscCall(VecRestoreArrayRead(locGrad, &lgrad));
3666:   PetscCall(PetscFree(isFE));
3667:   PetscFunctionReturn(PETSC_SUCCESS);
3668: }

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

3673:   Input Parameters:
3674: + dm     - The `DM`
3675: . fStart - The first face to include
3676: . fEnd   - The first face to exclude
3677: . locX   - A local vector with the solution fields
3678: . locX_t - A local vector with solution field time derivatives, or NULL
3679: . faceGeometry - A local vector with face geometry
3680: . cellGeometry - A local vector with cell geometry
3681: - locaGrad - A local vector with field gradients, or NULL

3683:   Output Parameters:
3684: + Nface - The number of faces with field values
3685: . uL - The field values at the left side of the face
3686: - uR - The field values at the right side of the face

3688:   Level: developer

3690: .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
3691: @*/
3692: 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)
3693: {
3694:   PetscFunctionBegin;
3695:   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uL));
3696:   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uR));
3697:   PetscFunctionReturn(PETSC_SUCCESS);
3698: }

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

3703:   Input Parameters:
3704: + dm     - The `DM`
3705: . fStart - The first face to include
3706: . fEnd   - The first face to exclude
3707: . faceGeometry - A local vector with face geometry
3708: - cellGeometry - A local vector with cell geometry

3710:   Output Parameters:
3711: + Nface - The number of faces with field values
3712: . fgeom - The extract the face centroid and normal
3713: - vol   - The cell volume

3715:   Level: developer

3717: .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellFields()`
3718: @*/
3719: PetscErrorCode DMPlexGetFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom **fgeom, PetscReal **vol)
3720: {
3721:   DM                 dmFace, dmCell;
3722:   DMLabel            ghostLabel;
3723:   const PetscScalar *facegeom, *cellgeom;
3724:   PetscInt           dim, numFaces = fEnd - fStart, iface, face;

3726:   PetscFunctionBegin;
3732:   PetscCall(DMGetDimension(dm, &dim));
3733:   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
3734:   PetscCall(VecGetDM(faceGeometry, &dmFace));
3735:   PetscCall(VecGetArrayRead(faceGeometry, &facegeom));
3736:   PetscCall(VecGetDM(cellGeometry, &dmCell));
3737:   PetscCall(VecGetArrayRead(cellGeometry, &cellgeom));
3738:   PetscCall(PetscMalloc1(numFaces, fgeom));
3739:   PetscCall(DMGetWorkArray(dm, numFaces * 2, MPIU_SCALAR, vol));
3740:   for (face = fStart, iface = 0; face < fEnd; ++face) {
3741:     const PetscInt  *cells;
3742:     PetscFVFaceGeom *fg;
3743:     PetscFVCellGeom *cgL, *cgR;
3744:     PetscFVFaceGeom *fgeoml = *fgeom;
3745:     PetscReal       *voll   = *vol;
3746:     PetscInt         ghost, d, nchild, nsupp;

3748:     PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
3749:     PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
3750:     PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
3751:     if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
3752:     PetscCall(DMPlexPointLocalRead(dmFace, face, facegeom, &fg));
3753:     PetscCall(DMPlexGetSupport(dm, face, &cells));
3754:     PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL));
3755:     PetscCall(DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR));
3756:     for (d = 0; d < dim; ++d) {
3757:       fgeoml[iface].centroid[d] = fg->centroid[d];
3758:       fgeoml[iface].normal[d]   = fg->normal[d];
3759:     }
3760:     voll[iface * 2 + 0] = cgL->volume;
3761:     voll[iface * 2 + 1] = cgR->volume;
3762:     ++iface;
3763:   }
3764:   *Nface = iface;
3765:   PetscCall(VecRestoreArrayRead(faceGeometry, &facegeom));
3766:   PetscCall(VecRestoreArrayRead(cellGeometry, &cellgeom));
3767:   PetscFunctionReturn(PETSC_SUCCESS);
3768: }

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

3773:   Input Parameters:
3774: + dm     - The `DM`
3775: . fStart - The first face to include
3776: . fEnd   - The first face to exclude
3777: . faceGeometry - A local vector with face geometry
3778: - cellGeometry - A local vector with cell geometry

3780:   Output Parameters:
3781: + Nface - The number of faces with field values
3782: . fgeom - The extract the face centroid and normal
3783: - vol   - The cell volume

3785:   Level: developer

3787: .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
3788: @*/
3789: PetscErrorCode DMPlexRestoreFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom **fgeom, PetscReal **vol)
3790: {
3791:   PetscFunctionBegin;
3792:   PetscCall(PetscFree(*fgeom));
3793:   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_REAL, vol));
3794:   PetscFunctionReturn(PETSC_SUCCESS);
3795: }

3797: PetscErrorCode DMSNESGetFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
3798: {
3799:   char           composeStr[33] = {0};
3800:   PetscObjectId  id;
3801:   PetscContainer container;

3803:   PetscFunctionBegin;
3804:   PetscCall(PetscObjectGetId((PetscObject)quad, &id));
3805:   PetscCall(PetscSNPrintf(composeStr, 32, "DMSNESGetFEGeom_%" PetscInt64_FMT "\n", id));
3806:   PetscCall(PetscObjectQuery((PetscObject)pointIS, composeStr, (PetscObject *)&container));
3807:   if (container) {
3808:     PetscCall(PetscContainerGetPointer(container, (void **)geom));
3809:   } else {
3810:     PetscCall(DMFieldCreateFEGeom(coordField, pointIS, quad, faceData, geom));
3811:     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &container));
3812:     PetscCall(PetscContainerSetPointer(container, (void *)*geom));
3813:     PetscCall(PetscContainerSetUserDestroy(container, PetscContainerUserDestroy_PetscFEGeom));
3814:     PetscCall(PetscObjectCompose((PetscObject)pointIS, composeStr, (PetscObject)container));
3815:     PetscCall(PetscContainerDestroy(&container));
3816:   }
3817:   PetscFunctionReturn(PETSC_SUCCESS);
3818: }

3820: PetscErrorCode DMSNESRestoreFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
3821: {
3822:   PetscFunctionBegin;
3823:   *geom = NULL;
3824:   PetscFunctionReturn(PETSC_SUCCESS);
3825: }

3827: PetscErrorCode DMPlexComputeResidual_Patch_Internal(DM dm, PetscSection section, IS cellIS, PetscReal t, Vec locX, Vec locX_t, Vec locF, void *user)
3828: {
3829:   DM_Plex        *mesh       = (DM_Plex *)dm->data;
3830:   const char     *name       = "Residual";
3831:   DM              dmAux      = NULL;
3832:   DMLabel         ghostLabel = NULL;
3833:   PetscDS         prob       = NULL;
3834:   PetscDS         probAux    = NULL;
3835:   PetscBool       useFEM     = PETSC_FALSE;
3836:   PetscBool       isImplicit = (locX_t || t == PETSC_MIN_REAL) ? PETSC_TRUE : PETSC_FALSE;
3837:   DMField         coordField = NULL;
3838:   Vec             locA;
3839:   PetscScalar    *u = NULL, *u_t, *a, *uL = NULL, *uR = NULL;
3840:   IS              chunkIS;
3841:   const PetscInt *cells;
3842:   PetscInt        cStart, cEnd, numCells;
3843:   PetscInt        Nf, f, totDim, totDimAux, numChunks, cellChunkSize, chunk, fStart, fEnd;
3844:   PetscInt        maxDegree = PETSC_MAX_INT;
3845:   PetscFormKey    key;
3846:   PetscQuadrature affineQuad = NULL, *quads = NULL;
3847:   PetscFEGeom    *affineGeom = NULL, **geoms = NULL;

3849:   PetscFunctionBegin;
3850:   PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
3851:   /* FEM+FVM */
3852:   /* 1: Get sizes from dm and dmAux */
3853:   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
3854:   PetscCall(DMGetDS(dm, &prob));
3855:   PetscCall(PetscDSGetNumFields(prob, &Nf));
3856:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
3857:   PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA));
3858:   if (locA) {
3859:     PetscCall(VecGetDM(locA, &dmAux));
3860:     PetscCall(DMGetDS(dmAux, &probAux));
3861:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
3862:   }
3863:   /* 2: Get geometric data */
3864:   for (f = 0; f < Nf; ++f) {
3865:     PetscObject  obj;
3866:     PetscClassId id;
3867:     PetscBool    fimp;

3869:     PetscCall(PetscDSGetImplicit(prob, f, &fimp));
3870:     if (isImplicit != fimp) continue;
3871:     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
3872:     PetscCall(PetscObjectGetClassId(obj, &id));
3873:     if (id == PETSCFE_CLASSID) useFEM = PETSC_TRUE;
3874:     PetscCheck(id != PETSCFV_CLASSID, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Use of FVM with PCPATCH not yet implemented");
3875:   }
3876:   if (useFEM) {
3877:     PetscCall(DMGetCoordinateField(dm, &coordField));
3878:     PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
3879:     if (maxDegree <= 1) {
3880:       PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad));
3881:       if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
3882:     } else {
3883:       PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
3884:       for (f = 0; f < Nf; ++f) {
3885:         PetscObject  obj;
3886:         PetscClassId id;
3887:         PetscBool    fimp;

3889:         PetscCall(PetscDSGetImplicit(prob, f, &fimp));
3890:         if (isImplicit != fimp) continue;
3891:         PetscCall(PetscDSGetDiscretization(prob, f, &obj));
3892:         PetscCall(PetscObjectGetClassId(obj, &id));
3893:         if (id == PETSCFE_CLASSID) {
3894:           PetscFE fe = (PetscFE)obj;

3896:           PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
3897:           PetscCall(PetscObjectReference((PetscObject)quads[f]));
3898:           PetscCall(DMSNESGetFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
3899:         }
3900:       }
3901:     }
3902:   }
3903:   /* Loop over chunks */
3904:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
3905:   PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
3906:   if (useFEM) PetscCall(ISCreate(PETSC_COMM_SELF, &chunkIS));
3907:   numCells      = cEnd - cStart;
3908:   numChunks     = 1;
3909:   cellChunkSize = numCells / numChunks;
3910:   numChunks     = PetscMin(1, numCells);
3911:   key.label     = NULL;
3912:   key.value     = 0;
3913:   key.part      = 0;
3914:   for (chunk = 0; chunk < numChunks; ++chunk) {
3915:     PetscScalar     *elemVec, *fluxL = NULL, *fluxR = NULL;
3916:     PetscReal       *vol   = NULL;
3917:     PetscFVFaceGeom *fgeom = NULL;
3918:     PetscInt         cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
3919:     PetscInt         numFaces = 0;

3921:     /* Extract field coefficients */
3922:     if (useFEM) {
3923:       PetscCall(ISGetPointSubrange(chunkIS, cS, cE, cells));
3924:       PetscCall(DMPlexGetCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
3925:       PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
3926:       PetscCall(PetscArrayzero(elemVec, numCells * totDim));
3927:     }
3928:     /* TODO We will interlace both our field coefficients (u, u_t, uL, uR, etc.) and our output (elemVec, fL, fR). I think this works */
3929:     /* Loop over fields */
3930:     for (f = 0; f < Nf; ++f) {
3931:       PetscObject  obj;
3932:       PetscClassId id;
3933:       PetscBool    fimp;
3934:       PetscInt     numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;

3936:       key.field = f;
3937:       PetscCall(PetscDSGetImplicit(prob, f, &fimp));
3938:       if (isImplicit != fimp) continue;
3939:       PetscCall(PetscDSGetDiscretization(prob, f, &obj));
3940:       PetscCall(PetscObjectGetClassId(obj, &id));
3941:       if (id == PETSCFE_CLASSID) {
3942:         PetscFE         fe        = (PetscFE)obj;
3943:         PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[f];
3944:         PetscFEGeom    *chunkGeom = NULL;
3945:         PetscQuadrature quad      = affineQuad ? affineQuad : quads[f];
3946:         PetscInt        Nq, Nb;

3948:         PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
3949:         PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
3950:         PetscCall(PetscFEGetDimension(fe, &Nb));
3951:         blockSize = Nb;
3952:         batchSize = numBlocks * blockSize;
3953:         PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
3954:         numChunks = numCells / (numBatches * batchSize);
3955:         Ne        = numChunks * numBatches * batchSize;
3956:         Nr        = numCells % (numBatches * batchSize);
3957:         offset    = numCells - Nr;
3958:         /* Integrate FE residual to get elemVec (need fields at quadrature points) */
3959:         /*   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) */
3960:         PetscCall(PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom));
3961:         PetscCall(PetscFEIntegrateResidual(prob, key, Ne, chunkGeom, u, u_t, probAux, a, t, elemVec));
3962:         PetscCall(PetscFEGeomGetChunk(geom, offset, numCells, &chunkGeom));
3963:         PetscCall(PetscFEIntegrateResidual(prob, key, Nr, chunkGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, &a[offset * totDimAux], t, &elemVec[offset * totDim]));
3964:         PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &chunkGeom));
3965:       } else if (id == PETSCFV_CLASSID) {
3966:         PetscFV fv = (PetscFV)obj;

3968:         Ne = numFaces;
3969:         /* Riemann solve over faces (need fields at face centroids) */
3970:         /*   We need to evaluate FE fields at those coordinates */
3971:         PetscCall(PetscFVIntegrateRHSFunction(fv, prob, f, Ne, fgeom, vol, uL, uR, fluxL, fluxR));
3972:       } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
3973:     }
3974:     /* Loop over domain */
3975:     if (useFEM) {
3976:       /* Add elemVec to locX */
3977:       for (c = cS; c < cE; ++c) {
3978:         const PetscInt cell = cells ? cells[c] : c;
3979:         const PetscInt cind = c - cStart;

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

3985:           PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
3986:           if (ghostVal > 0) continue;
3987:         }
3988:         PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind * totDim], ADD_ALL_VALUES));
3989:       }
3990:     }
3991:     /* Handle time derivative */
3992:     if (locX_t) {
3993:       PetscScalar *x_t, *fa;

3995:       PetscCall(VecGetArray(locF, &fa));
3996:       PetscCall(VecGetArray(locX_t, &x_t));
3997:       for (f = 0; f < Nf; ++f) {
3998:         PetscFV      fv;
3999:         PetscObject  obj;
4000:         PetscClassId id;
4001:         PetscInt     pdim, d;

4003:         PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4004:         PetscCall(PetscObjectGetClassId(obj, &id));
4005:         if (id != PETSCFV_CLASSID) continue;
4006:         fv = (PetscFV)obj;
4007:         PetscCall(PetscFVGetNumComponents(fv, &pdim));
4008:         for (c = cS; c < cE; ++c) {
4009:           const PetscInt cell = cells ? cells[c] : c;
4010:           PetscScalar   *u_t, *r;

4012:           if (ghostLabel) {
4013:             PetscInt ghostVal;

4015:             PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
4016:             if (ghostVal > 0) continue;
4017:           }
4018:           PetscCall(DMPlexPointLocalFieldRead(dm, cell, f, x_t, &u_t));
4019:           PetscCall(DMPlexPointLocalFieldRef(dm, cell, f, fa, &r));
4020:           for (d = 0; d < pdim; ++d) r[d] += u_t[d];
4021:         }
4022:       }
4023:       PetscCall(VecRestoreArray(locX_t, &x_t));
4024:       PetscCall(VecRestoreArray(locF, &fa));
4025:     }
4026:     if (useFEM) {
4027:       PetscCall(DMPlexRestoreCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
4028:       PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
4029:     }
4030:   }
4031:   if (useFEM) PetscCall(ISDestroy(&chunkIS));
4032:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
4033:   /* TODO Could include boundary residual here (see DMPlexComputeResidual_Internal) */
4034:   if (useFEM) {
4035:     if (maxDegree <= 1) {
4036:       PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
4037:       PetscCall(PetscQuadratureDestroy(&affineQuad));
4038:     } else {
4039:       for (f = 0; f < Nf; ++f) {
4040:         PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
4041:         PetscCall(PetscQuadratureDestroy(&quads[f]));
4042:       }
4043:       PetscCall(PetscFree2(quads, geoms));
4044:     }
4045:   }
4046:   PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
4047:   PetscFunctionReturn(PETSC_SUCCESS);
4048: }

4050: /*
4051:   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

4053:   X   - The local solution vector
4054:   X_t - The local solution time derivative vector, or NULL
4055: */
4056: 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)
4057: {
4058:   DM_Plex        *mesh = (DM_Plex *)dm->data;
4059:   const char     *name = "Jacobian", *nameP = "JacobianPre";
4060:   DM              dmAux = NULL;
4061:   PetscDS         prob, probAux = NULL;
4062:   PetscSection    sectionAux = NULL;
4063:   Vec             A;
4064:   DMField         coordField;
4065:   PetscFEGeom    *cgeomFEM;
4066:   PetscQuadrature qGeom = NULL;
4067:   Mat             J = Jac, JP = JacP;
4068:   PetscScalar    *work, *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL, *elemMatP = NULL, *elemMatD = NULL;
4069:   PetscBool       hasJac, hasPrec, hasDyn, assembleJac, *isFE, hasFV = PETSC_FALSE;
4070:   const PetscInt *cells;
4071:   PetscFormKey    key;
4072:   PetscInt        Nf, fieldI, fieldJ, maxDegree, numCells, cStart, cEnd, numChunks, chunkSize, chunk, totDim, totDimAux = 0, sz, wsz, off = 0, offCell = 0;

4074:   PetscFunctionBegin;
4075:   PetscCall(ISGetLocalSize(cellIS, &numCells));
4076:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
4077:   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
4078:   PetscCall(DMGetDS(dm, &prob));
4079:   PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &A));
4080:   if (A) {
4081:     PetscCall(VecGetDM(A, &dmAux));
4082:     PetscCall(DMGetLocalSection(dmAux, &sectionAux));
4083:     PetscCall(DMGetDS(dmAux, &probAux));
4084:   }
4085:   /* Get flags */
4086:   PetscCall(PetscDSGetNumFields(prob, &Nf));
4087:   PetscCall(DMGetWorkArray(dm, Nf, MPIU_BOOL, &isFE));
4088:   for (fieldI = 0; fieldI < Nf; ++fieldI) {
4089:     PetscObject  disc;
4090:     PetscClassId id;
4091:     PetscCall(PetscDSGetDiscretization(prob, fieldI, &disc));
4092:     PetscCall(PetscObjectGetClassId(disc, &id));
4093:     if (id == PETSCFE_CLASSID) {
4094:       isFE[fieldI] = PETSC_TRUE;
4095:     } else if (id == PETSCFV_CLASSID) {
4096:       hasFV        = PETSC_TRUE;
4097:       isFE[fieldI] = PETSC_FALSE;
4098:     }
4099:   }
4100:   PetscCall(PetscDSHasJacobian(prob, &hasJac));
4101:   PetscCall(PetscDSHasJacobianPreconditioner(prob, &hasPrec));
4102:   PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
4103:   assembleJac = hasJac && hasPrec && (Jac != JacP) ? PETSC_TRUE : PETSC_FALSE;
4104:   hasDyn      = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
4105:   if (hasFV) PetscCall(MatSetOption(JP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE)); /* No allocated space for FV stuff, so ignore the zero entries */
4106:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
4107:   if (probAux) PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
4108:   /* Compute batch sizes */
4109:   if (isFE[0]) {
4110:     PetscFE         fe;
4111:     PetscQuadrature q;
4112:     PetscInt        numQuadPoints, numBatches, batchSize, numBlocks, blockSize, Nb;

4114:     PetscCall(PetscDSGetDiscretization(prob, 0, (PetscObject *)&fe));
4115:     PetscCall(PetscFEGetQuadrature(fe, &q));
4116:     PetscCall(PetscQuadratureGetData(q, NULL, NULL, &numQuadPoints, NULL, NULL));
4117:     PetscCall(PetscFEGetDimension(fe, &Nb));
4118:     PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
4119:     blockSize = Nb * numQuadPoints;
4120:     batchSize = numBlocks * blockSize;
4121:     chunkSize = numBatches * batchSize;
4122:     numChunks = numCells / chunkSize + numCells % chunkSize;
4123:     PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
4124:   } else {
4125:     chunkSize = numCells;
4126:     numChunks = 1;
4127:   }
4128:   /* Get work space */
4129:   wsz = (((X ? 1 : 0) + (X_t ? 1 : 0) + (dmAux ? 1 : 0)) * totDim + ((hasJac ? 1 : 0) + (hasPrec ? 1 : 0) + (hasDyn ? 1 : 0)) * totDim * totDim) * chunkSize;
4130:   PetscCall(DMGetWorkArray(dm, wsz, MPIU_SCALAR, &work));
4131:   PetscCall(PetscArrayzero(work, wsz));
4132:   off      = 0;
4133:   u        = X ? (sz = chunkSize * totDim, off += sz, work + off - sz) : NULL;
4134:   u_t      = X_t ? (sz = chunkSize * totDim, off += sz, work + off - sz) : NULL;
4135:   a        = dmAux ? (sz = chunkSize * totDimAux, off += sz, work + off - sz) : NULL;
4136:   elemMat  = hasJac ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL;
4137:   elemMatP = hasPrec ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL;
4138:   elemMatD = hasDyn ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL;
4139:   PetscCheck(off == wsz, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Error is workspace size %" PetscInt_FMT " should be %" PetscInt_FMT, off, wsz);
4140:   /* Setup geometry */
4141:   PetscCall(DMGetCoordinateField(dm, &coordField));
4142:   PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
4143:   if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
4144:   if (!qGeom) {
4145:     PetscFE fe;

4147:     PetscCall(PetscDSGetDiscretization(prob, 0, (PetscObject *)&fe));
4148:     PetscCall(PetscFEGetQuadrature(fe, &qGeom));
4149:     PetscCall(PetscObjectReference((PetscObject)qGeom));
4150:   }
4151:   PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
4152:   /* Compute volume integrals */
4153:   if (assembleJac) PetscCall(MatZeroEntries(J));
4154:   PetscCall(MatZeroEntries(JP));
4155:   key.label = NULL;
4156:   key.value = 0;
4157:   key.part  = 0;
4158:   for (chunk = 0; chunk < numChunks; ++chunk, offCell += chunkSize) {
4159:     const PetscInt Ncell = PetscMin(chunkSize, numCells - offCell);
4160:     PetscInt       c;

4162:     /* Extract values */
4163:     for (c = 0; c < Ncell; ++c) {
4164:       const PetscInt cell = cells ? cells[c + offCell] : c + offCell;
4165:       PetscScalar   *x = NULL, *x_t = NULL;
4166:       PetscInt       i;

4168:       if (X) {
4169:         PetscCall(DMPlexVecGetClosure(dm, section, X, cell, NULL, &x));
4170:         for (i = 0; i < totDim; ++i) u[c * totDim + i] = x[i];
4171:         PetscCall(DMPlexVecRestoreClosure(dm, section, X, cell, NULL, &x));
4172:       }
4173:       if (X_t) {
4174:         PetscCall(DMPlexVecGetClosure(dm, section, X_t, cell, NULL, &x_t));
4175:         for (i = 0; i < totDim; ++i) u_t[c * totDim + i] = x_t[i];
4176:         PetscCall(DMPlexVecRestoreClosure(dm, section, X_t, cell, NULL, &x_t));
4177:       }
4178:       if (dmAux) {
4179:         PetscCall(DMPlexVecGetClosure(dmAux, sectionAux, A, cell, NULL, &x));
4180:         for (i = 0; i < totDimAux; ++i) a[c * totDimAux + i] = x[i];
4181:         PetscCall(DMPlexVecRestoreClosure(dmAux, sectionAux, A, cell, NULL, &x));
4182:       }
4183:     }
4184:     for (fieldI = 0; fieldI < Nf; ++fieldI) {
4185:       PetscFE fe;
4186:       PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
4187:       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
4188:         key.field = fieldI * Nf + fieldJ;
4189:         if (hasJac) PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMat));
4190:         if (hasPrec) PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatP));
4191:         if (hasDyn) PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatD));
4192:       }
4193:       /* For finite volume, add the identity */
4194:       if (!isFE[fieldI]) {
4195:         PetscFV  fv;
4196:         PetscInt eOffset = 0, Nc, fc, foff;

4198:         PetscCall(PetscDSGetFieldOffset(prob, fieldI, &foff));
4199:         PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fv));
4200:         PetscCall(PetscFVGetNumComponents(fv, &Nc));
4201:         for (c = 0; c < chunkSize; ++c, eOffset += totDim * totDim) {
4202:           for (fc = 0; fc < Nc; ++fc) {
4203:             const PetscInt i = foff + fc;
4204:             if (hasJac) elemMat[eOffset + i * totDim + i] = 1.0;
4205:             if (hasPrec) elemMatP[eOffset + i * totDim + i] = 1.0;
4206:           }
4207:         }
4208:       }
4209:     }
4210:     /*   Add contribution from X_t */
4211:     if (hasDyn) {
4212:       for (c = 0; c < chunkSize * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
4213:     }
4214:     /* Insert values into matrix */
4215:     for (c = 0; c < Ncell; ++c) {
4216:       const PetscInt cell = cells ? cells[c + offCell] : c + offCell;
4217:       if (mesh->printFEM > 1) {
4218:         if (hasJac) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[(c - cStart) * totDim * totDim]));
4219:         if (hasPrec) PetscCall(DMPrintCellMatrix(cell, nameP, totDim, totDim, &elemMatP[(c - cStart) * totDim * totDim]));
4220:       }
4221:       if (assembleJac) PetscCall(DMPlexMatSetClosure(dm, section, globalSection, Jac, cell, &elemMat[(c - cStart) * totDim * totDim], ADD_VALUES));
4222:       PetscCall(DMPlexMatSetClosure(dm, section, globalSection, JP, cell, &elemMat[(c - cStart) * totDim * totDim], ADD_VALUES));
4223:     }
4224:   }
4225:   /* Cleanup */
4226:   PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
4227:   PetscCall(PetscQuadratureDestroy(&qGeom));
4228:   if (hasFV) PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE));
4229:   PetscCall(DMRestoreWorkArray(dm, Nf, MPIU_BOOL, &isFE));
4230:   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));
4231:   /* Compute boundary integrals */
4232:   /* PetscCall(DMPlexComputeBdJacobian_Internal(dm, X, X_t, t, X_tShift, Jac, JacP, ctx)); */
4233:   /* Assemble matrix */
4234:   if (assembleJac) {
4235:     PetscCall(MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY));
4236:     PetscCall(MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY));
4237:   }
4238:   PetscCall(MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY));
4239:   PetscCall(MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY));
4240:   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
4241:   PetscFunctionReturn(PETSC_SUCCESS);
4242: }

4244: /******** FEM Assembly Function ********/

4246: static PetscErrorCode DMConvertPlex_Internal(DM dm, DM *plex, PetscBool copy)
4247: {
4248:   PetscBool isPlex;

4250:   PetscFunctionBegin;
4251:   PetscCall(PetscObjectTypeCompare((PetscObject)dm, DMPLEX, &isPlex));
4252:   if (isPlex) {
4253:     *plex = dm;
4254:     PetscCall(PetscObjectReference((PetscObject)dm));
4255:   } else {
4256:     PetscCall(PetscObjectQuery((PetscObject)dm, "dm_plex", (PetscObject *)plex));
4257:     if (!*plex) {
4258:       PetscCall(DMConvert(dm, DMPLEX, plex));
4259:       PetscCall(PetscObjectCompose((PetscObject)dm, "dm_plex", (PetscObject)*plex));
4260:       if (copy) PetscCall(DMCopyAuxiliaryVec(dm, *plex));
4261:     } else {
4262:       PetscCall(PetscObjectReference((PetscObject)*plex));
4263:     }
4264:   }
4265:   PetscFunctionReturn(PETSC_SUCCESS);
4266: }

4268: /*@
4269:   DMPlexGetGeometryFVM - Return precomputed geometric data

4271:   Collective on dm

4273:   Input Parameter:
4274: . dm - The `DM`

4276:   Output Parameters:
4277: + facegeom - The values precomputed from face geometry
4278: . cellgeom - The values precomputed from cell geometry
4279: - minRadius - The minimum radius over the mesh of an inscribed sphere in a cell

4281:   Level: developer

4283: .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMTSSetRHSFunctionLocal()`
4284: @*/
4285: PetscErrorCode DMPlexGetGeometryFVM(DM dm, Vec *facegeom, Vec *cellgeom, PetscReal *minRadius)
4286: {
4287:   DM plex;

4289:   PetscFunctionBegin;
4291:   PetscCall(DMConvertPlex_Internal(dm, &plex, PETSC_TRUE));
4292:   PetscCall(DMPlexGetDataFVM(plex, NULL, cellgeom, facegeom, NULL));
4293:   if (minRadius) PetscCall(DMPlexGetMinRadius(plex, minRadius));
4294:   PetscCall(DMDestroy(&plex));
4295:   PetscFunctionReturn(PETSC_SUCCESS);
4296: }

4298: /*@
4299:   DMPlexGetGradientDM - Return gradient data layout

4301:   Collective on dm

4303:   Input Parameters:
4304: + dm - The `DM`
4305: - fv - The PetscFV

4307:   Output Parameter:
4308: . dmGrad - The layout for gradient values

4310:   Level: developer

4312: .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetGeometryFVM()`
4313: @*/
4314: PetscErrorCode DMPlexGetGradientDM(DM dm, PetscFV fv, DM *dmGrad)
4315: {
4316:   DM        plex;
4317:   PetscBool computeGradients;

4319:   PetscFunctionBegin;
4323:   PetscCall(PetscFVGetComputeGradients(fv, &computeGradients));
4324:   if (!computeGradients) {
4325:     *dmGrad = NULL;
4326:     PetscFunctionReturn(PETSC_SUCCESS);
4327:   }
4328:   PetscCall(DMConvertPlex_Internal(dm, &plex, PETSC_TRUE));
4329:   PetscCall(DMPlexGetDataFVM(plex, fv, NULL, NULL, dmGrad));
4330:   PetscCall(DMDestroy(&plex));
4331:   PetscFunctionReturn(PETSC_SUCCESS);
4332: }

4334: static PetscErrorCode DMPlexComputeBdResidual_Single_Internal(DM dm, PetscReal t, PetscWeakForm wf, PetscFormKey key, Vec locX, Vec locX_t, Vec locF, DMField coordField, IS facetIS)
4335: {
4336:   DM_Plex        *mesh = (DM_Plex *)dm->data;
4337:   DM              plex = NULL, plexA = NULL;
4338:   DMEnclosureType encAux;
4339:   PetscDS         prob, probAux       = NULL;
4340:   PetscSection    section, sectionAux = NULL;
4341:   Vec             locA = NULL;
4342:   PetscScalar    *u = NULL, *u_t = NULL, *a = NULL, *elemVec = NULL;
4343:   PetscInt        totDim, totDimAux = 0;

4345:   PetscFunctionBegin;
4346:   PetscCall(DMConvert(dm, DMPLEX, &plex));
4347:   PetscCall(DMGetLocalSection(dm, &section));
4348:   PetscCall(DMGetDS(dm, &prob));
4349:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
4350:   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &locA));
4351:   if (locA) {
4352:     DM dmAux;

4354:     PetscCall(VecGetDM(locA, &dmAux));
4355:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
4356:     PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
4357:     PetscCall(DMGetDS(plexA, &probAux));
4358:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
4359:     PetscCall(DMGetLocalSection(plexA, &sectionAux));
4360:   }
4361:   {
4362:     PetscFEGeom    *fgeom;
4363:     PetscInt        maxDegree;
4364:     PetscQuadrature qGeom = NULL;
4365:     IS              pointIS;
4366:     const PetscInt *points;
4367:     PetscInt        numFaces, face, Nq;

4369:     PetscCall(DMLabelGetStratumIS(key.label, key.value, &pointIS));
4370:     if (!pointIS) goto end; /* No points with that id on this process */
4371:     {
4372:       IS isectIS;

4374:       /* TODO: Special cases of ISIntersect where it is quick to check a priori if one is a superset of the other */
4375:       PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS));
4376:       PetscCall(ISDestroy(&pointIS));
4377:       pointIS = isectIS;
4378:     }
4379:     PetscCall(ISGetLocalSize(pointIS, &numFaces));
4380:     PetscCall(ISGetIndices(pointIS, &points));
4381:     PetscCall(PetscMalloc4(numFaces * totDim, &u, locX_t ? numFaces * totDim : 0, &u_t, numFaces * totDim, &elemVec, locA ? numFaces * totDimAux : 0, &a));
4382:     PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
4383:     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom));
4384:     if (!qGeom) {
4385:       PetscFE fe;

4387:       PetscCall(PetscDSGetDiscretization(prob, key.field, (PetscObject *)&fe));
4388:       PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
4389:       PetscCall(PetscObjectReference((PetscObject)qGeom));
4390:     }
4391:     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
4392:     PetscCall(DMSNESGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
4393:     for (face = 0; face < numFaces; ++face) {
4394:       const PetscInt point = points[face], *support;
4395:       PetscScalar   *x     = NULL;
4396:       PetscInt       i;

4398:       PetscCall(DMPlexGetSupport(dm, point, &support));
4399:       PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
4400:       for (i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
4401:       PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
4402:       if (locX_t) {
4403:         PetscCall(DMPlexVecGetClosure(plex, section, locX_t, support[0], NULL, &x));
4404:         for (i = 0; i < totDim; ++i) u_t[face * totDim + i] = x[i];
4405:         PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, support[0], NULL, &x));
4406:       }
4407:       if (locA) {
4408:         PetscInt subp;

4410:         PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
4411:         PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
4412:         for (i = 0; i < totDimAux; ++i) a[face * totDimAux + i] = x[i];
4413:         PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
4414:       }
4415:     }
4416:     PetscCall(PetscArrayzero(elemVec, numFaces * totDim));
4417:     {
4418:       PetscFE      fe;
4419:       PetscInt     Nb;
4420:       PetscFEGeom *chunkGeom = NULL;
4421:       /* Conforming batches */
4422:       PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
4423:       /* Remainder */
4424:       PetscInt Nr, offset;

4426:       PetscCall(PetscDSGetDiscretization(prob, key.field, (PetscObject *)&fe));
4427:       PetscCall(PetscFEGetDimension(fe, &Nb));
4428:       PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
4429:       /* TODO: documentation is unclear about what is going on with these numbers: how should Nb / Nq factor in ? */
4430:       blockSize = Nb;
4431:       batchSize = numBlocks * blockSize;
4432:       PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
4433:       numChunks = numFaces / (numBatches * batchSize);
4434:       Ne        = numChunks * numBatches * batchSize;
4435:       Nr        = numFaces % (numBatches * batchSize);
4436:       offset    = numFaces - Nr;
4437:       PetscCall(PetscFEGeomGetChunk(fgeom, 0, offset, &chunkGeom));
4438:       PetscCall(PetscFEIntegrateBdResidual(prob, wf, key, Ne, chunkGeom, u, u_t, probAux, a, t, elemVec));
4439:       PetscCall(PetscFEGeomRestoreChunk(fgeom, 0, offset, &chunkGeom));
4440:       PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom));
4441:       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]));
4442:       PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom));
4443:     }
4444:     for (face = 0; face < numFaces; ++face) {
4445:       const PetscInt point = points[face], *support;

4447:       if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(point, "BdResidual", totDim, &elemVec[face * totDim]));
4448:       PetscCall(DMPlexGetSupport(plex, point, &support));
4449:       PetscCall(DMPlexVecSetClosure(plex, NULL, locF, support[0], &elemVec[face * totDim], ADD_ALL_VALUES));
4450:     }
4451:     PetscCall(DMSNESRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
4452:     PetscCall(PetscQuadratureDestroy(&qGeom));
4453:     PetscCall(ISRestoreIndices(pointIS, &points));
4454:     PetscCall(ISDestroy(&pointIS));
4455:     PetscCall(PetscFree4(u, u_t, elemVec, a));
4456:   }
4457: end:
4458:   PetscCall(DMDestroy(&plex));
4459:   PetscCall(DMDestroy(&plexA));
4460:   PetscFunctionReturn(PETSC_SUCCESS);
4461: }

4463: PetscErrorCode DMPlexComputeBdResidualSingle(DM dm, PetscReal t, PetscWeakForm wf, PetscFormKey key, Vec locX, Vec locX_t, Vec locF)
4464: {
4465:   DMField  coordField;
4466:   DMLabel  depthLabel;
4467:   IS       facetIS;
4468:   PetscInt dim;

4470:   PetscFunctionBegin;
4471:   PetscCall(DMGetDimension(dm, &dim));
4472:   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
4473:   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
4474:   PetscCall(DMGetCoordinateField(dm, &coordField));
4475:   PetscCall(DMPlexComputeBdResidual_Single_Internal(dm, t, wf, key, locX, locX_t, locF, coordField, facetIS));
4476:   PetscCall(ISDestroy(&facetIS));
4477:   PetscFunctionReturn(PETSC_SUCCESS);
4478: }

4480: PetscErrorCode DMPlexComputeBdResidual_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
4481: {
4482:   PetscDS  prob;
4483:   PetscInt numBd, bd;
4484:   DMField  coordField = NULL;
4485:   IS       facetIS    = NULL;
4486:   DMLabel  depthLabel;
4487:   PetscInt dim;

4489:   PetscFunctionBegin;
4490:   PetscCall(DMGetDS(dm, &prob));
4491:   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
4492:   PetscCall(DMGetDimension(dm, &dim));
4493:   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
4494:   PetscCall(PetscDSGetNumBoundary(prob, &numBd));
4495:   for (bd = 0; bd < numBd; ++bd) {
4496:     PetscWeakForm           wf;
4497:     DMBoundaryConditionType type;
4498:     DMLabel                 label;
4499:     const PetscInt         *values;
4500:     PetscInt                field, numValues, v;
4501:     PetscObject             obj;
4502:     PetscClassId            id;
4503:     PetscFormKey            key;

4505:     PetscCall(PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &field, NULL, NULL, NULL, NULL, NULL));
4506:     if (type & DM_BC_ESSENTIAL) continue;
4507:     PetscCall(PetscDSGetDiscretization(prob, field, &obj));
4508:     PetscCall(PetscObjectGetClassId(obj, &id));
4509:     if (id != PETSCFE_CLASSID) continue;
4510:     if (!facetIS) {
4511:       DMLabel  depthLabel;
4512:       PetscInt dim;

4514:       PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
4515:       PetscCall(DMGetDimension(dm, &dim));
4516:       PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
4517:     }
4518:     PetscCall(DMGetCoordinateField(dm, &coordField));
4519:     for (v = 0; v < numValues; ++v) {
4520:       key.label = label;
4521:       key.value = values[v];
4522:       key.field = field;
4523:       key.part  = 0;
4524:       PetscCall(DMPlexComputeBdResidual_Single_Internal(dm, t, wf, key, locX, locX_t, locF, coordField, facetIS));
4525:     }
4526:   }
4527:   PetscCall(ISDestroy(&facetIS));
4528:   PetscFunctionReturn(PETSC_SUCCESS);
4529: }

4531: PetscErrorCode DMPlexComputeResidual_Internal(DM dm, PetscFormKey key, IS cellIS, PetscReal time, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
4532: {
4533:   DM_Plex         *mesh       = (DM_Plex *)dm->data;
4534:   const char      *name       = "Residual";
4535:   DM               dmAux      = NULL;
4536:   DM               dmGrad     = NULL;
4537:   DMLabel          ghostLabel = NULL;
4538:   PetscDS          ds         = NULL;
4539:   PetscDS          dsAux      = NULL;
4540:   PetscSection     section    = NULL;
4541:   PetscBool        useFEM     = PETSC_FALSE;
4542:   PetscBool        useFVM     = PETSC_FALSE;
4543:   PetscBool        isImplicit = (locX_t || time == PETSC_MIN_REAL) ? PETSC_TRUE : PETSC_FALSE;
4544:   PetscFV          fvm        = NULL;
4545:   PetscFVCellGeom *cgeomFVM   = NULL;
4546:   PetscFVFaceGeom *fgeomFVM   = NULL;
4547:   DMField          coordField = NULL;
4548:   Vec              locA, cellGeometryFVM = NULL, faceGeometryFVM = NULL, grad, locGrad = NULL;
4549:   PetscScalar     *u = NULL, *u_t, *a, *uL, *uR;
4550:   IS               chunkIS;
4551:   const PetscInt  *cells;
4552:   PetscInt         cStart, cEnd, numCells;
4553:   PetscInt         Nf, f, totDim, totDimAux, numChunks, cellChunkSize, faceChunkSize, chunk, fStart, fEnd;
4554:   PetscInt         maxDegree  = PETSC_MAX_INT;
4555:   PetscQuadrature  affineQuad = NULL, *quads = NULL;
4556:   PetscFEGeom     *affineGeom = NULL, **geoms = NULL;

4558:   PetscFunctionBegin;
4559:   if (!cellIS) PetscFunctionReturn(PETSC_SUCCESS);
4560:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
4561:   if (cStart >= cEnd) PetscFunctionReturn(PETSC_SUCCESS);
4562:   PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
4563:   /* TODO The places where we have to use isFE are probably the member functions for the PetscDisc class */
4564:   /* TODO The FVM geometry is over-manipulated. Make the precalc functions return exactly what we need */
4565:   /* FEM+FVM */
4566:   PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
4567:   /* 1: Get sizes from dm and dmAux */
4568:   PetscCall(DMGetLocalSection(dm, &section));
4569:   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
4570:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds));
4571:   PetscCall(PetscDSGetNumFields(ds, &Nf));
4572:   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
4573:   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &locA));
4574:   if (locA) {
4575:     PetscInt subcell;
4576:     PetscCall(VecGetDM(locA, &dmAux));
4577:     PetscCall(DMGetEnclosurePoint(dmAux, dm, DM_ENC_UNKNOWN, cells ? cells[cStart] : cStart, &subcell));
4578:     PetscCall(DMGetCellDS(dmAux, subcell, &dsAux));
4579:     PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
4580:   }
4581:   /* 2: Get geometric data */
4582:   for (f = 0; f < Nf; ++f) {
4583:     PetscObject  obj;
4584:     PetscClassId id;
4585:     PetscBool    fimp;

4587:     PetscCall(PetscDSGetImplicit(ds, f, &fimp));
4588:     if (isImplicit != fimp) continue;
4589:     PetscCall(PetscDSGetDiscretization(ds, f, &obj));
4590:     PetscCall(PetscObjectGetClassId(obj, &id));
4591:     if (id == PETSCFE_CLASSID) useFEM = PETSC_TRUE;
4592:     if (id == PETSCFV_CLASSID) {
4593:       useFVM = PETSC_TRUE;
4594:       fvm    = (PetscFV)obj;
4595:     }
4596:   }
4597:   if (useFEM) {
4598:     PetscCall(DMGetCoordinateField(dm, &coordField));
4599:     PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
4600:     if (maxDegree <= 1) {
4601:       PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad));
4602:       if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
4603:     } else {
4604:       PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
4605:       for (f = 0; f < Nf; ++f) {
4606:         PetscObject  obj;
4607:         PetscClassId id;
4608:         PetscBool    fimp;

4610:         PetscCall(PetscDSGetImplicit(ds, f, &fimp));
4611:         if (isImplicit != fimp) continue;
4612:         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
4613:         PetscCall(PetscObjectGetClassId(obj, &id));
4614:         if (id == PETSCFE_CLASSID) {
4615:           PetscFE fe = (PetscFE)obj;

4617:           PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
4618:           PetscCall(PetscObjectReference((PetscObject)quads[f]));
4619:           PetscCall(DMSNESGetFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
4620:         }
4621:       }
4622:     }
4623:   }
4624:   if (useFVM) {
4625:     PetscCall(DMPlexGetGeometryFVM(dm, &faceGeometryFVM, &cellGeometryFVM, NULL));
4626:     PetscCall(VecGetArrayRead(faceGeometryFVM, (const PetscScalar **)&fgeomFVM));
4627:     PetscCall(VecGetArrayRead(cellGeometryFVM, (const PetscScalar **)&cgeomFVM));
4628:     /* Reconstruct and limit cell gradients */
4629:     PetscCall(DMPlexGetGradientDM(dm, fvm, &dmGrad));
4630:     if (dmGrad) {
4631:       PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
4632:       PetscCall(DMGetGlobalVector(dmGrad, &grad));
4633:       PetscCall(DMPlexReconstructGradients_Internal(dm, fvm, fStart, fEnd, faceGeometryFVM, cellGeometryFVM, locX, grad));
4634:       /* Communicate gradient values */
4635:       PetscCall(DMGetLocalVector(dmGrad, &locGrad));
4636:       PetscCall(DMGlobalToLocalBegin(dmGrad, grad, INSERT_VALUES, locGrad));
4637:       PetscCall(DMGlobalToLocalEnd(dmGrad, grad, INSERT_VALUES, locGrad));
4638:       PetscCall(DMRestoreGlobalVector(dmGrad, &grad));
4639:     }
4640:     /* Handle non-essential (e.g. outflow) boundary values */
4641:     PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_FALSE, locX, time, faceGeometryFVM, cellGeometryFVM, locGrad));
4642:   }
4643:   /* Loop over chunks */
4644:   if (useFEM) PetscCall(ISCreate(PETSC_COMM_SELF, &chunkIS));
4645:   numCells      = cEnd - cStart;
4646:   numChunks     = 1;
4647:   cellChunkSize = numCells / numChunks;
4648:   faceChunkSize = (fEnd - fStart) / numChunks;
4649:   numChunks     = PetscMin(1, numCells);
4650:   for (chunk = 0; chunk < numChunks; ++chunk) {
4651:     PetscScalar     *elemVec, *fluxL, *fluxR;
4652:     PetscReal       *vol;
4653:     PetscFVFaceGeom *fgeom;
4654:     PetscInt         cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
4655:     PetscInt         fS = fStart + chunk * faceChunkSize, fE = PetscMin(fS + faceChunkSize, fEnd), numFaces = 0, face;

4657:     /* Extract field coefficients */
4658:     if (useFEM) {
4659:       PetscCall(ISGetPointSubrange(chunkIS, cS, cE, cells));
4660:       PetscCall(DMPlexGetCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
4661:       PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
4662:       PetscCall(PetscArrayzero(elemVec, numCells * totDim));
4663:     }
4664:     if (useFVM) {
4665:       PetscCall(DMPlexGetFaceFields(dm, fS, fE, locX, locX_t, faceGeometryFVM, cellGeometryFVM, locGrad, &numFaces, &uL, &uR));
4666:       PetscCall(DMPlexGetFaceGeometry(dm, fS, fE, faceGeometryFVM, cellGeometryFVM, &numFaces, &fgeom, &vol));
4667:       PetscCall(DMGetWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxL));
4668:       PetscCall(DMGetWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxR));
4669:       PetscCall(PetscArrayzero(fluxL, numFaces * totDim));
4670:       PetscCall(PetscArrayzero(fluxR, numFaces * totDim));
4671:     }
4672:     /* TODO We will interlace both our field coefficients (u, u_t, uL, uR, etc.) and our output (elemVec, fL, fR). I think this works */
4673:     /* Loop over fields */
4674:     for (f = 0; f < Nf; ++f) {
4675:       PetscObject  obj;
4676:       PetscClassId id;
4677:       PetscBool    fimp;
4678:       PetscInt     numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;

4680:       key.field = f;
4681:       PetscCall(PetscDSGetImplicit(ds, f, &fimp));
4682:       if (isImplicit != fimp) continue;
4683:       PetscCall(PetscDSGetDiscretization(ds, f, &obj));
4684:       PetscCall(PetscObjectGetClassId(obj, &id));
4685:       if (id == PETSCFE_CLASSID) {
4686:         PetscFE         fe        = (PetscFE)obj;
4687:         PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[f];
4688:         PetscFEGeom    *chunkGeom = NULL;
4689:         PetscQuadrature quad      = affineQuad ? affineQuad : quads[f];
4690:         PetscInt        Nq, Nb;

4692:         PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
4693:         PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
4694:         PetscCall(PetscFEGetDimension(fe, &Nb));
4695:         blockSize = Nb;
4696:         batchSize = numBlocks * blockSize;
4697:         PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
4698:         numChunks = numCells / (numBatches * batchSize);
4699:         Ne        = numChunks * numBatches * batchSize;
4700:         Nr        = numCells % (numBatches * batchSize);
4701:         offset    = numCells - Nr;
4702:         /* Integrate FE residual to get elemVec (need fields at quadrature points) */
4703:         /*   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) */
4704:         PetscCall(PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom));
4705:         PetscCall(PetscFEIntegrateResidual(ds, key, Ne, chunkGeom, u, u_t, dsAux, a, t, elemVec));
4706:         PetscCall(PetscFEGeomGetChunk(geom, offset, numCells, &chunkGeom));
4707:         PetscCall(PetscFEIntegrateResidual(ds, key, Nr, chunkGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, dsAux, &a[offset * totDimAux], t, &elemVec[offset * totDim]));
4708:         PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &chunkGeom));
4709:       } else if (id == PETSCFV_CLASSID) {
4710:         PetscFV fv = (PetscFV)obj;

4712:         Ne = numFaces;
4713:         /* Riemann solve over faces (need fields at face centroids) */
4714:         /*   We need to evaluate FE fields at those coordinates */
4715:         PetscCall(PetscFVIntegrateRHSFunction(fv, ds, f, Ne, fgeom, vol, uL, uR, fluxL, fluxR));
4716:       } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
4717:     }
4718:     /* Loop over domain */
4719:     if (useFEM) {
4720:       /* Add elemVec to locX */
4721:       for (c = cS; c < cE; ++c) {
4722:         const PetscInt cell = cells ? cells[c] : c;
4723:         const PetscInt cind = c - cStart;

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

4729:           PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
4730:           if (ghostVal > 0) continue;
4731:         }
4732:         PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind * totDim], ADD_ALL_VALUES));
4733:       }
4734:     }
4735:     if (useFVM) {
4736:       PetscScalar *fa;
4737:       PetscInt     iface;

4739:       PetscCall(VecGetArray(locF, &fa));
4740:       for (f = 0; f < Nf; ++f) {
4741:         PetscFV      fv;
4742:         PetscObject  obj;
4743:         PetscClassId id;
4744:         PetscInt     foff, pdim;

4746:         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
4747:         PetscCall(PetscDSGetFieldOffset(ds, f, &foff));
4748:         PetscCall(PetscObjectGetClassId(obj, &id));
4749:         if (id != PETSCFV_CLASSID) continue;
4750:         fv = (PetscFV)obj;
4751:         PetscCall(PetscFVGetNumComponents(fv, &pdim));
4752:         /* Accumulate fluxes to cells */
4753:         for (face = fS, iface = 0; face < fE; ++face) {
4754:           const PetscInt *scells;
4755:           PetscScalar    *fL = NULL, *fR = NULL;
4756:           PetscInt        ghost, d, nsupp, nchild;

4758:           PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
4759:           PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
4760:           PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
4761:           if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
4762:           PetscCall(DMPlexGetSupport(dm, face, &scells));
4763:           PetscCall(DMLabelGetValue(ghostLabel, scells[0], &ghost));
4764:           if (ghost <= 0) PetscCall(DMPlexPointLocalFieldRef(dm, scells[0], f, fa, &fL));
4765:           PetscCall(DMLabelGetValue(ghostLabel, scells[1], &ghost));
4766:           if (ghost <= 0) PetscCall(DMPlexPointLocalFieldRef(dm, scells[1], f, fa, &fR));
4767:           for (d = 0; d < pdim; ++d) {
4768:             if (fL) fL[d] -= fluxL[iface * totDim + foff + d];
4769:             if (fR) fR[d] += fluxR[iface * totDim + foff + d];
4770:           }
4771:           ++iface;
4772:         }
4773:       }
4774:       PetscCall(VecRestoreArray(locF, &fa));
4775:     }
4776:     /* Handle time derivative */
4777:     if (locX_t) {
4778:       PetscScalar *x_t, *fa;

4780:       PetscCall(VecGetArray(locF, &fa));
4781:       PetscCall(VecGetArray(locX_t, &x_t));
4782:       for (f = 0; f < Nf; ++f) {
4783:         PetscFV      fv;
4784:         PetscObject  obj;
4785:         PetscClassId id;
4786:         PetscInt     pdim, d;

4788:         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
4789:         PetscCall(PetscObjectGetClassId(obj, &id));
4790:         if (id != PETSCFV_CLASSID) continue;
4791:         fv = (PetscFV)obj;
4792:         PetscCall(PetscFVGetNumComponents(fv, &pdim));
4793:         for (c = cS; c < cE; ++c) {
4794:           const PetscInt cell = cells ? cells[c] : c;
4795:           PetscScalar   *u_t, *r;

4797:           if (ghostLabel) {
4798:             PetscInt ghostVal;

4800:             PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
4801:             if (ghostVal > 0) continue;
4802:           }
4803:           PetscCall(DMPlexPointLocalFieldRead(dm, cell, f, x_t, &u_t));
4804:           PetscCall(DMPlexPointLocalFieldRef(dm, cell, f, fa, &r));
4805:           for (d = 0; d < pdim; ++d) r[d] += u_t[d];
4806:         }
4807:       }
4808:       PetscCall(VecRestoreArray(locX_t, &x_t));
4809:       PetscCall(VecRestoreArray(locF, &fa));
4810:     }
4811:     if (useFEM) {
4812:       PetscCall(DMPlexRestoreCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
4813:       PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
4814:     }
4815:     if (useFVM) {
4816:       PetscCall(DMPlexRestoreFaceFields(dm, fS, fE, locX, locX_t, faceGeometryFVM, cellGeometryFVM, locGrad, &numFaces, &uL, &uR));
4817:       PetscCall(DMPlexRestoreFaceGeometry(dm, fS, fE, faceGeometryFVM, cellGeometryFVM, &numFaces, &fgeom, &vol));
4818:       PetscCall(DMRestoreWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxL));
4819:       PetscCall(DMRestoreWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxR));
4820:       if (dmGrad) PetscCall(DMRestoreLocalVector(dmGrad, &locGrad));
4821:     }
4822:   }
4823:   if (useFEM) PetscCall(ISDestroy(&chunkIS));
4824:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));

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

4829:     if (maxDegree <= 1) {
4830:       PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
4831:       PetscCall(PetscQuadratureDestroy(&affineQuad));
4832:     } else {
4833:       for (f = 0; f < Nf; ++f) {
4834:         PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
4835:         PetscCall(PetscQuadratureDestroy(&quads[f]));
4836:       }
4837:       PetscCall(PetscFree2(quads, geoms));
4838:     }
4839:   }

4841:   /* FEM */
4842:   /* 1: Get sizes from dm and dmAux */
4843:   /* 2: Get geometric data */
4844:   /* 3: Handle boundary values */
4845:   /* 4: Loop over domain */
4846:   /*   Extract coefficients */
4847:   /* Loop over fields */
4848:   /*   Set tiling for FE*/
4849:   /*   Integrate FE residual to get elemVec */
4850:   /*     Loop over subdomain */
4851:   /*       Loop over quad points */
4852:   /*         Transform coords to real space */
4853:   /*         Evaluate field and aux fields at point */
4854:   /*         Evaluate residual at point */
4855:   /*         Transform residual to real space */
4856:   /*       Add residual to elemVec */
4857:   /* Loop over domain */
4858:   /*   Add elemVec to locX */

4860:   /* FVM */
4861:   /* Get geometric data */
4862:   /* If using gradients */
4863:   /*   Compute gradient data */
4864:   /*   Loop over domain faces */
4865:   /*     Count computational faces */
4866:   /*     Reconstruct cell gradient */
4867:   /*   Loop over domain cells */
4868:   /*     Limit cell gradients */
4869:   /* Handle boundary values */
4870:   /* Loop over domain faces */
4871:   /*   Read out field, centroid, normal, volume for each side of face */
4872:   /* Riemann solve over faces */
4873:   /* Loop over domain faces */
4874:   /*   Accumulate fluxes to cells */
4875:   /* TODO Change printFEM to printDisc here */
4876:   if (mesh->printFEM) {
4877:     Vec          locFbc;
4878:     PetscInt     pStart, pEnd, p, maxDof;
4879:     PetscScalar *zeroes;

4881:     PetscCall(VecDuplicate(locF, &locFbc));
4882:     PetscCall(VecCopy(locF, locFbc));
4883:     PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
4884:     PetscCall(PetscSectionGetMaxDof(section, &maxDof));
4885:     PetscCall(PetscCalloc1(maxDof, &zeroes));
4886:     for (p = pStart; p < pEnd; p++) PetscCall(VecSetValuesSection(locFbc, section, p, zeroes, INSERT_BC_VALUES));
4887:     PetscCall(PetscFree(zeroes));
4888:     PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc));
4889:     PetscCall(VecDestroy(&locFbc));
4890:   }
4891:   PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
4892:   PetscFunctionReturn(PETSC_SUCCESS);
4893: }

4895: /*
4896:   1) Allow multiple kernels for BdResidual for hybrid DS

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

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

4903:   4) Run both kernels for each non-hybrid field with correct dsAux, and then hybrid field as before
4904: */
4905: PetscErrorCode DMPlexComputeResidual_Hybrid_Internal(DM dm, PetscFormKey key[], IS cellIS, PetscReal time, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
4906: {
4907:   DM_Plex        *mesh       = (DM_Plex *)dm->data;
4908:   const char     *name       = "Hybrid Residual";
4909:   DM              dmAux[3]   = {NULL, NULL, NULL};
4910:   DMLabel         ghostLabel = NULL;
4911:   PetscDS         ds         = NULL;
4912:   PetscDS         dsAux[3]   = {NULL, NULL, NULL};
4913:   Vec             locA[3]    = {NULL, NULL, NULL};
4914:   PetscScalar    *a[3]       = {NULL, NULL, NULL};
4915:   PetscSection    section    = NULL;
4916:   DMField         coordField = NULL;
4917:   PetscScalar    *u          = NULL, *u_t;
4918:   PetscScalar    *elemVec;
4919:   IS              chunkIS;
4920:   const PetscInt *cells;
4921:   PetscInt       *faces;
4922:   PetscInt        cStart, cEnd, numCells;
4923:   PetscInt        Nf, f, totDim, totDimAux[3], numChunks, cellChunkSize, chunk;
4924:   PetscInt        maxDegree  = PETSC_MAX_INT;
4925:   PetscQuadrature affineQuad = NULL, *quads = NULL;
4926:   PetscFEGeom    *affineGeom = NULL, **geoms = NULL;

4928:   PetscFunctionBegin;
4929:   if (!cellIS) PetscFunctionReturn(PETSC_SUCCESS);
4930:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
4931:   PetscCall(ISGetLocalSize(cellIS, &numCells));
4932:   if (cStart >= cEnd) PetscFunctionReturn(PETSC_SUCCESS);
4933:   if ((key[0].label == key[1].label) && (key[0].value == key[1].value) && (key[0].part == key[1].part)) {
4934:     const char *name;
4935:     PetscCall(PetscObjectGetName((PetscObject)key[0].label, &name));
4936:     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);
4937:   }
4938:   PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
4939:   /* TODO The places where we have to use isFE are probably the member functions for the PetscDisc class */
4940:   /* FEM */
4941:   /* 1: Get sizes from dm and dmAux */
4942:   PetscCall(DMGetSection(dm, &section));
4943:   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
4944:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds));
4945:   PetscCall(PetscDSGetNumFields(ds, &Nf));
4946:   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
4947:   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, key[2].value, key[2].part, &locA[2]));
4948:   if (locA[2]) {
4949:     const PetscInt cellStart = cells ? cells[cStart] : cStart;

4951:     PetscCall(VecGetDM(locA[2], &dmAux[2]));
4952:     PetscCall(DMGetCellDS(dmAux[2], cellStart, &dsAux[2]));
4953:     PetscCall(PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]));
4954:     {
4955:       const PetscInt *cone;
4956:       PetscInt        c;

4958:       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
4959:       for (c = 0; c < 2; ++c) {
4960:         const PetscInt *support;
4961:         PetscInt        ssize, s;

4963:         PetscCall(DMPlexGetSupport(dm, cone[c], &support));
4964:         PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
4965:         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);
4966:         if (support[0] == cellStart) s = 1;
4967:         else if (support[1] == cellStart) s = 0;
4968:         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
4969:         PetscCall(DMGetAuxiliaryVec(dm, key[c].label, key[c].value, key[c].part, &locA[c]));
4970:         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);
4971:         if (locA[c]) PetscCall(VecGetDM(locA[c], &dmAux[c]));
4972:         else dmAux[c] = dmAux[2];
4973:         PetscCall(DMGetCellDS(dmAux[c], support[s], &dsAux[c]));
4974:         PetscCall(PetscDSGetTotalDimension(dsAux[c], &totDimAux[c]));
4975:       }
4976:     }
4977:   }
4978:   /* 2: Setup geometric data */
4979:   PetscCall(DMGetCoordinateField(dm, &coordField));
4980:   PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
4981:   if (maxDegree > 1) {
4982:     PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
4983:     for (f = 0; f < Nf; ++f) {
4984:       PetscFE fe;

4986:       PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
4987:       if (fe) {
4988:         PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
4989:         PetscCall(PetscObjectReference((PetscObject)quads[f]));
4990:       }
4991:     }
4992:   }
4993:   /* Loop over chunks */
4994:   cellChunkSize = numCells;
4995:   numChunks     = !numCells ? 0 : PetscCeilReal(((PetscReal)numCells) / cellChunkSize);
4996:   PetscCall(PetscCalloc1(1 * cellChunkSize, &faces));
4997:   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 1 * cellChunkSize, faces, PETSC_USE_POINTER, &chunkIS));
4998:   /* Extract field coefficients */
4999:   /* NOTE This needs the end cap faces to have identical orientations */
5000:   PetscCall(DMPlexGetCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
5001:   PetscCall(DMPlexGetHybridAuxFields(dm, dmAux, dsAux, cellIS, locA, a));
5002:   PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVec));
5003:   for (chunk = 0; chunk < numChunks; ++chunk) {
5004:     PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;

5006:     PetscCall(PetscMemzero(elemVec, cellChunkSize * totDim * sizeof(PetscScalar)));
5007:     /* Get faces */
5008:     for (c = cS; c < cE; ++c) {
5009:       const PetscInt  cell = cells ? cells[c] : c;
5010:       const PetscInt *cone;
5011:       PetscCall(DMPlexGetCone(dm, cell, &cone));
5012:       faces[0 * cellChunkSize + (c - cS)] = cone[0];
5013:       /*faces[1*cellChunkSize+(c-cS)] = cone[1];*/
5014:     }
5015:     PetscCall(ISGeneralSetIndices(chunkIS, 1 * cellChunkSize, faces, PETSC_USE_POINTER));
5016:     /* Get geometric data */
5017:     if (maxDegree <= 1) {
5018:       if (!affineQuad) PetscCall(DMFieldCreateDefaultQuadrature(coordField, chunkIS, &affineQuad));
5019:       if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, affineQuad, PETSC_TRUE, &affineGeom));
5020:     } else {
5021:       for (f = 0; f < Nf; ++f) {
5022:         if (quads[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, quads[f], PETSC_TRUE, &geoms[f]));
5023:       }
5024:     }
5025:     /* Loop over fields */
5026:     for (f = 0; f < Nf; ++f) {
5027:       PetscFE         fe;
5028:       PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[f];
5029:       PetscFEGeom    *chunkGeom = NULL, *remGeom = NULL;
5030:       PetscQuadrature quad = affineQuad ? affineQuad : quads[f];
5031:       PetscInt        numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb;
5032:       PetscBool       isCohesiveField;

5034:       PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
5035:       if (!fe) continue;
5036:       PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5037:       PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
5038:       PetscCall(PetscFEGetDimension(fe, &Nb));
5039:       blockSize = Nb;
5040:       batchSize = numBlocks * blockSize;
5041:       PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5042:       numChunks = numCells / (numBatches * batchSize);
5043:       Ne        = numChunks * numBatches * batchSize;
5044:       Nr        = numCells % (numBatches * batchSize);
5045:       offset    = numCells - Nr;
5046:       PetscCall(PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom));
5047:       PetscCall(PetscFEGeomGetChunk(geom, offset, numCells, &remGeom));
5048:       PetscCall(PetscDSGetCohesive(ds, f, &isCohesiveField));
5049:       chunkGeom->isCohesive = remGeom->isCohesive = PETSC_TRUE;
5050:       key[0].field                                = f;
5051:       key[1].field                                = f;
5052:       key[2].field                                = f;
5053:       PetscCall(PetscFEIntegrateHybridResidual(ds, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, elemVec));
5054:       PetscCall(PetscFEIntegrateHybridResidual(ds, key[0], 0, Nr, remGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, dsAux[0], &a[0][offset * totDimAux[0]], t, &elemVec[offset * totDim]));
5055:       PetscCall(PetscFEIntegrateHybridResidual(ds, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, elemVec));
5056:       PetscCall(PetscFEIntegrateHybridResidual(ds, key[1], 1, Nr, remGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, dsAux[1], &a[1][offset * totDimAux[1]], t, &elemVec[offset * totDim]));
5057:       PetscCall(PetscFEIntegrateHybridResidual(ds, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, elemVec));
5058:       PetscCall(PetscFEIntegrateHybridResidual(ds, key[2], 2, Nr, remGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, dsAux[2], &a[2][offset * totDimAux[2]], t, &elemVec[offset * totDim]));
5059:       PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &remGeom));
5060:       PetscCall(PetscFEGeomRestoreChunk(geom, 0, offset, &chunkGeom));
5061:     }
5062:     /* Add elemVec to locX */
5063:     for (c = cS; c < cE; ++c) {
5064:       const PetscInt cell = cells ? cells[c] : c;
5065:       const PetscInt cind = c - cStart;

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

5071:         PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
5072:         if (ghostVal > 0) continue;
5073:       }
5074:       PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind * totDim], ADD_ALL_VALUES));
5075:     }
5076:   }
5077:   PetscCall(DMPlexRestoreCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
5078:   PetscCall(DMPlexRestoreHybridAuxFields(dmAux, dsAux, cellIS, locA, a));
5079:   PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
5080:   PetscCall(PetscFree(faces));
5081:   PetscCall(ISDestroy(&chunkIS));
5082:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
5083:   if (maxDegree <= 1) {
5084:     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
5085:     PetscCall(PetscQuadratureDestroy(&affineQuad));
5086:   } else {
5087:     for (f = 0; f < Nf; ++f) {
5088:       if (geoms) PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
5089:       if (quads) PetscCall(PetscQuadratureDestroy(&quads[f]));
5090:     }
5091:     PetscCall(PetscFree2(quads, geoms));
5092:   }
5093:   if (mesh->printFEM) {
5094:     Vec          locFbc;
5095:     PetscInt     pStart, pEnd, p, maxDof;
5096:     PetscScalar *zeroes;

5098:     PetscCall(VecDuplicate(locF, &locFbc));
5099:     PetscCall(VecCopy(locF, locFbc));
5100:     PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5101:     PetscCall(PetscSectionGetMaxDof(section, &maxDof));
5102:     PetscCall(PetscCalloc1(maxDof, &zeroes));
5103:     for (p = pStart; p < pEnd; p++) PetscCall(VecSetValuesSection(locFbc, section, p, zeroes, INSERT_BC_VALUES));
5104:     PetscCall(PetscFree(zeroes));
5105:     PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc));
5106:     PetscCall(VecDestroy(&locFbc));
5107:   }
5108:   PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
5109:   PetscFunctionReturn(PETSC_SUCCESS);
5110: }

5112: 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)
5113: {
5114:   DM_Plex        *mesh = (DM_Plex *)dm->data;
5115:   DM              plex = NULL, plexA = NULL, tdm;
5116:   DMEnclosureType encAux;
5117:   PetscDS         prob, probAux       = NULL;
5118:   PetscSection    section, sectionAux = NULL;
5119:   PetscSection    globalSection;
5120:   Vec             locA = NULL, tv;
5121:   PetscScalar    *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL;
5122:   PetscInt        v;
5123:   PetscInt        Nf, totDim, totDimAux = 0;
5124:   PetscBool       transform;

5126:   PetscFunctionBegin;
5127:   PetscCall(DMConvert(dm, DMPLEX, &plex));
5128:   PetscCall(DMHasBasisTransform(dm, &transform));
5129:   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
5130:   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
5131:   PetscCall(DMGetLocalSection(dm, &section));
5132:   PetscCall(DMGetDS(dm, &prob));
5133:   PetscCall(PetscDSGetNumFields(prob, &Nf));
5134:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
5135:   PetscCall(DMGetAuxiliaryVec(dm, label, values[0], 0, &locA));
5136:   if (locA) {
5137:     DM dmAux;

5139:     PetscCall(VecGetDM(locA, &dmAux));
5140:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
5141:     PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
5142:     PetscCall(DMGetDS(plexA, &probAux));
5143:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
5144:     PetscCall(DMGetLocalSection(plexA, &sectionAux));
5145:   }

5147:   PetscCall(DMGetGlobalSection(dm, &globalSection));
5148:   for (v = 0; v < numValues; ++v) {
5149:     PetscFEGeom    *fgeom;
5150:     PetscInt        maxDegree;
5151:     PetscQuadrature qGeom = NULL;
5152:     IS              pointIS;
5153:     const PetscInt *points;
5154:     PetscFormKey    key;
5155:     PetscInt        numFaces, face, Nq;

5157:     key.label = label;
5158:     key.value = values[v];
5159:     key.part  = 0;
5160:     PetscCall(DMLabelGetStratumIS(label, values[v], &pointIS));
5161:     if (!pointIS) continue; /* No points with that id on this process */
5162:     {
5163:       IS isectIS;

5165:       /* TODO: Special cases of ISIntersect where it is quick to check a prior if one is a superset of the other */
5166:       PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS));
5167:       PetscCall(ISDestroy(&pointIS));
5168:       pointIS = isectIS;
5169:     }
5170:     PetscCall(ISGetLocalSize(pointIS, &numFaces));
5171:     PetscCall(ISGetIndices(pointIS, &points));
5172:     PetscCall(PetscMalloc4(numFaces * totDim, &u, locX_t ? numFaces * totDim : 0, &u_t, numFaces * totDim * totDim, &elemMat, locA ? numFaces * totDimAux : 0, &a));
5173:     PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
5174:     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom));
5175:     if (!qGeom) {
5176:       PetscFE fe;

5178:       PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
5179:       PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
5180:       PetscCall(PetscObjectReference((PetscObject)qGeom));
5181:     }
5182:     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
5183:     PetscCall(DMSNESGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
5184:     for (face = 0; face < numFaces; ++face) {
5185:       const PetscInt point = points[face], *support;
5186:       PetscScalar   *x     = NULL;
5187:       PetscInt       i;

5189:       PetscCall(DMPlexGetSupport(dm, point, &support));
5190:       PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
5191:       for (i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
5192:       PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
5193:       if (locX_t) {
5194:         PetscCall(DMPlexVecGetClosure(plex, section, locX_t, support[0], NULL, &x));
5195:         for (i = 0; i < totDim; ++i) u_t[face * totDim + i] = x[i];
5196:         PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, support[0], NULL, &x));
5197:       }
5198:       if (locA) {
5199:         PetscInt subp;
5200:         PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
5201:         PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
5202:         for (i = 0; i < totDimAux; ++i) a[face * totDimAux + i] = x[i];
5203:         PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
5204:       }
5205:     }
5206:     PetscCall(PetscArrayzero(elemMat, numFaces * totDim * totDim));
5207:     {
5208:       PetscFE  fe;
5209:       PetscInt Nb;
5210:       /* Conforming batches */
5211:       PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
5212:       /* Remainder */
5213:       PetscFEGeom *chunkGeom = NULL;
5214:       PetscInt     fieldJ, Nr, offset;

5216:       PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
5217:       PetscCall(PetscFEGetDimension(fe, &Nb));
5218:       PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5219:       blockSize = Nb;
5220:       batchSize = numBlocks * blockSize;
5221:       PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5222:       numChunks = numFaces / (numBatches * batchSize);
5223:       Ne        = numChunks * numBatches * batchSize;
5224:       Nr        = numFaces % (numBatches * batchSize);
5225:       offset    = numFaces - Nr;
5226:       PetscCall(PetscFEGeomGetChunk(fgeom, 0, offset, &chunkGeom));
5227:       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5228:         key.field = fieldI * Nf + fieldJ;
5229:         PetscCall(PetscFEIntegrateBdJacobian(prob, wf, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat));
5230:       }
5231:       PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom));
5232:       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5233:         key.field = fieldI * Nf + fieldJ;
5234:         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]));
5235:       }
5236:       PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom));
5237:     }
5238:     for (face = 0; face < numFaces; ++face) {
5239:       const PetscInt point = points[face], *support;

5241:       /* Transform to global basis before insertion in Jacobian */
5242:       PetscCall(DMPlexGetSupport(plex, point, &support));
5243:       if (transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, support[0], PETSC_TRUE, totDim, &elemMat[face * totDim * totDim]));
5244:       if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(point, "BdJacobian", totDim, totDim, &elemMat[face * totDim * totDim]));
5245:       PetscCall(DMPlexMatSetClosure(plex, section, globalSection, JacP, support[0], &elemMat[face * totDim * totDim], ADD_VALUES));
5246:     }
5247:     PetscCall(DMSNESRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
5248:     PetscCall(PetscQuadratureDestroy(&qGeom));
5249:     PetscCall(ISRestoreIndices(pointIS, &points));
5250:     PetscCall(ISDestroy(&pointIS));
5251:     PetscCall(PetscFree4(u, u_t, elemMat, a));
5252:   }
5253:   if (plex) PetscCall(DMDestroy(&plex));
5254:   if (plexA) PetscCall(DMDestroy(&plexA));
5255:   PetscFunctionReturn(PETSC_SUCCESS);
5256: }

5258: 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)
5259: {
5260:   DMField  coordField;
5261:   DMLabel  depthLabel;
5262:   IS       facetIS;
5263:   PetscInt dim;

5265:   PetscFunctionBegin;
5266:   PetscCall(DMGetDimension(dm, &dim));
5267:   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5268:   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
5269:   PetscCall(DMGetCoordinateField(dm, &coordField));
5270:   PetscCall(DMPlexComputeBdJacobian_Single_Internal(dm, t, wf, label, numValues, values, field, locX, locX_t, X_tShift, Jac, JacP, coordField, facetIS));
5271:   PetscCall(ISDestroy(&facetIS));
5272:   PetscFunctionReturn(PETSC_SUCCESS);
5273: }

5275: PetscErrorCode DMPlexComputeBdJacobian_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, PetscReal X_tShift, Mat Jac, Mat JacP, void *user)
5276: {
5277:   PetscDS  prob;
5278:   PetscInt dim, numBd, bd;
5279:   DMLabel  depthLabel;
5280:   DMField  coordField = NULL;
5281:   IS       facetIS;

5283:   PetscFunctionBegin;
5284:   PetscCall(DMGetDS(dm, &prob));
5285:   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5286:   PetscCall(DMGetDimension(dm, &dim));
5287:   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
5288:   PetscCall(PetscDSGetNumBoundary(prob, &numBd));
5289:   PetscCall(DMGetCoordinateField(dm, &coordField));
5290:   for (bd = 0; bd < numBd; ++bd) {
5291:     PetscWeakForm           wf;
5292:     DMBoundaryConditionType type;
5293:     DMLabel                 label;
5294:     const PetscInt         *values;
5295:     PetscInt                fieldI, numValues;
5296:     PetscObject             obj;
5297:     PetscClassId            id;

5299:     PetscCall(PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &fieldI, NULL, NULL, NULL, NULL, NULL));
5300:     if (type & DM_BC_ESSENTIAL) continue;
5301:     PetscCall(PetscDSGetDiscretization(prob, fieldI, &obj));
5302:     PetscCall(PetscObjectGetClassId(obj, &id));
5303:     if (id != PETSCFE_CLASSID) continue;
5304:     PetscCall(DMPlexComputeBdJacobian_Single_Internal(dm, t, wf, label, numValues, values, fieldI, locX, locX_t, X_tShift, Jac, JacP, coordField, facetIS));
5305:   }
5306:   PetscCall(ISDestroy(&facetIS));
5307:   PetscFunctionReturn(PETSC_SUCCESS);
5308: }

5310: 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)
5311: {
5312:   DM_Plex        *mesh  = (DM_Plex *)dm->data;
5313:   const char     *name  = "Jacobian";
5314:   DM              dmAux = NULL, plex, tdm;
5315:   DMEnclosureType encAux;
5316:   Vec             A, tv;
5317:   DMField         coordField;
5318:   PetscDS         prob, probAux = NULL;
5319:   PetscSection    section, globalSection, sectionAux;
5320:   PetscScalar    *elemMat, *elemMatP, *elemMatD, *u, *u_t, *a = NULL;
5321:   const PetscInt *cells;
5322:   PetscInt        Nf, fieldI, fieldJ;
5323:   PetscInt        totDim, totDimAux = 0, cStart, cEnd, numCells, c;
5324:   PetscBool       hasJac = PETSC_FALSE, hasPrec = PETSC_FALSE, hasDyn, hasFV = PETSC_FALSE, transform;

5326:   PetscFunctionBegin;
5327:   if (!cellIS) goto end;
5328:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
5329:   PetscCall(ISGetLocalSize(cellIS, &numCells));
5330:   if (cStart >= cEnd) goto end;
5331:   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
5332:   PetscCall(DMHasBasisTransform(dm, &transform));
5333:   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
5334:   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
5335:   PetscCall(DMGetLocalSection(dm, &section));
5336:   PetscCall(DMGetGlobalSection(dm, &globalSection));
5337:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob));
5338:   PetscCall(PetscDSGetNumFields(prob, &Nf));
5339:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
5340:   PetscCall(PetscDSHasJacobian(prob, &hasJac));
5341:   PetscCall(PetscDSHasJacobianPreconditioner(prob, &hasPrec));
5342:   /* user passed in the same matrix, avoid double contributions and
5343:      only assemble the Jacobian */
5344:   if (hasJac && Jac == JacP) hasPrec = PETSC_FALSE;
5345:   PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
5346:   hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
5347:   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &A));
5348:   if (A) {
5349:     PetscCall(VecGetDM(A, &dmAux));
5350:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
5351:     PetscCall(DMConvert(dmAux, DMPLEX, &plex));
5352:     PetscCall(DMGetLocalSection(plex, &sectionAux));
5353:     PetscCall(DMGetDS(dmAux, &probAux));
5354:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
5355:   }
5356:   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));
5357:   if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
5358:   PetscCall(DMGetCoordinateField(dm, &coordField));
5359:   for (c = cStart; c < cEnd; ++c) {
5360:     const PetscInt cell = cells ? cells[c] : c;
5361:     const PetscInt cind = c - cStart;
5362:     PetscScalar   *x = NULL, *x_t = NULL;
5363:     PetscInt       i;

5365:     PetscCall(DMPlexVecGetClosure(dm, section, X, cell, NULL, &x));
5366:     for (i = 0; i < totDim; ++i) u[cind * totDim + i] = x[i];
5367:     PetscCall(DMPlexVecRestoreClosure(dm, section, X, cell, NULL, &x));
5368:     if (X_t) {
5369:       PetscCall(DMPlexVecGetClosure(dm, section, X_t, cell, NULL, &x_t));
5370:       for (i = 0; i < totDim; ++i) u_t[cind * totDim + i] = x_t[i];
5371:       PetscCall(DMPlexVecRestoreClosure(dm, section, X_t, cell, NULL, &x_t));
5372:     }
5373:     if (dmAux) {
5374:       PetscInt subcell;
5375:       PetscCall(DMGetEnclosurePoint(dmAux, dm, encAux, cell, &subcell));
5376:       PetscCall(DMPlexVecGetClosure(plex, sectionAux, A, subcell, NULL, &x));
5377:       for (i = 0; i < totDimAux; ++i) a[cind * totDimAux + i] = x[i];
5378:       PetscCall(DMPlexVecRestoreClosure(plex, sectionAux, A, subcell, NULL, &x));
5379:     }
5380:   }
5381:   if (hasJac) PetscCall(PetscArrayzero(elemMat, numCells * totDim * totDim));
5382:   if (hasPrec) PetscCall(PetscArrayzero(elemMatP, numCells * totDim * totDim));
5383:   if (hasDyn) PetscCall(PetscArrayzero(elemMatD, numCells * totDim * totDim));
5384:   for (fieldI = 0; fieldI < Nf; ++fieldI) {
5385:     PetscClassId    id;
5386:     PetscFE         fe;
5387:     PetscQuadrature qGeom = NULL;
5388:     PetscInt        Nb;
5389:     /* Conforming batches */
5390:     PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
5391:     /* Remainder */
5392:     PetscInt     Nr, offset, Nq;
5393:     PetscInt     maxDegree;
5394:     PetscFEGeom *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL;

5396:     PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
5397:     PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
5398:     if (id == PETSCFV_CLASSID) {
5399:       hasFV = PETSC_TRUE;
5400:       continue;
5401:     }
5402:     PetscCall(PetscFEGetDimension(fe, &Nb));
5403:     PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5404:     PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
5405:     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
5406:     if (!qGeom) {
5407:       PetscCall(PetscFEGetQuadrature(fe, &qGeom));
5408:       PetscCall(PetscObjectReference((PetscObject)qGeom));
5409:     }
5410:     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
5411:     PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
5412:     blockSize = Nb;
5413:     batchSize = numBlocks * blockSize;
5414:     PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5415:     numChunks = numCells / (numBatches * batchSize);
5416:     Ne        = numChunks * numBatches * batchSize;
5417:     Nr        = numCells % (numBatches * batchSize);
5418:     offset    = numCells - Nr;
5419:     PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
5420:     PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &remGeom));
5421:     for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5422:       key.field = fieldI * Nf + fieldJ;
5423:       if (hasJac) {
5424:         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat));
5425:         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]));
5426:       }
5427:       if (hasPrec) {
5428:         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatP));
5429:         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]));
5430:       }
5431:       if (hasDyn) {
5432:         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatD));
5433:         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]));
5434:       }
5435:     }
5436:     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &remGeom));
5437:     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, 0, offset, &chunkGeom));
5438:     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
5439:     PetscCall(PetscQuadratureDestroy(&qGeom));
5440:   }
5441:   /*   Add contribution from X_t */
5442:   if (hasDyn) {
5443:     for (c = 0; c < numCells * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
5444:   }
5445:   if (hasFV) {
5446:     PetscClassId id;
5447:     PetscFV      fv;
5448:     PetscInt     offsetI, NcI, NbI = 1, fc, f;

5450:     for (fieldI = 0; fieldI < Nf; ++fieldI) {
5451:       PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fv));
5452:       PetscCall(PetscDSGetFieldOffset(prob, fieldI, &offsetI));
5453:       PetscCall(PetscObjectGetClassId((PetscObject)fv, &id));
5454:       if (id != PETSCFV_CLASSID) continue;
5455:       /* Put in the identity */
5456:       PetscCall(PetscFVGetNumComponents(fv, &NcI));
5457:       for (c = cStart; c < cEnd; ++c) {
5458:         const PetscInt cind    = c - cStart;
5459:         const PetscInt eOffset = cind * totDim * totDim;
5460:         for (fc = 0; fc < NcI; ++fc) {
5461:           for (f = 0; f < NbI; ++f) {
5462:             const PetscInt i = offsetI + f * NcI + fc;
5463:             if (hasPrec) {
5464:               if (hasJac) elemMat[eOffset + i * totDim + i] = 1.0;
5465:               elemMatP[eOffset + i * totDim + i] = 1.0;
5466:             } else {
5467:               elemMat[eOffset + i * totDim + i] = 1.0;
5468:             }
5469:           }
5470:         }
5471:       }
5472:     }
5473:     /* No allocated space for FV stuff, so ignore the zero entries */
5474:     PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE));
5475:   }
5476:   /* Insert values into matrix */
5477:   for (c = cStart; c < cEnd; ++c) {
5478:     const PetscInt cell = cells ? cells[c] : c;
5479:     const PetscInt cind = c - cStart;

5481:     /* Transform to global basis before insertion in Jacobian */
5482:     if (transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, cell, PETSC_TRUE, totDim, &elemMat[cind * totDim * totDim]));
5483:     if (hasPrec) {
5484:       if (hasJac) {
5485:         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
5486:         PetscCall(DMPlexMatSetClosure(dm, section, globalSection, Jac, cell, &elemMat[cind * totDim * totDim], ADD_VALUES));
5487:       }
5488:       if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatP[cind * totDim * totDim]));
5489:       PetscCall(DMPlexMatSetClosure(dm, section, globalSection, JacP, cell, &elemMatP[cind * totDim * totDim], ADD_VALUES));
5490:     } else {
5491:       if (hasJac) {
5492:         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
5493:         PetscCall(DMPlexMatSetClosure(dm, section, globalSection, JacP, cell, &elemMat[cind * totDim * totDim], ADD_VALUES));
5494:       }
5495:     }
5496:   }
5497:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
5498:   if (hasFV) PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE));
5499:   PetscCall(PetscFree5(u, u_t, elemMat, elemMatP, elemMatD));
5500:   if (dmAux) {
5501:     PetscCall(PetscFree(a));
5502:     PetscCall(DMDestroy(&plex));
5503:   }
5504:   /* Compute boundary integrals */
5505:   PetscCall(DMPlexComputeBdJacobian_Internal(dm, X, X_t, t, X_tShift, Jac, JacP, user));
5506:   /* Assemble matrix */
5507: end : {
5508:   PetscBool assOp = hasJac && hasPrec ? PETSC_TRUE : PETSC_FALSE, gassOp;

5510:   PetscCallMPI(MPI_Allreduce(&assOp, &gassOp, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
5511:   if (hasJac && hasPrec) {
5512:     PetscCall(MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY));
5513:     PetscCall(MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY));
5514:   }
5515: }
5516:   PetscCall(MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY));
5517:   PetscCall(MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY));
5518:   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
5519:   PetscFunctionReturn(PETSC_SUCCESS);
5520: }

5522: 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)
5523: {
5524:   DM_Plex        *mesh          = (DM_Plex *)dm->data;
5525:   const char     *name          = "Hybrid Jacobian";
5526:   DM              dmAux[3]      = {NULL, NULL, NULL};
5527:   DMLabel         ghostLabel    = NULL;
5528:   DM              plex          = NULL;
5529:   DM              plexA         = NULL;
5530:   PetscDS         ds            = NULL;
5531:   PetscDS         dsAux[3]      = {NULL, NULL, NULL};
5532:   Vec             locA[3]       = {NULL, NULL, NULL};
5533:   PetscSection    section       = NULL;
5534:   PetscSection    sectionAux[3] = {NULL, NULL, NULL};
5535:   DMField         coordField    = NULL;
5536:   PetscScalar    *u             = NULL, *u_t, *a[3];
5537:   PetscScalar    *elemMat, *elemMatP;
5538:   PetscSection    globalSection;
5539:   IS              chunkIS;
5540:   const PetscInt *cells;
5541:   PetscInt       *faces;
5542:   PetscInt        cStart, cEnd, numCells;
5543:   PetscInt        Nf, fieldI, fieldJ, totDim, totDimAux[3], numChunks, cellChunkSize, chunk;
5544:   PetscInt        maxDegree  = PETSC_MAX_INT;
5545:   PetscQuadrature affineQuad = NULL, *quads = NULL;
5546:   PetscFEGeom    *affineGeom = NULL, **geoms = NULL;
5547:   PetscBool       hasBdJac, hasBdPrec;

5549:   PetscFunctionBegin;
5550:   if (!cellIS) PetscFunctionReturn(PETSC_SUCCESS);
5551:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
5552:   PetscCall(ISGetLocalSize(cellIS, &numCells));
5553:   if (cStart >= cEnd) PetscFunctionReturn(PETSC_SUCCESS);
5554:   if ((key[0].label == key[1].label) && (key[0].value == key[1].value) && (key[0].part == key[1].part)) {
5555:     const char *name;
5556:     PetscCall(PetscObjectGetName((PetscObject)key[0].label, &name));
5557:     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);
5558:   }
5559:   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
5560:   PetscCall(DMConvert(dm, DMPLEX, &plex));
5561:   PetscCall(DMGetSection(dm, &section));
5562:   PetscCall(DMGetGlobalSection(dm, &globalSection));
5563:   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
5564:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds));
5565:   PetscCall(PetscDSGetNumFields(ds, &Nf));
5566:   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
5567:   PetscCall(PetscDSHasBdJacobian(ds, &hasBdJac));
5568:   PetscCall(PetscDSHasBdJacobianPreconditioner(ds, &hasBdPrec));
5569:   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, key[2].value, key[2].part, &locA[2]));
5570:   if (locA[2]) {
5571:     const PetscInt cellStart = cells ? cells[cStart] : cStart;

5573:     PetscCall(VecGetDM(locA[2], &dmAux[2]));
5574:     PetscCall(DMConvert(dmAux[2], DMPLEX, &plexA));
5575:     PetscCall(DMGetSection(dmAux[2], &sectionAux[2]));
5576:     PetscCall(DMGetCellDS(dmAux[2], cellStart, &dsAux[2]));
5577:     PetscCall(PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]));
5578:     {
5579:       const PetscInt *cone;
5580:       PetscInt        c;

5582:       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
5583:       for (c = 0; c < 2; ++c) {
5584:         const PetscInt *support;
5585:         PetscInt        ssize, s;

5587:         PetscCall(DMPlexGetSupport(dm, cone[c], &support));
5588:         PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
5589:         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);
5590:         if (support[0] == cellStart) s = 1;
5591:         else if (support[1] == cellStart) s = 0;
5592:         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
5593:         PetscCall(DMGetAuxiliaryVec(dm, key[c].label, key[c].value, key[c].part, &locA[c]));
5594:         if (locA[c]) PetscCall(VecGetDM(locA[c], &dmAux[c]));
5595:         else dmAux[c] = dmAux[2];
5596:         PetscCall(DMGetCellDS(dmAux[c], support[s], &dsAux[c]));
5597:         PetscCall(PetscDSGetTotalDimension(dsAux[c], &totDimAux[c]));
5598:       }
5599:     }
5600:   }
5601:   PetscCall(DMGetCoordinateField(dm, &coordField));
5602:   PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
5603:   if (maxDegree > 1) {
5604:     PetscInt f;
5605:     PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
5606:     for (f = 0; f < Nf; ++f) {
5607:       PetscFE fe;

5609:       PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
5610:       if (fe) {
5611:         PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
5612:         PetscCall(PetscObjectReference((PetscObject)quads[f]));
5613:       }
5614:     }
5615:   }
5616:   cellChunkSize = numCells;
5617:   numChunks     = !numCells ? 0 : PetscCeilReal(((PetscReal)numCells) / cellChunkSize);
5618:   PetscCall(PetscCalloc1(1 * cellChunkSize, &faces));
5619:   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 1 * cellChunkSize, faces, PETSC_USE_POINTER, &chunkIS));
5620:   PetscCall(DMPlexGetCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
5621:   PetscCall(DMPlexGetHybridAuxFields(dm, dmAux, dsAux, cellIS, locA, a));
5622:   PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMat));
5623:   PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatP));
5624:   for (chunk = 0; chunk < numChunks; ++chunk) {
5625:     PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;

5627:     if (hasBdJac) PetscCall(PetscMemzero(elemMat, numCells * totDim * totDim * sizeof(PetscScalar)));
5628:     if (hasBdPrec) PetscCall(PetscMemzero(elemMatP, numCells * totDim * totDim * sizeof(PetscScalar)));
5629:     /* Get faces */
5630:     for (c = cS; c < cE; ++c) {
5631:       const PetscInt  cell = cells ? cells[c] : c;
5632:       const PetscInt *cone;
5633:       PetscCall(DMPlexGetCone(plex, cell, &cone));
5634:       faces[0 * cellChunkSize + (c - cS)] = cone[0];
5635:       /*faces[2*cellChunkSize+(c-cS)] = cone[1];*/
5636:     }
5637:     PetscCall(ISGeneralSetIndices(chunkIS, 1 * cellChunkSize, faces, PETSC_USE_POINTER));
5638:     if (maxDegree <= 1) {
5639:       if (!affineQuad) PetscCall(DMFieldCreateDefaultQuadrature(coordField, chunkIS, &affineQuad));
5640:       if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, affineQuad, PETSC_TRUE, &affineGeom));
5641:     } else {
5642:       PetscInt f;
5643:       for (f = 0; f < Nf; ++f) {
5644:         if (quads[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, quads[f], PETSC_TRUE, &geoms[f]));
5645:       }
5646:     }

5648:     for (fieldI = 0; fieldI < Nf; ++fieldI) {
5649:       PetscFE         feI;
5650:       PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[fieldI];
5651:       PetscFEGeom    *chunkGeom = NULL, *remGeom = NULL;
5652:       PetscQuadrature quad = affineQuad ? affineQuad : quads[fieldI];
5653:       PetscInt        numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb;
5654:       PetscBool       isCohesiveField;

5656:       PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&feI));
5657:       if (!feI) continue;
5658:       PetscCall(PetscFEGetTileSizes(feI, NULL, &numBlocks, NULL, &numBatches));
5659:       PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
5660:       PetscCall(PetscFEGetDimension(feI, &Nb));
5661:       blockSize = Nb;
5662:       batchSize = numBlocks * blockSize;
5663:       PetscCall(PetscFESetTileSizes(feI, blockSize, numBlocks, batchSize, numBatches));
5664:       numChunks = numCells / (numBatches * batchSize);
5665:       Ne        = numChunks * numBatches * batchSize;
5666:       Nr        = numCells % (numBatches * batchSize);
5667:       offset    = numCells - Nr;
5668:       PetscCall(PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom));
5669:       PetscCall(PetscFEGeomGetChunk(geom, offset, numCells, &remGeom));
5670:       PetscCall(PetscDSGetCohesive(ds, fieldI, &isCohesiveField));
5671:       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5672:         PetscFE feJ;

5674:         PetscCall(PetscDSGetDiscretization(ds, fieldJ, (PetscObject *)&feJ));
5675:         if (!feJ) continue;
5676:         key[0].field = fieldI * Nf + fieldJ;
5677:         key[1].field = fieldI * Nf + fieldJ;
5678:         key[2].field = fieldI * Nf + fieldJ;
5679:         if (hasBdJac) {
5680:           PetscCall(PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, X_tShift, elemMat));
5681:           PetscCall(PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN, key[0], 0, Nr, remGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, dsAux[0], &a[0][offset * totDimAux[0]], t, X_tShift, &elemMat[offset * totDim * totDim]));
5682:           PetscCall(PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, X_tShift, elemMat));
5683:           PetscCall(PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN, key[1], 1, Nr, remGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, dsAux[1], &a[1][offset * totDimAux[1]], t, X_tShift, &elemMat[offset * totDim * totDim]));
5684:         }
5685:         if (hasBdPrec) {
5686:           PetscCall(PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN_PRE, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, X_tShift, elemMatP));
5687:           PetscCall(PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN_PRE, key[0], 0, Nr, remGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, dsAux[0], &a[0][offset * totDimAux[0]], t, X_tShift, &elemMatP[offset * totDim * totDim]));
5688:           PetscCall(PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN_PRE, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, X_tShift, elemMatP));
5689:           PetscCall(PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN_PRE, key[1], 1, Nr, remGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, dsAux[1], &a[1][offset * totDimAux[1]], t, X_tShift, &elemMatP[offset * totDim * totDim]));
5690:         }
5691:         if (hasBdJac) {
5692:           PetscCall(PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, X_tShift, elemMat));
5693:           PetscCall(PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN, key[2], 2, Nr, remGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, dsAux[2], &a[2][offset * totDimAux[2]], t, X_tShift, &elemMat[offset * totDim * totDim]));
5694:         }
5695:         if (hasBdPrec) {
5696:           PetscCall(PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN_PRE, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, X_tShift, elemMatP));
5697:           PetscCall(PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN_PRE, key[2], 2, Nr, remGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, dsAux[2], &a[2][offset * totDimAux[2]], t, X_tShift, &elemMatP[offset * totDim * totDim]));
5698:         }
5699:       }
5700:       PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &remGeom));
5701:       PetscCall(PetscFEGeomRestoreChunk(geom, 0, offset, &chunkGeom));
5702:     }
5703:     /* Insert values into matrix */
5704:     for (c = cS; c < cE; ++c) {
5705:       const PetscInt cell = cells ? cells[c] : c;
5706:       const PetscInt cind = c - cS;

5708:       if (hasBdPrec) {
5709:         if (hasBdJac) {
5710:           if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
5711:           PetscCall(DMPlexMatSetClosure(plex, section, globalSection, Jac, cell, &elemMat[cind * totDim * totDim], ADD_VALUES));
5712:         }
5713:         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatP[cind * totDim * totDim]));
5714:         PetscCall(DMPlexMatSetClosure(plex, section, globalSection, JacP, cell, &elemMatP[cind * totDim * totDim], ADD_VALUES));
5715:       } else if (hasBdJac) {
5716:         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
5717:         PetscCall(DMPlexMatSetClosure(plex, section, globalSection, JacP, cell, &elemMat[cind * totDim * totDim], ADD_VALUES));
5718:       }
5719:     }
5720:   }
5721:   PetscCall(DMPlexRestoreCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
5722:   PetscCall(DMPlexRestoreHybridAuxFields(dmAux, dsAux, cellIS, locA, a));
5723:   PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMat));
5724:   PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatP));
5725:   PetscCall(PetscFree(faces));
5726:   PetscCall(ISDestroy(&chunkIS));
5727:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
5728:   if (maxDegree <= 1) {
5729:     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
5730:     PetscCall(PetscQuadratureDestroy(&affineQuad));
5731:   } else {
5732:     PetscInt f;
5733:     for (f = 0; f < Nf; ++f) {
5734:       if (geoms) PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
5735:       if (quads) PetscCall(PetscQuadratureDestroy(&quads[f]));
5736:     }
5737:     PetscCall(PetscFree2(quads, geoms));
5738:   }
5739:   if (dmAux[2]) PetscCall(DMDestroy(&plexA));
5740:   PetscCall(DMDestroy(&plex));
5741:   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
5742:   PetscFunctionReturn(PETSC_SUCCESS);
5743: }

5745: /*
5746:   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.

5748:   Input Parameters:
5749: + dm     - The mesh
5750: . key    - The PetscWeakFormKey indcating where integration should happen
5751: . cellIS - The cells to integrate over
5752: . t      - The time
5753: . X_tShift - The multiplier for the Jacobian with repsect to X_t
5754: . X      - Local solution vector
5755: . X_t    - Time-derivative of the local solution vector
5756: . Y      - Local input vector
5757: - user   - the user context

5759:   Output Parameter:
5760: . Z - Local output vector

5762:   Note:
5763:   We form the residual one batch of elements at a time. This allows us to offload work onto an accelerator,
5764:   like a GPU, or vectorize on a multicore machine.
5765: */
5766: 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)
5767: {
5768:   DM_Plex        *mesh  = (DM_Plex *)dm->data;
5769:   const char     *name  = "Jacobian";
5770:   DM              dmAux = NULL, plex, plexAux = NULL;
5771:   DMEnclosureType encAux;
5772:   Vec             A;
5773:   DMField         coordField;
5774:   PetscDS         prob, probAux = NULL;
5775:   PetscQuadrature quad;
5776:   PetscSection    section, globalSection, sectionAux;
5777:   PetscScalar    *elemMat, *elemMatD, *u, *u_t, *a = NULL, *y, *z;
5778:   const PetscInt *cells;
5779:   PetscInt        Nf, fieldI, fieldJ;
5780:   PetscInt        totDim, totDimAux = 0, cStart, cEnd, numCells, c;
5781:   PetscBool       hasDyn;

5783:   PetscFunctionBegin;
5784:   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
5785:   PetscCall(DMConvert(dm, DMPLEX, &plex));
5786:   if (!cellIS) {
5787:     PetscInt depth;

5789:     PetscCall(DMPlexGetDepth(plex, &depth));
5790:     PetscCall(DMGetStratumIS(plex, "dim", depth, &cellIS));
5791:     if (!cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, &cellIS));
5792:   } else {
5793:     PetscCall(PetscObjectReference((PetscObject)cellIS));
5794:   }
5795:   PetscCall(ISGetLocalSize(cellIS, &numCells));
5796:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
5797:   PetscCall(DMGetLocalSection(dm, &section));
5798:   PetscCall(DMGetGlobalSection(dm, &globalSection));
5799:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob));
5800:   PetscCall(PetscDSGetNumFields(prob, &Nf));
5801:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
5802:   PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
5803:   hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
5804:   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &A));
5805:   if (A) {
5806:     PetscCall(VecGetDM(A, &dmAux));
5807:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
5808:     PetscCall(DMConvert(dmAux, DMPLEX, &plexAux));
5809:     PetscCall(DMGetLocalSection(plexAux, &sectionAux));
5810:     PetscCall(DMGetDS(dmAux, &probAux));
5811:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
5812:   }
5813:   PetscCall(VecSet(Z, 0.0));
5814:   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));
5815:   if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
5816:   PetscCall(DMGetCoordinateField(dm, &coordField));
5817:   for (c = cStart; c < cEnd; ++c) {
5818:     const PetscInt cell = cells ? cells[c] : c;
5819:     const PetscInt cind = c - cStart;
5820:     PetscScalar   *x = NULL, *x_t = NULL;
5821:     PetscInt       i;

5823:     PetscCall(DMPlexVecGetClosure(plex, section, X, cell, NULL, &x));
5824:     for (i = 0; i < totDim; ++i) u[cind * totDim + i] = x[i];
5825:     PetscCall(DMPlexVecRestoreClosure(plex, section, X, cell, NULL, &x));
5826:     if (X_t) {
5827:       PetscCall(DMPlexVecGetClosure(plex, section, X_t, cell, NULL, &x_t));
5828:       for (i = 0; i < totDim; ++i) u_t[cind * totDim + i] = x_t[i];
5829:       PetscCall(DMPlexVecRestoreClosure(plex, section, X_t, cell, NULL, &x_t));
5830:     }
5831:     if (dmAux) {
5832:       PetscInt subcell;
5833:       PetscCall(DMGetEnclosurePoint(dmAux, dm, encAux, cell, &subcell));
5834:       PetscCall(DMPlexVecGetClosure(plexAux, sectionAux, A, subcell, NULL, &x));
5835:       for (i = 0; i < totDimAux; ++i) a[cind * totDimAux + i] = x[i];
5836:       PetscCall(DMPlexVecRestoreClosure(plexAux, sectionAux, A, subcell, NULL, &x));
5837:     }
5838:     PetscCall(DMPlexVecGetClosure(plex, section, Y, cell, NULL, &x));
5839:     for (i = 0; i < totDim; ++i) y[cind * totDim + i] = x[i];
5840:     PetscCall(DMPlexVecRestoreClosure(plex, section, Y, cell, NULL, &x));
5841:   }
5842:   PetscCall(PetscArrayzero(elemMat, numCells * totDim * totDim));
5843:   if (hasDyn) PetscCall(PetscArrayzero(elemMatD, numCells * totDim * totDim));
5844:   for (fieldI = 0; fieldI < Nf; ++fieldI) {
5845:     PetscFE  fe;
5846:     PetscInt Nb;
5847:     /* Conforming batches */
5848:     PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
5849:     /* Remainder */
5850:     PetscInt        Nr, offset, Nq;
5851:     PetscQuadrature qGeom = NULL;
5852:     PetscInt        maxDegree;
5853:     PetscFEGeom    *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL;

5855:     PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
5856:     PetscCall(PetscFEGetQuadrature(fe, &quad));
5857:     PetscCall(PetscFEGetDimension(fe, &Nb));
5858:     PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5859:     PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
5860:     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
5861:     if (!qGeom) {
5862:       PetscCall(PetscFEGetQuadrature(fe, &qGeom));
5863:       PetscCall(PetscObjectReference((PetscObject)qGeom));
5864:     }
5865:     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
5866:     PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
5867:     blockSize = Nb;
5868:     batchSize = numBlocks * blockSize;
5869:     PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5870:     numChunks = numCells / (numBatches * batchSize);
5871:     Ne        = numChunks * numBatches * batchSize;
5872:     Nr        = numCells % (numBatches * batchSize);
5873:     offset    = numCells - Nr;
5874:     PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
5875:     PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &remGeom));
5876:     for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5877:       key.field = fieldI * Nf + fieldJ;
5878:       PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat));
5879:       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]));
5880:       if (hasDyn) {
5881:         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatD));
5882:         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]));
5883:       }
5884:     }
5885:     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &remGeom));
5886:     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, 0, offset, &chunkGeom));
5887:     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
5888:     PetscCall(PetscQuadratureDestroy(&qGeom));
5889:   }
5890:   if (hasDyn) {
5891:     for (c = 0; c < numCells * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
5892:   }
5893:   for (c = cStart; c < cEnd; ++c) {
5894:     const PetscInt     cell = cells ? cells[c] : c;
5895:     const PetscInt     cind = c - cStart;
5896:     const PetscBLASInt M = totDim, one = 1;
5897:     const PetscScalar  a = 1.0, b = 0.0;

5899:     PetscCallBLAS("BLASgemv", BLASgemv_("N", &M, &M, &a, &elemMat[cind * totDim * totDim], &M, &y[cind * totDim], &one, &b, z, &one));
5900:     if (mesh->printFEM > 1) {
5901:       PetscCall(DMPrintCellMatrix(c, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
5902:       PetscCall(DMPrintCellVector(c, "Y", totDim, &y[cind * totDim]));
5903:       PetscCall(DMPrintCellVector(c, "Z", totDim, z));
5904:     }
5905:     PetscCall(DMPlexVecSetClosure(dm, section, Z, cell, z, ADD_VALUES));
5906:   }
5907:   PetscCall(PetscFree6(u, u_t, elemMat, elemMatD, y, z));
5908:   if (mesh->printFEM) {
5909:     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)Z), "Z:\n"));
5910:     PetscCall(VecView(Z, NULL));
5911:   }
5912:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
5913:   PetscCall(PetscFree(a));
5914:   PetscCall(ISDestroy(&cellIS));
5915:   PetscCall(DMDestroy(&plexAux));
5916:   PetscCall(DMDestroy(&plex));
5917:   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
5918:   PetscFunctionReturn(PETSC_SUCCESS);
5919: }