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:   PetscObjectTypeCompare((PetscObject)dm, DMPLEX, &isPlex);
 24:   if (isPlex) {
 25:     *plex = dm;
 26:     PetscObjectReference((PetscObject)dm);
 27:   } else {
 28:     PetscObjectQuery((PetscObject)dm, "dm_plex", (PetscObject *)plex);
 29:     if (!*plex) {
 30:       DMConvert(dm, DMPLEX, plex);
 31:       PetscObjectCompose((PetscObject)dm, "dm_plex", (PetscObject)*plex);
 32:       if (copy) {
 33:         DMSubDomainHookLink link;

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

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

 52:   PetscFEGeomDestroy(&geom);
 53:   return 0;
 54: }

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

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

 78: static PetscErrorCode DMPlexRestoreFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
 79: {
 80:   *geom = NULL;
 81:   return 0;
 82: }

 84: /*@
 85:   DMPlexGetScale - Get the scale for the specified fundamental unit

 87:   Not collective

 89:   Input Parameters:
 90: + dm   - the DM
 91: - unit - The SI unit

 93:   Output Parameter:
 94: . scale - The value used to scale all quantities with this unit

 96:   Level: advanced

 98: .seealso: `DMPlexSetScale()`, `PetscUnit`
 99: @*/
100: PetscErrorCode DMPlexGetScale(DM dm, PetscUnit unit, PetscReal *scale)
101: {
102:   DM_Plex *mesh = (DM_Plex *)dm->data;

106:   *scale = mesh->scale[unit];
107:   return 0;
108: }

110: /*@
111:   DMPlexSetScale - Set the scale for the specified fundamental unit

113:   Not collective

115:   Input Parameters:
116: + dm   - the DM
117: . unit - The SI unit
118: - scale - The value used to scale all quantities with this unit

120:   Level: advanced

122: .seealso: `DMPlexGetScale()`, `PetscUnit`
123: @*/
124: PetscErrorCode DMPlexSetScale(DM dm, PetscUnit unit, PetscReal scale)
125: {
126:   DM_Plex *mesh = (DM_Plex *)dm->data;

129:   mesh->scale[unit] = scale;
130:   return 0;
131: }

133: static PetscErrorCode DMPlexProjectRigidBody_Private(PetscInt dim, PetscReal t, const PetscReal X[], PetscInt Nc, PetscScalar *mode, void *ctx)
134: {
135:   const PetscInt eps[3][3][3] = {
136:     {{0, 0, 0},  {0, 0, 1},  {0, -1, 0}},
137:     {{0, 0, -1}, {0, 0, 0},  {1, 0, 0} },
138:     {{0, 1, 0},  {-1, 0, 0}, {0, 0, 0} }
139:   };
140:   PetscInt *ctxInt = (PetscInt *)ctx;
141:   PetscInt  dim2   = ctxInt[0];
142:   PetscInt  d      = ctxInt[1];
143:   PetscInt  i, j, k = dim > 2 ? d - dim : d;

146:   for (i = 0; i < dim; i++) mode[i] = 0.;
147:   if (d < dim) {
148:     mode[d] = 1.; /* Translation along axis d */
149:   } else {
150:     for (i = 0; i < dim; i++) {
151:       for (j = 0; j < dim; j++) { mode[j] += eps[i][j][k] * X[i]; /* Rotation about axis d */ }
152:     }
153:   }
154:   return 0;
155: }

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

160:   Collective on dm

162:   Input Parameters:
163: + dm - the DM
164: - field - The field number for the rigid body space, or 0 for the default

166:   Output Parameter:
167: . sp - the null space

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

171:   Level: advanced

173: .seealso: `MatNullSpaceCreate()`, `PCGAMG`
174: @*/
175: PetscErrorCode DMPlexCreateRigidBody(DM dm, PetscInt field, MatNullSpace *sp)
176: {
177:   PetscErrorCode (**func)(PetscInt, PetscReal, const PetscReal *, PetscInt, PetscScalar *, void *);
178:   MPI_Comm     comm;
179:   Vec          mode[6];
180:   PetscSection section, globalSection;
181:   PetscInt     dim, dimEmbed, Nf, n, m, mmin, d, i, j;

183:   PetscObjectGetComm((PetscObject)dm, &comm);
184:   DMGetDimension(dm, &dim);
185:   DMGetCoordinateDim(dm, &dimEmbed);
186:   DMGetNumFields(dm, &Nf);
188:   if (dim == 1 && Nf < 2) {
189:     MatNullSpaceCreate(comm, PETSC_TRUE, 0, NULL, sp);
190:     return 0;
191:   }
192:   DMGetLocalSection(dm, &section);
193:   DMGetGlobalSection(dm, &globalSection);
194:   PetscSectionGetConstrainedStorageSize(globalSection, &n);
195:   PetscCalloc1(Nf, &func);
196:   m = (dim * (dim + 1)) / 2;
197:   VecCreate(comm, &mode[0]);
198:   VecSetType(mode[0], dm->vectype);
199:   VecSetSizes(mode[0], n, PETSC_DETERMINE);
200:   VecSetUp(mode[0]);
201:   VecGetSize(mode[0], &n);
202:   mmin        = PetscMin(m, n);
203:   func[field] = DMPlexProjectRigidBody_Private;
204:   for (i = 1; i < m; ++i) VecDuplicate(mode[0], &mode[i]);
205:   for (d = 0; d < m; d++) {
206:     PetscInt ctx[2];
207:     void    *voidctx = (void *)(&ctx[0]);

209:     ctx[0] = dimEmbed;
210:     ctx[1] = d;
211:     DMProjectFunction(dm, 0.0, func, &voidctx, INSERT_VALUES, mode[d]);
212:   }
213:   /* Orthonormalize system */
214:   for (i = 0; i < mmin; ++i) {
215:     PetscScalar dots[6];

217:     VecNormalize(mode[i], NULL);
218:     VecMDot(mode[i], mmin - i - 1, mode + i + 1, dots + i + 1);
219:     for (j = i + 1; j < mmin; ++j) {
220:       dots[j] *= -1.0;
221:       VecAXPY(mode[j], dots[j], mode[i]);
222:     }
223:   }
224:   MatNullSpaceCreate(comm, PETSC_FALSE, mmin, mode, sp);
225:   for (i = 0; i < m; ++i) VecDestroy(&mode[i]);
226:   PetscFree(func);
227:   return 0;
228: }

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

233:   Collective on dm

235:   Input Parameters:
236: + dm    - the DM
237: . nb    - The number of bodies
238: . label - The DMLabel marking each domain
239: . nids  - The number of ids per body
240: - ids   - An array of the label ids in sequence for each domain

242:   Output Parameter:
243: . sp - the null space

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

247:   Level: advanced

249: .seealso: `MatNullSpaceCreate()`
250: @*/
251: PetscErrorCode DMPlexCreateRigidBodies(DM dm, PetscInt nb, DMLabel label, const PetscInt nids[], const PetscInt ids[], MatNullSpace *sp)
252: {
253:   MPI_Comm     comm;
254:   PetscSection section, globalSection;
255:   Vec         *mode;
256:   PetscScalar *dots;
257:   PetscInt     dim, dimEmbed, n, m, b, d, i, j, off;

259:   PetscObjectGetComm((PetscObject)dm, &comm);
260:   DMGetDimension(dm, &dim);
261:   DMGetCoordinateDim(dm, &dimEmbed);
262:   DMGetLocalSection(dm, &section);
263:   DMGetGlobalSection(dm, &globalSection);
264:   PetscSectionGetConstrainedStorageSize(globalSection, &n);
265:   m = nb * (dim * (dim + 1)) / 2;
266:   PetscMalloc2(m, &mode, m, &dots);
267:   VecCreate(comm, &mode[0]);
268:   VecSetSizes(mode[0], n, PETSC_DETERMINE);
269:   VecSetUp(mode[0]);
270:   for (i = 1; i < m; ++i) VecDuplicate(mode[0], &mode[i]);
271:   for (b = 0, off = 0; b < nb; ++b) {
272:     for (d = 0; d < m / nb; ++d) {
273:       PetscInt ctx[2];
274:       PetscErrorCode (*func)(PetscInt, PetscReal, const PetscReal *, PetscInt, PetscScalar *, void *) = DMPlexProjectRigidBody_Private;
275:       void *voidctx                                                                                   = (void *)(&ctx[0]);

277:       ctx[0] = dimEmbed;
278:       ctx[1] = d;
279:       DMProjectFunctionLabel(dm, 0.0, label, nids[b], &ids[off], 0, NULL, &func, &voidctx, INSERT_VALUES, mode[d]);
280:       off += nids[b];
281:     }
282:   }
283:   /* Orthonormalize system */
284:   for (i = 0; i < m; ++i) {
285:     PetscScalar dots[6];

287:     VecNormalize(mode[i], NULL);
288:     VecMDot(mode[i], m - i - 1, mode + i + 1, dots + i + 1);
289:     for (j = i + 1; j < m; ++j) {
290:       dots[j] *= -1.0;
291:       VecAXPY(mode[j], dots[j], mode[i]);
292:     }
293:   }
294:   MatNullSpaceCreate(comm, PETSC_FALSE, m, mode, sp);
295:   for (i = 0; i < m; ++i) VecDestroy(&mode[i]);
296:   PetscFree2(mode, dots);
297:   return 0;
298: }

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

309:   Input Parameters:
310: + dm - the DMPlex object
311: - height - the maximum projection height >= 0

313:   Level: advanced

315: .seealso: `DMPlexGetMaxProjectionHeight()`, `DMProjectFunctionLocal()`, `DMProjectFunctionLabelLocal()`
316: @*/
317: PetscErrorCode DMPlexSetMaxProjectionHeight(DM dm, PetscInt height)
318: {
319:   DM_Plex *plex = (DM_Plex *)dm->data;

322:   plex->maxProjectionHeight = height;
323:   return 0;
324: }

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

330:   Input Parameters:
331: . dm - the DMPlex object

333:   Output Parameters:
334: . height - the maximum projection height

336:   Level: intermediate

338: .seealso: `DMPlexSetMaxProjectionHeight()`, `DMProjectFunctionLocal()`, `DMProjectFunctionLabelLocal()`
339: @*/
340: PetscErrorCode DMPlexGetMaxProjectionHeight(DM dm, PetscInt *height)
341: {
342:   DM_Plex *plex = (DM_Plex *)dm->data;

345:   *height = plex->maxProjectionHeight;
346:   return 0;
347: }

349: typedef struct {
350:   PetscReal    alpha; /* The first Euler angle, and in 2D the only one */
351:   PetscReal    beta;  /* The second Euler angle */
352:   PetscReal    gamma; /* The third Euler angle */
353:   PetscInt     dim;   /* The dimension of R */
354:   PetscScalar *R;     /* The rotation matrix, transforming a vector in the local basis to the global basis */
355:   PetscScalar *RT;    /* The transposed rotation matrix, transforming a vector in the global basis to the local basis */
356: } RotCtx;

358: /*
359:   Note: Following https://en.wikipedia.org/wiki/Euler_angles, we will specify Euler angles by extrinsic rotations, meaning that
360:   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:
361:   $ The XYZ system rotates about the z axis by alpha. The X axis is now at angle alpha with respect to the x axis.
362:   $ 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.
363:   $ The XYZ system rotates a third time about the z axis by gamma.
364: */
365: static PetscErrorCode DMPlexBasisTransformSetUp_Rotation_Internal(DM dm, void *ctx)
366: {
367:   RotCtx   *rc  = (RotCtx *)ctx;
368:   PetscInt  dim = rc->dim;
369:   PetscReal c1, s1, c2, s2, c3, s3;

371:   PetscMalloc2(PetscSqr(dim), &rc->R, PetscSqr(dim), &rc->RT);
372:   switch (dim) {
373:   case 2:
374:     c1       = PetscCosReal(rc->alpha);
375:     s1       = PetscSinReal(rc->alpha);
376:     rc->R[0] = c1;
377:     rc->R[1] = s1;
378:     rc->R[2] = -s1;
379:     rc->R[3] = c1;
380:     PetscArraycpy(rc->RT, rc->R, PetscSqr(dim));
381:     DMPlex_Transpose2D_Internal(rc->RT);
382:     break;
383:   case 3:
384:     c1       = PetscCosReal(rc->alpha);
385:     s1       = PetscSinReal(rc->alpha);
386:     c2       = PetscCosReal(rc->beta);
387:     s2       = PetscSinReal(rc->beta);
388:     c3       = PetscCosReal(rc->gamma);
389:     s3       = PetscSinReal(rc->gamma);
390:     rc->R[0] = c1 * c3 - c2 * s1 * s3;
391:     rc->R[1] = c3 * s1 + c1 * c2 * s3;
392:     rc->R[2] = s2 * s3;
393:     rc->R[3] = -c1 * s3 - c2 * c3 * s1;
394:     rc->R[4] = c1 * c2 * c3 - s1 * s3;
395:     rc->R[5] = c3 * s2;
396:     rc->R[6] = s1 * s2;
397:     rc->R[7] = -c1 * s2;
398:     rc->R[8] = c2;
399:     PetscArraycpy(rc->RT, rc->R, PetscSqr(dim));
400:     DMPlex_Transpose3D_Internal(rc->RT);
401:     break;
402:   default:
403:     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Dimension %" PetscInt_FMT " not supported", dim);
404:   }
405:   return 0;
406: }

408: static PetscErrorCode DMPlexBasisTransformDestroy_Rotation_Internal(DM dm, void *ctx)
409: {
410:   RotCtx *rc = (RotCtx *)ctx;

412:   PetscFree2(rc->R, rc->RT);
413:   PetscFree(rc);
414:   return 0;
415: }

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

423:   if (l2g) {
424:     *A = rc->R;
425:   } else {
426:     *A = rc->RT;
427:   }
428:   return 0;
429: }

431: PetscErrorCode DMPlexBasisTransformApplyReal_Internal(DM dm, const PetscReal x[], PetscBool l2g, PetscInt dim, const PetscReal *y, PetscReal *z, void *ctx)
432: {
433: #if defined(PETSC_USE_COMPLEX)
434:   switch (dim) {
435:   case 2: {
436:     PetscScalar yt[2] = {y[0], y[1]}, zt[2] = {0.0, 0.0};

438:     DMPlexBasisTransformApply_Internal(dm, x, l2g, dim, yt, zt, ctx);
439:     z[0] = PetscRealPart(zt[0]);
440:     z[1] = PetscRealPart(zt[1]);
441:   } break;
442:   case 3: {
443:     PetscScalar yt[3] = {y[0], y[1], y[2]}, zt[3] = {0.0, 0.0, 0.0};

445:     DMPlexBasisTransformApply_Internal(dm, x, l2g, dim, yt, zt, ctx);
446:     z[0] = PetscRealPart(zt[0]);
447:     z[1] = PetscRealPart(zt[1]);
448:     z[2] = PetscRealPart(zt[2]);
449:   } break;
450:   }
451: #else
452:   DMPlexBasisTransformApply_Internal(dm, x, l2g, dim, y, z, ctx);
453: #endif
454:   return 0;
455: }

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

462:   (*dm->transformGetMatrix)(dm, x, l2g, &A, ctx);
463:   switch (dim) {
464:   case 2:
465:     DMPlex_Mult2D_Internal(A, 1, y, z);
466:     break;
467:   case 3:
468:     DMPlex_Mult3D_Internal(A, 1, y, z);
469:     break;
470:   }
471:   return 0;
472: }

474: static PetscErrorCode DMPlexBasisTransformField_Internal(DM dm, DM tdm, Vec tv, PetscInt p, PetscInt f, PetscBool l2g, PetscScalar *a)
475: {
476:   PetscSection       ts;
477:   const PetscScalar *ta, *tva;
478:   PetscInt           dof;

481:   DMGetLocalSection(tdm, &ts);
482:   PetscSectionGetFieldDof(ts, p, f, &dof);
483:   VecGetArrayRead(tv, &ta);
484:   DMPlexPointLocalFieldRead(tdm, p, f, ta, &tva);
485:   if (l2g) {
486:     switch (dof) {
487:     case 4:
488:       DMPlex_Mult2D_Internal(tva, 1, a, a);
489:       break;
490:     case 9:
491:       DMPlex_Mult3D_Internal(tva, 1, a, a);
492:       break;
493:     }
494:   } else {
495:     switch (dof) {
496:     case 4:
497:       DMPlex_MultTranspose2D_Internal(tva, 1, a, a);
498:       break;
499:     case 9:
500:       DMPlex_MultTranspose3D_Internal(tva, 1, a, a);
501:       break;
502:     }
503:   }
504:   VecRestoreArrayRead(tv, &ta);
505:   return 0;
506: }

508: static PetscErrorCode DMPlexBasisTransformFieldTensor_Internal(DM dm, DM tdm, Vec tv, PetscInt pf, PetscInt f, PetscInt pg, PetscInt g, PetscBool l2g, PetscInt lda, PetscScalar *a)
509: {
510:   PetscSection       s, ts;
511:   const PetscScalar *ta, *tvaf, *tvag;
512:   PetscInt           fdof, gdof, fpdof, gpdof;

515:   DMGetLocalSection(dm, &s);
516:   DMGetLocalSection(tdm, &ts);
517:   PetscSectionGetFieldDof(s, pf, f, &fpdof);
518:   PetscSectionGetFieldDof(s, pg, g, &gpdof);
519:   PetscSectionGetFieldDof(ts, pf, f, &fdof);
520:   PetscSectionGetFieldDof(ts, pg, g, &gdof);
521:   VecGetArrayRead(tv, &ta);
522:   DMPlexPointLocalFieldRead(tdm, pf, f, ta, &tvaf);
523:   DMPlexPointLocalFieldRead(tdm, pg, g, ta, &tvag);
524:   if (l2g) {
525:     switch (fdof) {
526:     case 4:
527:       DMPlex_MatMult2D_Internal(tvaf, gpdof, lda, a, a);
528:       break;
529:     case 9:
530:       DMPlex_MatMult3D_Internal(tvaf, gpdof, lda, a, a);
531:       break;
532:     }
533:     switch (gdof) {
534:     case 4:
535:       DMPlex_MatMultTransposeLeft2D_Internal(tvag, fpdof, lda, a, a);
536:       break;
537:     case 9:
538:       DMPlex_MatMultTransposeLeft3D_Internal(tvag, fpdof, lda, a, a);
539:       break;
540:     }
541:   } else {
542:     switch (fdof) {
543:     case 4:
544:       DMPlex_MatMultTranspose2D_Internal(tvaf, gpdof, lda, a, a);
545:       break;
546:     case 9:
547:       DMPlex_MatMultTranspose3D_Internal(tvaf, gpdof, lda, a, a);
548:       break;
549:     }
550:     switch (gdof) {
551:     case 4:
552:       DMPlex_MatMultLeft2D_Internal(tvag, fpdof, lda, a, a);
553:       break;
554:     case 9:
555:       DMPlex_MatMultLeft3D_Internal(tvag, fpdof, lda, a, a);
556:       break;
557:     }
558:   }
559:   VecRestoreArrayRead(tv, &ta);
560:   return 0;
561: }

563: PetscErrorCode DMPlexBasisTransformPoint_Internal(DM dm, DM tdm, Vec tv, PetscInt p, PetscBool fieldActive[], PetscBool l2g, PetscScalar *a)
564: {
565:   PetscSection    s;
566:   PetscSection    clSection;
567:   IS              clPoints;
568:   const PetscInt *clp;
569:   PetscInt       *points = NULL;
570:   PetscInt        Nf, f, Np, cp, dof, d = 0;

572:   DMGetLocalSection(dm, &s);
573:   PetscSectionGetNumFields(s, &Nf);
574:   DMPlexGetCompressedClosure(dm, s, p, &Np, &points, &clSection, &clPoints, &clp);
575:   for (f = 0; f < Nf; ++f) {
576:     for (cp = 0; cp < Np * 2; cp += 2) {
577:       PetscSectionGetFieldDof(s, points[cp], f, &dof);
578:       if (!dof) continue;
579:       if (fieldActive[f]) DMPlexBasisTransformField_Internal(dm, tdm, tv, points[cp], f, l2g, &a[d]);
580:       d += dof;
581:     }
582:   }
583:   DMPlexRestoreCompressedClosure(dm, s, p, &Np, &points, &clSection, &clPoints, &clp);
584:   return 0;
585: }

587: PetscErrorCode DMPlexBasisTransformPointTensor_Internal(DM dm, DM tdm, Vec tv, PetscInt p, PetscBool l2g, PetscInt lda, PetscScalar *a)
588: {
589:   PetscSection    s;
590:   PetscSection    clSection;
591:   IS              clPoints;
592:   const PetscInt *clp;
593:   PetscInt       *points = NULL;
594:   PetscInt        Nf, f, g, Np, cpf, cpg, fdof, gdof, r, c = 0;

596:   DMGetLocalSection(dm, &s);
597:   PetscSectionGetNumFields(s, &Nf);
598:   DMPlexGetCompressedClosure(dm, s, p, &Np, &points, &clSection, &clPoints, &clp);
599:   for (f = 0, r = 0; f < Nf; ++f) {
600:     for (cpf = 0; cpf < Np * 2; cpf += 2) {
601:       PetscSectionGetFieldDof(s, points[cpf], f, &fdof);
602:       for (g = 0, c = 0; g < Nf; ++g) {
603:         for (cpg = 0; cpg < Np * 2; cpg += 2) {
604:           PetscSectionGetFieldDof(s, points[cpg], g, &gdof);
605:           DMPlexBasisTransformFieldTensor_Internal(dm, tdm, tv, points[cpf], f, points[cpg], g, l2g, lda, &a[r * lda + c]);
606:           c += gdof;
607:         }
608:       }
610:       r += fdof;
611:     }
612:   }
614:   DMPlexRestoreCompressedClosure(dm, s, p, &Np, &points, &clSection, &clPoints, &clp);
615:   return 0;
616: }

618: static PetscErrorCode DMPlexBasisTransform_Internal(DM dm, Vec lv, PetscBool l2g)
619: {
620:   DM                 tdm;
621:   Vec                tv;
622:   PetscSection       ts, s;
623:   const PetscScalar *ta;
624:   PetscScalar       *a, *va;
625:   PetscInt           pStart, pEnd, p, Nf, f;

627:   DMGetBasisTransformDM_Internal(dm, &tdm);
628:   DMGetBasisTransformVec_Internal(dm, &tv);
629:   DMGetLocalSection(tdm, &ts);
630:   DMGetLocalSection(dm, &s);
631:   PetscSectionGetChart(s, &pStart, &pEnd);
632:   PetscSectionGetNumFields(s, &Nf);
633:   VecGetArray(lv, &a);
634:   VecGetArrayRead(tv, &ta);
635:   for (p = pStart; p < pEnd; ++p) {
636:     for (f = 0; f < Nf; ++f) {
637:       DMPlexPointLocalFieldRef(dm, p, f, a, &va);
638:       DMPlexBasisTransformField_Internal(dm, tdm, tv, p, f, l2g, va);
639:     }
640:   }
641:   VecRestoreArray(lv, &a);
642:   VecRestoreArrayRead(tv, &ta);
643:   return 0;
644: }

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

649:   Input Parameters:
650: + dm - The DM
651: - lv - A local vector with values in the global basis

653:   Output Parameters:
654: . lv - A local vector with values in the local basis

656:   Note: 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.

658:   Level: developer

660: .seealso: `DMPlexLocalToGlobalBasis()`, `DMGetLocalSection()`, `DMPlexCreateBasisRotation()`
661: @*/
662: PetscErrorCode DMPlexGlobalToLocalBasis(DM dm, Vec lv)
663: {
666:   DMPlexBasisTransform_Internal(dm, lv, PETSC_FALSE);
667:   return 0;
668: }

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

673:   Input Parameters:
674: + dm - The DM
675: - lv - A local vector with values in the local basis

677:   Output Parameters:
678: . lv - A local vector with values in the global basis

680:   Note: 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.

682:   Level: developer

684: .seealso: `DMPlexGlobalToLocalBasis()`, `DMGetLocalSection()`, `DMPlexCreateBasisRotation()`
685: @*/
686: PetscErrorCode DMPlexLocalToGlobalBasis(DM dm, Vec lv)
687: {
690:   DMPlexBasisTransform_Internal(dm, lv, PETSC_TRUE);
691:   return 0;
692: }

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

698:   Input Parameters:
699: + dm    - The DM
700: . alpha - The first Euler angle, and in 2D the only one
701: . beta  - The second Euler angle
702: - gamma - The third Euler angle

704:   Note: Following https://en.wikipedia.org/wiki/Euler_angles, we will specify Euler angles by extrinsic rotations, meaning that
705:   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:
706:   $ The XYZ system rotates about the z axis by alpha. The X axis is now at angle alpha with respect to the x axis.
707:   $ 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.
708:   $ The XYZ system rotates a third time about the z axis by gamma.

710:   Level: developer

712: .seealso: `DMPlexGlobalToLocalBasis()`, `DMPlexLocalToGlobalBasis()`
713: @*/
714: PetscErrorCode DMPlexCreateBasisRotation(DM dm, PetscReal alpha, PetscReal beta, PetscReal gamma)
715: {
716:   RotCtx  *rc;
717:   PetscInt cdim;

719:   DMGetCoordinateDim(dm, &cdim);
720:   PetscMalloc1(1, &rc);
721:   dm->transformCtx       = rc;
722:   dm->transformSetUp     = DMPlexBasisTransformSetUp_Rotation_Internal;
723:   dm->transformDestroy   = DMPlexBasisTransformDestroy_Rotation_Internal;
724:   dm->transformGetMatrix = DMPlexBasisTransformGetMatrix_Rotation_Internal;
725:   rc->dim                = cdim;
726:   rc->alpha              = alpha;
727:   rc->beta               = beta;
728:   rc->gamma              = gamma;
729:   (*dm->transformSetUp)(dm, dm->transformCtx);
730:   DMConstructBasisTransform_Internal(dm);
731:   return 0;
732: }

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

737:   Input Parameters:
738: + dm     - The DM, with a PetscDS that matches the problem being constrained
739: . time   - The time
740: . field  - The field to constrain
741: . Nc     - The number of constrained field components, or 0 for all components
742: . comps  - An array of constrained component numbers, or NULL for all components
743: . label  - The DMLabel defining constrained points
744: . numids - The number of DMLabel ids for constrained points
745: . ids    - An array of ids for constrained points
746: . func   - A pointwise function giving boundary values
747: - ctx    - An optional user context for bcFunc

749:   Output Parameter:
750: . locX   - A local vector to receives the boundary values

752:   Level: developer

754: .seealso: `DMPlexInsertBoundaryValuesEssentialField()`, `DMPlexInsertBoundaryValuesEssentialBdField()`, `DMAddBoundary()`
755: @*/
756: 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)
757: {
758:   PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal x[], PetscInt, PetscScalar *u, void *ctx);
759:   void   **ctxs;
760:   PetscInt numFields;

762:   DMGetNumFields(dm, &numFields);
763:   PetscCalloc2(numFields, &funcs, numFields, &ctxs);
764:   funcs[field] = func;
765:   ctxs[field]  = ctx;
766:   DMProjectFunctionLabelLocal(dm, time, label, numids, ids, Nc, comps, funcs, ctxs, INSERT_BC_VALUES, locX);
767:   PetscFree2(funcs, ctxs);
768:   return 0;
769: }

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

774:   Input Parameters:
775: + dm     - The DM, with a PetscDS that matches the problem being constrained
776: . time   - The time
777: . locU   - A local vector with the input solution values
778: . field  - The field to constrain
779: . Nc     - The number of constrained field components, or 0 for all components
780: . comps  - An array of constrained component numbers, or NULL for all components
781: . label  - The DMLabel defining constrained points
782: . numids - The number of DMLabel ids for constrained points
783: . ids    - An array of ids for constrained points
784: . func   - A pointwise function giving boundary values
785: - ctx    - An optional user context for bcFunc

787:   Output Parameter:
788: . locX   - A local vector to receives the boundary values

790:   Level: developer

792: .seealso: `DMPlexInsertBoundaryValuesEssential()`, `DMPlexInsertBoundaryValuesEssentialBdField()`, `DMAddBoundary()`
793: @*/
794: 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)
795: {
796:   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[]);
797:   void   **ctxs;
798:   PetscInt numFields;

800:   DMGetNumFields(dm, &numFields);
801:   PetscCalloc2(numFields, &funcs, numFields, &ctxs);
802:   funcs[field] = func;
803:   ctxs[field]  = ctx;
804:   DMProjectFieldLabelLocal(dm, time, label, numids, ids, Nc, comps, locU, funcs, INSERT_BC_VALUES, locX);
805:   PetscFree2(funcs, ctxs);
806:   return 0;
807: }

809: /*@C
810:   DMPlexInsertBoundaryValuesEssentialBdField - Insert boundary values into a local vector using a function of the coodinates and boundary field data

812:   Collective on dm

814:   Input Parameters:
815: + dm     - The DM, with a PetscDS that matches the problem being constrained
816: . time   - The time
817: . locU   - A local vector with the input solution values
818: . field  - The field to constrain
819: . Nc     - The number of constrained field components, or 0 for all components
820: . comps  - An array of constrained component numbers, or NULL for all components
821: . label  - The DMLabel defining constrained points
822: . numids - The number of DMLabel ids for constrained points
823: . ids    - An array of ids for constrained points
824: . func   - A pointwise function giving boundary values, the calling sequence is given in DMProjectBdFieldLabelLocal()
825: - ctx    - An optional user context for bcFunc

827:   Output Parameter:
828: . locX   - A local vector to receive the boundary values

830:   Level: developer

832: .seealso: `DMProjectBdFieldLabelLocal()`, `DMPlexInsertBoundaryValuesEssential()`, `DMPlexInsertBoundaryValuesEssentialField()`, `DMAddBoundary()`
833: @*/
834: 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)
835: {
836:   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[]);
837:   void   **ctxs;
838:   PetscInt numFields;

840:   DMGetNumFields(dm, &numFields);
841:   PetscCalloc2(numFields, &funcs, numFields, &ctxs);
842:   funcs[field] = func;
843:   ctxs[field]  = ctx;
844:   DMProjectBdFieldLabelLocal(dm, time, label, numids, ids, Nc, comps, locU, funcs, INSERT_BC_VALUES, locX);
845:   PetscFree2(funcs, ctxs);
846:   return 0;
847: }

849: /*@C
850:   DMPlexInsertBoundaryValuesRiemann - Insert boundary values into a local vector

852:   Input Parameters:
853: + dm     - The DM, with a PetscDS that matches the problem being constrained
854: . time   - The time
855: . faceGeometry - A vector with the FVM face geometry information
856: . cellGeometry - A vector with the FVM cell geometry information
857: . Grad         - A vector with the FVM cell gradient information
858: . field  - The field to constrain
859: . Nc     - The number of constrained field components, or 0 for all components
860: . comps  - An array of constrained component numbers, or NULL for all components
861: . label  - The DMLabel defining constrained points
862: . numids - The number of DMLabel ids for constrained points
863: . ids    - An array of ids for constrained points
864: . func   - A pointwise function giving boundary values
865: - ctx    - An optional user context for bcFunc

867:   Output Parameter:
868: . locX   - A local vector to receives the boundary values

870:   Note: This implementation currently ignores the numcomps/comps argument from DMAddBoundary()

872:   Level: developer

874: .seealso: `DMPlexInsertBoundaryValuesEssential()`, `DMPlexInsertBoundaryValuesEssentialField()`, `DMAddBoundary()`
875: @*/
876: 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)
877: {
878:   PetscDS            prob;
879:   PetscSF            sf;
880:   DM                 dmFace, dmCell, dmGrad;
881:   const PetscScalar *facegeom, *cellgeom = NULL, *grad;
882:   const PetscInt    *leaves;
883:   PetscScalar       *x, *fx;
884:   PetscInt           dim, nleaves, loc, fStart, fEnd, pdim, i;
885:   PetscErrorCode     ierru = 0;

887:   DMGetPointSF(dm, &sf);
888:   PetscSFGetGraph(sf, NULL, &nleaves, &leaves, NULL);
889:   nleaves = PetscMax(0, nleaves);
890:   DMGetDimension(dm, &dim);
891:   DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd);
892:   DMGetDS(dm, &prob);
893:   VecGetDM(faceGeometry, &dmFace);
894:   VecGetArrayRead(faceGeometry, &facegeom);
895:   if (cellGeometry) {
896:     VecGetDM(cellGeometry, &dmCell);
897:     VecGetArrayRead(cellGeometry, &cellgeom);
898:   }
899:   if (Grad) {
900:     PetscFV fv;

902:     PetscDSGetDiscretization(prob, field, (PetscObject *)&fv);
903:     VecGetDM(Grad, &dmGrad);
904:     VecGetArrayRead(Grad, &grad);
905:     PetscFVGetNumComponents(fv, &pdim);
906:     DMGetWorkArray(dm, pdim, MPIU_SCALAR, &fx);
907:   }
908:   VecGetArray(locX, &x);
909:   for (i = 0; i < numids; ++i) {
910:     IS              faceIS;
911:     const PetscInt *faces;
912:     PetscInt        numFaces, f;

914:     DMLabelGetStratumIS(label, ids[i], &faceIS);
915:     if (!faceIS) continue; /* No points with that id on this process */
916:     ISGetLocalSize(faceIS, &numFaces);
917:     ISGetIndices(faceIS, &faces);
918:     for (f = 0; f < numFaces; ++f) {
919:       const PetscInt   face = faces[f], *cells;
920:       PetscFVFaceGeom *fg;

922:       if ((face < fStart) || (face >= fEnd)) continue; /* Refinement adds non-faces to labels */
923:       PetscFindInt(face, nleaves, (PetscInt *)leaves, &loc);
924:       if (loc >= 0) continue;
925:       DMPlexPointLocalRead(dmFace, face, facegeom, &fg);
926:       DMPlexGetSupport(dm, face, &cells);
927:       if (Grad) {
928:         PetscFVCellGeom *cg;
929:         PetscScalar     *cx, *cgrad;
930:         PetscScalar     *xG;
931:         PetscReal        dx[3];
932:         PetscInt         d;

934:         DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cg);
935:         DMPlexPointLocalRead(dm, cells[0], x, &cx);
936:         DMPlexPointLocalRead(dmGrad, cells[0], grad, &cgrad);
937:         DMPlexPointLocalFieldRef(dm, cells[1], field, x, &xG);
938:         DMPlex_WaxpyD_Internal(dim, -1, cg->centroid, fg->centroid, dx);
939:         for (d = 0; d < pdim; ++d) fx[d] = cx[d] + DMPlex_DotD_Internal(dim, &cgrad[d * dim], dx);
940:         (*func)(time, fg->centroid, fg->normal, fx, xG, ctx);
941:       } else {
942:         PetscScalar *xI;
943:         PetscScalar *xG;

945:         DMPlexPointLocalRead(dm, cells[0], x, &xI);
946:         DMPlexPointLocalFieldRef(dm, cells[1], field, x, &xG);
947:         ierru = (*func)(time, fg->centroid, fg->normal, xI, xG, ctx);
948:         if (ierru) {
949:           ISRestoreIndices(faceIS, &faces);
950:           ISDestroy(&faceIS);
951:           goto cleanup;
952:         }
953:       }
954:     }
955:     ISRestoreIndices(faceIS, &faces);
956:     ISDestroy(&faceIS);
957:   }
958: cleanup:
959:   VecRestoreArray(locX, &x);
960:   if (Grad) {
961:     DMRestoreWorkArray(dm, pdim, MPIU_SCALAR, &fx);
962:     VecRestoreArrayRead(Grad, &grad);
963:   }
964:   if (cellGeometry) VecRestoreArrayRead(cellGeometry, &cellgeom);
965:   VecRestoreArrayRead(faceGeometry, &facegeom);
966:   ierru;
967:   return 0;
968: }

970: static PetscErrorCode zero(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nc, PetscScalar *u, void *ctx)
971: {
972:   PetscInt c;
973:   for (c = 0; c < Nc; ++c) u[c] = 0.0;
974:   return 0;
975: }

977: PetscErrorCode DMPlexInsertBoundaryValues_Plex(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
978: {
979:   PetscObject isZero;
980:   PetscDS     prob;
981:   PetscInt    numBd, b;

983:   DMGetDS(dm, &prob);
984:   PetscDSGetNumBoundary(prob, &numBd);
985:   PetscObjectQuery((PetscObject)locX, "__Vec_bc_zero__", &isZero);
986:   for (b = 0; b < numBd; ++b) {
987:     PetscWeakForm           wf;
988:     DMBoundaryConditionType type;
989:     const char             *name;
990:     DMLabel                 label;
991:     PetscInt                field, Nc;
992:     const PetscInt         *comps;
993:     PetscObject             obj;
994:     PetscClassId            id;
995:     void (*bvfunc)(void);
996:     PetscInt        numids;
997:     const PetscInt *ids;
998:     void           *ctx;

1000:     PetscDSGetBoundary(prob, b, &wf, &type, &name, &label, &numids, &ids, &field, &Nc, &comps, &bvfunc, NULL, &ctx);
1001:     if (insertEssential != (type & DM_BC_ESSENTIAL)) continue;
1002:     DMGetField(dm, field, NULL, &obj);
1003:     PetscObjectGetClassId(obj, &id);
1004:     if (id == PETSCFE_CLASSID) {
1005:       switch (type) {
1006:         /* for FEM, there is no insertion to be done for non-essential boundary conditions */
1007:       case DM_BC_ESSENTIAL: {
1008:         PetscSimplePointFunc func = (PetscSimplePointFunc)bvfunc;

1010:         if (isZero) func = zero;
1011:         DMPlexLabelAddCells(dm, label);
1012:         DMPlexInsertBoundaryValuesEssential(dm, time, field, Nc, comps, label, numids, ids, func, ctx, locX);
1013:         DMPlexLabelClearCells(dm, label);
1014:       } break;
1015:       case DM_BC_ESSENTIAL_FIELD: {
1016:         PetscPointFunc func = (PetscPointFunc)bvfunc;

1018:         DMPlexLabelAddCells(dm, label);
1019:         DMPlexInsertBoundaryValuesEssentialField(dm, time, locX, field, Nc, comps, label, numids, ids, func, ctx, locX);
1020:         DMPlexLabelClearCells(dm, label);
1021:       } break;
1022:       default:
1023:         break;
1024:       }
1025:     } else if (id == PETSCFV_CLASSID) {
1026:       {
1027:         PetscErrorCode (*func)(PetscReal, const PetscReal *, const PetscReal *, const PetscScalar *, PetscScalar *, void *) = (PetscErrorCode(*)(PetscReal, const PetscReal *, const PetscReal *, const PetscScalar *, PetscScalar *, void *))bvfunc;

1029:         if (!faceGeomFVM) continue;
1030:         DMPlexInsertBoundaryValuesRiemann(dm, time, faceGeomFVM, cellGeomFVM, gradFVM, field, Nc, comps, label, numids, ids, func, ctx, locX);
1031:       }
1032:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1033:   }
1034:   return 0;
1035: }

1037: PetscErrorCode DMPlexInsertTimeDerivativeBoundaryValues_Plex(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
1038: {
1039:   PetscObject isZero;
1040:   PetscDS     prob;
1041:   PetscInt    numBd, b;

1043:   if (!locX) return 0;
1044:   DMGetDS(dm, &prob);
1045:   PetscDSGetNumBoundary(prob, &numBd);
1046:   PetscObjectQuery((PetscObject)locX, "__Vec_bc_zero__", &isZero);
1047:   for (b = 0; b < numBd; ++b) {
1048:     PetscWeakForm           wf;
1049:     DMBoundaryConditionType type;
1050:     const char             *name;
1051:     DMLabel                 label;
1052:     PetscInt                field, Nc;
1053:     const PetscInt         *comps;
1054:     PetscObject             obj;
1055:     PetscClassId            id;
1056:     PetscInt                numids;
1057:     const PetscInt         *ids;
1058:     void (*bvfunc)(void);
1059:     void *ctx;

1061:     PetscDSGetBoundary(prob, b, &wf, &type, &name, &label, &numids, &ids, &field, &Nc, &comps, NULL, &bvfunc, &ctx);
1062:     if (insertEssential != (type & DM_BC_ESSENTIAL)) continue;
1063:     DMGetField(dm, field, NULL, &obj);
1064:     PetscObjectGetClassId(obj, &id);
1065:     if (id == PETSCFE_CLASSID) {
1066:       switch (type) {
1067:         /* for FEM, there is no insertion to be done for non-essential boundary conditions */
1068:       case DM_BC_ESSENTIAL: {
1069:         PetscSimplePointFunc func_t = (PetscSimplePointFunc)bvfunc;

1071:         if (isZero) func_t = zero;
1072:         DMPlexLabelAddCells(dm, label);
1073:         DMPlexInsertBoundaryValuesEssential(dm, time, field, Nc, comps, label, numids, ids, func_t, ctx, locX);
1074:         DMPlexLabelClearCells(dm, label);
1075:       } break;
1076:       case DM_BC_ESSENTIAL_FIELD: {
1077:         PetscPointFunc func_t = (PetscPointFunc)bvfunc;

1079:         DMPlexLabelAddCells(dm, label);
1080:         DMPlexInsertBoundaryValuesEssentialField(dm, time, locX, field, Nc, comps, label, numids, ids, func_t, ctx, locX);
1081:         DMPlexLabelClearCells(dm, label);
1082:       } break;
1083:       default:
1084:         break;
1085:       }
1086:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1087:   }
1088:   return 0;
1089: }

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

1094:   Not Collective

1096:   Input Parameters:
1097: + dm - The DM
1098: . insertEssential - Should I insert essential (e.g. Dirichlet) or inessential (e.g. Neumann) boundary conditions
1099: . time - The time
1100: . faceGeomFVM - Face geometry data for FV discretizations
1101: . cellGeomFVM - Cell geometry data for FV discretizations
1102: - gradFVM - Gradient reconstruction data for FV discretizations

1104:   Output Parameters:
1105: . locX - Solution updated with boundary values

1107:   Level: intermediate

1109: .seealso: `DMProjectFunctionLabelLocal()`, `DMAddBoundary()`
1110: @*/
1111: PetscErrorCode DMPlexInsertBoundaryValues(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
1112: {
1118:   PetscTryMethod(dm, "DMPlexInsertBoundaryValues_C", (DM, PetscBool, Vec, PetscReal, Vec, Vec, Vec), (dm, insertEssential, locX, time, faceGeomFVM, cellGeomFVM, gradFVM));
1119:   return 0;
1120: }

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

1125:   Input Parameters:
1126: + dm - The DM
1127: . insertEssential - Should I insert essential (e.g. Dirichlet) or inessential (e.g. Neumann) boundary conditions
1128: . time - The time
1129: . faceGeomFVM - Face geometry data for FV discretizations
1130: . cellGeomFVM - Cell geometry data for FV discretizations
1131: - gradFVM - Gradient reconstruction data for FV discretizations

1133:   Output Parameters:
1134: . locX_t - Solution updated with boundary values

1136:   Level: developer

1138: .seealso: `DMProjectFunctionLabelLocal()`
1139: @*/
1140: PetscErrorCode DMPlexInsertTimeDerivativeBoundaryValues(DM dm, PetscBool insertEssential, Vec locX_t, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
1141: {
1147:   PetscTryMethod(dm, "DMPlexInsertTimeDerviativeBoundaryValues_C", (DM, PetscBool, Vec, PetscReal, Vec, Vec, Vec), (dm, insertEssential, locX_t, time, faceGeomFVM, cellGeomFVM, gradFVM));
1148:   return 0;
1149: }

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

1155:   DMGetLocalVector(dm, &localX);
1156:   DMPlexInsertBoundaryValues(dm, PETSC_TRUE, localX, time, NULL, NULL, NULL);
1157:   DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX);
1158:   DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX);
1159:   DMPlexComputeL2DiffLocal(dm, time, funcs, ctxs, localX, diff);
1160:   DMRestoreLocalVector(dm, &localX);
1161:   return 0;
1162: }

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

1167:   Collective on dm

1169:   Input Parameters:
1170: + dm     - The DM
1171: . time   - The time
1172: . funcs  - The functions to evaluate for each field component
1173: . ctxs   - Optional array of contexts to pass to each function, or NULL.
1174: - localX - The coefficient vector u_h, a local vector

1176:   Output Parameter:
1177: . diff - The diff ||u - u_h||_2

1179:   Level: developer

1181: .seealso: `DMProjectFunction()`, `DMComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
1182: @*/
1183: PetscErrorCode DMPlexComputeL2DiffLocal(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec localX, PetscReal *diff)
1184: {
1185:   const PetscInt   debug = ((DM_Plex *)dm->data)->printL2;
1186:   DM               tdm;
1187:   Vec              tv;
1188:   PetscSection     section;
1189:   PetscQuadrature  quad;
1190:   PetscFEGeom      fegeom;
1191:   PetscScalar     *funcVal, *interpolant;
1192:   PetscReal       *coords, *gcoords;
1193:   PetscReal        localDiff = 0.0;
1194:   const PetscReal *quadWeights;
1195:   PetscInt         dim, coordDim, numFields, numComponents = 0, qNc, Nq, cellHeight, cStart, cEnd, c, field, fieldOffset;
1196:   PetscBool        transform;

1198:   DMGetDimension(dm, &dim);
1199:   DMGetCoordinateDim(dm, &coordDim);
1200:   fegeom.dimEmbed = coordDim;
1201:   DMGetLocalSection(dm, &section);
1202:   PetscSectionGetNumFields(section, &numFields);
1203:   DMGetBasisTransformDM_Internal(dm, &tdm);
1204:   DMGetBasisTransformVec_Internal(dm, &tv);
1205:   DMHasBasisTransform(dm, &transform);
1206:   for (field = 0; field < numFields; ++field) {
1207:     PetscObject  obj;
1208:     PetscClassId id;
1209:     PetscInt     Nc;

1211:     DMGetField(dm, field, NULL, &obj);
1212:     PetscObjectGetClassId(obj, &id);
1213:     if (id == PETSCFE_CLASSID) {
1214:       PetscFE fe = (PetscFE)obj;

1216:       PetscFEGetQuadrature(fe, &quad);
1217:       PetscFEGetNumComponents(fe, &Nc);
1218:     } else if (id == PETSCFV_CLASSID) {
1219:       PetscFV fv = (PetscFV)obj;

1221:       PetscFVGetQuadrature(fv, &quad);
1222:       PetscFVGetNumComponents(fv, &Nc);
1223:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1224:     numComponents += Nc;
1225:   }
1226:   PetscQuadratureGetData(quad, NULL, &qNc, &Nq, NULL, &quadWeights);
1228:   PetscMalloc6(numComponents, &funcVal, numComponents, &interpolant, coordDim * Nq, &coords, Nq, &fegeom.detJ, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ);
1229:   DMPlexGetVTKCellHeight(dm, &cellHeight);
1230:   DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd);
1231:   for (c = cStart; c < cEnd; ++c) {
1232:     PetscScalar *x        = NULL;
1233:     PetscReal    elemDiff = 0.0;
1234:     PetscInt     qc       = 0;

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

1239:     for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1240:       PetscObject  obj;
1241:       PetscClassId id;
1242:       void *const  ctx = ctxs ? ctxs[field] : NULL;
1243:       PetscInt     Nb, Nc, q, fc;

1245:       DMGetField(dm, field, NULL, &obj);
1246:       PetscObjectGetClassId(obj, &id);
1247:       if (id == PETSCFE_CLASSID) {
1248:         PetscFEGetNumComponents((PetscFE)obj, &Nc);
1249:         PetscFEGetDimension((PetscFE)obj, &Nb);
1250:       } else if (id == PETSCFV_CLASSID) {
1251:         PetscFVGetNumComponents((PetscFV)obj, &Nc);
1252:         Nb = 1;
1253:       } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1254:       if (debug) {
1255:         char title[1024];
1256:         PetscSNPrintf(title, 1023, "Solution for Field %" PetscInt_FMT, field);
1257:         DMPrintCellVector(c, title, Nb, &x[fieldOffset]);
1258:       }
1259:       for (q = 0; q < Nq; ++q) {
1260:         PetscFEGeom    qgeom;

1263:         qgeom.dimEmbed = fegeom.dimEmbed;
1264:         qgeom.J        = &fegeom.J[q * coordDim * coordDim];
1265:         qgeom.invJ     = &fegeom.invJ[q * coordDim * coordDim];
1266:         qgeom.detJ     = &fegeom.detJ[q];
1268:         if (transform) {
1269:           gcoords = &coords[coordDim * Nq];
1270:           DMPlexBasisTransformApplyReal_Internal(dm, &coords[coordDim * q], PETSC_TRUE, coordDim, &coords[coordDim * q], gcoords, dm->transformCtx);
1271:         } else {
1272:           gcoords = &coords[coordDim * q];
1273:         }
1274:         PetscArrayzero(funcVal, Nc);
1275:         (*funcs[field])(coordDim, time, gcoords, Nc, funcVal, ctx);
1276:         if (ierr) {
1277:           DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x);
1278:           DMRestoreLocalVector(dm, &localX);
1279:           PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ);
1280:         }
1281:         if (transform) DMPlexBasisTransformApply_Internal(dm, &coords[coordDim * q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx);
1282:         if (id == PETSCFE_CLASSID) PetscFEInterpolate_Static((PetscFE)obj, &x[fieldOffset], &qgeom, q, interpolant);
1283:         else if (id == PETSCFV_CLASSID) PetscFVInterpolate_Static((PetscFV)obj, &x[fieldOffset], q, interpolant);
1284:         else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1285:         for (fc = 0; fc < Nc; ++fc) {
1286:           const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
1287:           if (debug)
1288:             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.),
1289:                                   (double)(PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q]), (double)PetscRealPart(interpolant[fc]), (double)PetscRealPart(funcVal[fc])));
1290:           elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
1291:         }
1292:       }
1293:       fieldOffset += Nb;
1294:       qc += Nc;
1295:     }
1296:     DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x);
1297:     if (debug) PetscPrintf(PETSC_COMM_SELF, "  elem %" PetscInt_FMT " diff %g\n", c, (double)elemDiff);
1298:     localDiff += elemDiff;
1299:   }
1300:   PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ);
1301:   MPIU_Allreduce(&localDiff, diff, 1, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm));
1302:   *diff = PetscSqrtReal(*diff);
1303:   return 0;
1304: }

1306: 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)
1307: {
1308:   const PetscInt   debug = ((DM_Plex *)dm->data)->printL2;
1309:   DM               tdm;
1310:   PetscSection     section;
1311:   PetscQuadrature  quad;
1312:   Vec              localX, tv;
1313:   PetscScalar     *funcVal, *interpolant;
1314:   const PetscReal *quadWeights;
1315:   PetscFEGeom      fegeom;
1316:   PetscReal       *coords, *gcoords;
1317:   PetscReal        localDiff = 0.0;
1318:   PetscInt         dim, coordDim, qNc = 0, Nq = 0, numFields, numComponents = 0, cStart, cEnd, c, field, fieldOffset;
1319:   PetscBool        transform;

1321:   DMGetDimension(dm, &dim);
1322:   DMGetCoordinateDim(dm, &coordDim);
1323:   fegeom.dimEmbed = coordDim;
1324:   DMGetLocalSection(dm, &section);
1325:   PetscSectionGetNumFields(section, &numFields);
1326:   DMGetLocalVector(dm, &localX);
1327:   DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX);
1328:   DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX);
1329:   DMGetBasisTransformDM_Internal(dm, &tdm);
1330:   DMGetBasisTransformVec_Internal(dm, &tv);
1331:   DMHasBasisTransform(dm, &transform);
1332:   for (field = 0; field < numFields; ++field) {
1333:     PetscFE  fe;
1334:     PetscInt Nc;

1336:     DMGetField(dm, field, NULL, (PetscObject *)&fe);
1337:     PetscFEGetQuadrature(fe, &quad);
1338:     PetscFEGetNumComponents(fe, &Nc);
1339:     numComponents += Nc;
1340:   }
1341:   PetscQuadratureGetData(quad, NULL, &qNc, &Nq, NULL, &quadWeights);
1343:   /* DMProjectFunctionLocal(dm, fe, funcs, INSERT_BC_VALUES, localX); */
1344:   PetscMalloc6(numComponents, &funcVal, coordDim * Nq, &coords, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ, numComponents * coordDim, &interpolant, Nq, &fegeom.detJ);
1345:   DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd);
1346:   for (c = cStart; c < cEnd; ++c) {
1347:     PetscScalar *x        = NULL;
1348:     PetscReal    elemDiff = 0.0;
1349:     PetscInt     qc       = 0;

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

1354:     for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1355:       PetscFE     fe;
1356:       void *const ctx = ctxs ? ctxs[field] : NULL;
1357:       PetscInt    Nb, Nc, q, fc;

1359:       DMGetField(dm, field, NULL, (PetscObject *)&fe);
1360:       PetscFEGetDimension(fe, &Nb);
1361:       PetscFEGetNumComponents(fe, &Nc);
1362:       if (debug) {
1363:         char title[1024];
1364:         PetscSNPrintf(title, 1023, "Solution for Field %" PetscInt_FMT, field);
1365:         DMPrintCellVector(c, title, Nb, &x[fieldOffset]);
1366:       }
1367:       for (q = 0; q < Nq; ++q) {
1368:         PetscFEGeom    qgeom;

1371:         qgeom.dimEmbed = fegeom.dimEmbed;
1372:         qgeom.J        = &fegeom.J[q * coordDim * coordDim];
1373:         qgeom.invJ     = &fegeom.invJ[q * coordDim * coordDim];
1374:         qgeom.detJ     = &fegeom.detJ[q];
1376:         if (transform) {
1377:           gcoords = &coords[coordDim * Nq];
1378:           DMPlexBasisTransformApplyReal_Internal(dm, &coords[coordDim * q], PETSC_TRUE, coordDim, &coords[coordDim * q], gcoords, dm->transformCtx);
1379:         } else {
1380:           gcoords = &coords[coordDim * q];
1381:         }
1382:         PetscArrayzero(funcVal, Nc);
1383:         (*funcs[field])(coordDim, time, gcoords, n, Nc, funcVal, ctx);
1384:         if (ierr) {
1385:           DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x);
1386:           DMRestoreLocalVector(dm, &localX);
1387:           PetscFree6(funcVal, coords, fegeom.J, fegeom.invJ, interpolant, fegeom.detJ);
1388:         }
1389:         if (transform) DMPlexBasisTransformApply_Internal(dm, &coords[coordDim * q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx);
1390:         PetscFEInterpolateGradient_Static(fe, 1, &x[fieldOffset], &qgeom, q, interpolant);
1391:         /* Overwrite with the dot product if the normal is given */
1392:         if (n) {
1393:           for (fc = 0; fc < Nc; ++fc) {
1394:             PetscScalar sum = 0.0;
1395:             PetscInt    d;
1396:             for (d = 0; d < dim; ++d) sum += interpolant[fc * dim + d] * n[d];
1397:             interpolant[fc] = sum;
1398:           }
1399:         }
1400:         for (fc = 0; fc < Nc; ++fc) {
1401:           const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
1402:           if (debug) 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]));
1403:           elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
1404:         }
1405:       }
1406:       fieldOffset += Nb;
1407:       qc += Nc;
1408:     }
1409:     DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x);
1410:     if (debug) PetscPrintf(PETSC_COMM_SELF, "  elem %" PetscInt_FMT " diff %g\n", c, (double)elemDiff);
1411:     localDiff += elemDiff;
1412:   }
1413:   PetscFree6(funcVal, coords, fegeom.J, fegeom.invJ, interpolant, fegeom.detJ);
1414:   DMRestoreLocalVector(dm, &localX);
1415:   MPIU_Allreduce(&localDiff, diff, 1, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm));
1416:   *diff = PetscSqrtReal(*diff);
1417:   return 0;
1418: }

1420: PetscErrorCode DMComputeL2FieldDiff_Plex(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, PetscReal *diff)
1421: {
1422:   const PetscInt debug = ((DM_Plex *)dm->data)->printL2;
1423:   DM             tdm;
1424:   DMLabel        depthLabel;
1425:   PetscSection   section;
1426:   Vec            localX, tv;
1427:   PetscReal     *localDiff;
1428:   PetscInt       dim, depth, dE, Nf, f, Nds, s;
1429:   PetscBool      transform;

1431:   DMGetDimension(dm, &dim);
1432:   DMGetCoordinateDim(dm, &dE);
1433:   DMGetLocalSection(dm, &section);
1434:   DMGetLocalVector(dm, &localX);
1435:   DMGetBasisTransformDM_Internal(dm, &tdm);
1436:   DMGetBasisTransformVec_Internal(dm, &tv);
1437:   DMHasBasisTransform(dm, &transform);
1438:   DMGetNumFields(dm, &Nf);
1439:   DMPlexGetDepthLabel(dm, &depthLabel);
1440:   DMLabelGetNumValues(depthLabel, &depth);

1442:   VecSet(localX, 0.0);
1443:   DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX);
1444:   DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX);
1445:   DMProjectFunctionLocal(dm, time, funcs, ctxs, INSERT_BC_VALUES, localX);
1446:   DMGetNumDS(dm, &Nds);
1447:   PetscCalloc1(Nf, &localDiff);
1448:   for (s = 0; s < Nds; ++s) {
1449:     PetscDS          ds;
1450:     DMLabel          label;
1451:     IS               fieldIS, pointIS;
1452:     const PetscInt  *fields, *points = NULL;
1453:     PetscQuadrature  quad;
1454:     const PetscReal *quadPoints, *quadWeights;
1455:     PetscFEGeom      fegeom;
1456:     PetscReal       *coords, *gcoords;
1457:     PetscScalar     *funcVal, *interpolant;
1458:     PetscBool        isCohesive;
1459:     PetscInt         qNc, Nq, totNc, cStart = 0, cEnd, c, dsNf;

1461:     DMGetRegionNumDS(dm, s, &label, &fieldIS, &ds);
1462:     ISGetIndices(fieldIS, &fields);
1463:     PetscDSIsCohesive(ds, &isCohesive);
1464:     PetscDSGetNumFields(ds, &dsNf);
1465:     PetscDSGetTotalComponents(ds, &totNc);
1466:     PetscDSGetQuadrature(ds, &quad);
1467:     PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights);
1469:     PetscCalloc6(totNc, &funcVal, totNc, &interpolant, dE * (Nq + 1), &coords, Nq, &fegeom.detJ, dE * dE * Nq, &fegeom.J, dE * dE * Nq, &fegeom.invJ);
1470:     if (!label) {
1471:       DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd);
1472:     } else {
1473:       DMLabelGetStratumIS(label, 1, &pointIS);
1474:       ISGetLocalSize(pointIS, &cEnd);
1475:       ISGetIndices(pointIS, &points);
1476:     }
1477:     for (c = cStart; c < cEnd; ++c) {
1478:       const PetscInt  cell = points ? points[c] : c;
1479:       PetscScalar    *x    = NULL;
1480:       const PetscInt *cone;
1481:       PetscInt        qc = 0, fOff = 0, dep;

1483:       DMLabelGetValue(depthLabel, cell, &dep);
1484:       if (dep != depth - 1) continue;
1485:       if (isCohesive) {
1486:         DMPlexGetCone(dm, cell, &cone);
1487:         DMPlexComputeCellGeometryFEM(dm, cone[0], quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ);
1488:       } else {
1489:         DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ);
1490:       }
1491:       DMPlexVecGetClosure(dm, NULL, localX, cell, NULL, &x);
1492:       for (f = 0; f < dsNf; ++f) {
1493:         PetscObject  obj;
1494:         PetscClassId id;
1495:         void *const  ctx = ctxs ? ctxs[fields[f]] : NULL;
1496:         PetscInt     Nb, Nc, q, fc;
1497:         PetscReal    elemDiff = 0.0;
1498:         PetscBool    cohesive;

1500:         PetscDSGetCohesive(ds, f, &cohesive);
1501:         if (isCohesive && !cohesive) continue;
1502:         PetscDSGetDiscretization(ds, f, &obj);
1503:         PetscObjectGetClassId(obj, &id);
1504:         if (id == PETSCFE_CLASSID) {
1505:           PetscFEGetNumComponents((PetscFE)obj, &Nc);
1506:           PetscFEGetDimension((PetscFE)obj, &Nb);
1507:         } else if (id == PETSCFV_CLASSID) {
1508:           PetscFVGetNumComponents((PetscFV)obj, &Nc);
1509:           Nb = 1;
1510:         } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, fields[f]);
1511:         if (debug) {
1512:           char title[1024];
1513:           PetscSNPrintf(title, 1023, "Solution for Field %" PetscInt_FMT, fields[f]);
1514:           DMPrintCellVector(cell, title, Nb, &x[fOff]);
1515:         }
1516:         for (q = 0; q < Nq; ++q) {
1517:           PetscFEGeom    qgeom;

1520:           qgeom.dimEmbed = fegeom.dimEmbed;
1521:           qgeom.J        = &fegeom.J[q * dE * dE];
1522:           qgeom.invJ     = &fegeom.invJ[q * dE * dE];
1523:           qgeom.detJ     = &fegeom.detJ[q];
1525:           if (transform) {
1526:             gcoords = &coords[dE * Nq];
1527:             DMPlexBasisTransformApplyReal_Internal(dm, &coords[dE * q], PETSC_TRUE, dE, &coords[dE * q], gcoords, dm->transformCtx);
1528:           } else {
1529:             gcoords = &coords[dE * q];
1530:           }
1531:           for (fc = 0; fc < Nc; ++fc) funcVal[fc] = 0.;
1532:           (*funcs[fields[f]])(dE, time, gcoords, Nc, funcVal, ctx);
1533:           if (ierr) {
1534:             DMPlexVecRestoreClosure(dm, NULL, localX, cell, NULL, &x);
1535:             DMRestoreLocalVector(dm, &localX);
1536:             PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ);
1537:           }
1538:           if (transform) DMPlexBasisTransformApply_Internal(dm, &coords[dE * q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx);
1539:           /* Call once for each face, except for lagrange field */
1540:           if (id == PETSCFE_CLASSID) PetscFEInterpolate_Static((PetscFE)obj, &x[fOff], &qgeom, q, interpolant);
1541:           else if (id == PETSCFV_CLASSID) PetscFVInterpolate_Static((PetscFV)obj, &x[fOff], q, interpolant);
1542:           else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, fields[f]);
1543:           for (fc = 0; fc < Nc; ++fc) {
1544:             const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
1545:             if (debug)
1546:               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.),
1547:                                     (double)(PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q])));
1548:             elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
1549:           }
1550:         }
1551:         fOff += Nb;
1552:         qc += Nc;
1553:         localDiff[fields[f]] += elemDiff;
1554:         if (debug) PetscPrintf(PETSC_COMM_SELF, "  cell %" PetscInt_FMT " field %" PetscInt_FMT " cum diff %g\n", cell, fields[f], (double)localDiff[fields[f]]);
1555:       }
1556:       DMPlexVecRestoreClosure(dm, NULL, localX, cell, NULL, &x);
1557:     }
1558:     if (label) {
1559:       ISRestoreIndices(pointIS, &points);
1560:       ISDestroy(&pointIS);
1561:     }
1562:     ISRestoreIndices(fieldIS, &fields);
1563:     PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ);
1564:   }
1565:   DMRestoreLocalVector(dm, &localX);
1566:   MPIU_Allreduce(localDiff, diff, Nf, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm));
1567:   PetscFree(localDiff);
1568:   for (f = 0; f < Nf; ++f) diff[f] = PetscSqrtReal(diff[f]);
1569:   return 0;
1570: }

1572: /*@C
1573:   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.

1575:   Collective on dm

1577:   Input Parameters:
1578: + dm    - The DM
1579: . time  - The time
1580: . funcs - The functions to evaluate for each field component: NULL means that component does not contribute to error calculation
1581: . ctxs  - Optional array of contexts to pass to each function, or NULL.
1582: - X     - The coefficient vector u_h

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

1587:   Level: developer

1589: .seealso: `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
1590: @*/
1591: PetscErrorCode DMPlexComputeL2DiffVec(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, Vec D)
1592: {
1593:   PetscSection     section;
1594:   PetscQuadrature  quad;
1595:   Vec              localX;
1596:   PetscFEGeom      fegeom;
1597:   PetscScalar     *funcVal, *interpolant;
1598:   PetscReal       *coords;
1599:   const PetscReal *quadPoints, *quadWeights;
1600:   PetscInt         dim, coordDim, numFields, numComponents = 0, qNc, Nq, cStart, cEnd, c, field, fieldOffset;

1602:   VecSet(D, 0.0);
1603:   DMGetDimension(dm, &dim);
1604:   DMGetCoordinateDim(dm, &coordDim);
1605:   DMGetLocalSection(dm, &section);
1606:   PetscSectionGetNumFields(section, &numFields);
1607:   DMGetLocalVector(dm, &localX);
1608:   DMProjectFunctionLocal(dm, time, funcs, ctxs, INSERT_BC_VALUES, localX);
1609:   DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX);
1610:   DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX);
1611:   for (field = 0; field < numFields; ++field) {
1612:     PetscObject  obj;
1613:     PetscClassId id;
1614:     PetscInt     Nc;

1616:     DMGetField(dm, field, NULL, &obj);
1617:     PetscObjectGetClassId(obj, &id);
1618:     if (id == PETSCFE_CLASSID) {
1619:       PetscFE fe = (PetscFE)obj;

1621:       PetscFEGetQuadrature(fe, &quad);
1622:       PetscFEGetNumComponents(fe, &Nc);
1623:     } else if (id == PETSCFV_CLASSID) {
1624:       PetscFV fv = (PetscFV)obj;

1626:       PetscFVGetQuadrature(fv, &quad);
1627:       PetscFVGetNumComponents(fv, &Nc);
1628:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1629:     numComponents += Nc;
1630:   }
1631:   PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights);
1633:   PetscMalloc6(numComponents, &funcVal, numComponents, &interpolant, coordDim * Nq, &coords, Nq, &fegeom.detJ, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ);
1634:   DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd);
1635:   for (c = cStart; c < cEnd; ++c) {
1636:     PetscScalar *x        = NULL;
1637:     PetscScalar  elemDiff = 0.0;
1638:     PetscInt     qc       = 0;

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

1643:     for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1644:       PetscObject  obj;
1645:       PetscClassId id;
1646:       void *const  ctx = ctxs ? ctxs[field] : NULL;
1647:       PetscInt     Nb, Nc, q, fc;

1649:       DMGetField(dm, field, NULL, &obj);
1650:       PetscObjectGetClassId(obj, &id);
1651:       if (id == PETSCFE_CLASSID) {
1652:         PetscFEGetNumComponents((PetscFE)obj, &Nc);
1653:         PetscFEGetDimension((PetscFE)obj, &Nb);
1654:       } else if (id == PETSCFV_CLASSID) {
1655:         PetscFVGetNumComponents((PetscFV)obj, &Nc);
1656:         Nb = 1;
1657:       } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1658:       if (funcs[field]) {
1659:         for (q = 0; q < Nq; ++q) {
1660:           PetscFEGeom qgeom;

1662:           qgeom.dimEmbed = fegeom.dimEmbed;
1663:           qgeom.J        = &fegeom.J[q * coordDim * coordDim];
1664:           qgeom.invJ     = &fegeom.invJ[q * coordDim * coordDim];
1665:           qgeom.detJ     = &fegeom.detJ[q];
1667:           (*funcs[field])(coordDim, time, &coords[q * coordDim], Nc, funcVal, ctx);
1668: #if defined(needs_fix_with_return_code_argument)
1669:           if (ierr) {
1670:             DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x);
1671:             PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ);
1672:             DMRestoreLocalVector(dm, &localX);
1673:           }
1674: #endif
1675:           if (id == PETSCFE_CLASSID) PetscFEInterpolate_Static((PetscFE)obj, &x[fieldOffset], &qgeom, q, interpolant);
1676:           else if (id == PETSCFV_CLASSID) PetscFVInterpolate_Static((PetscFV)obj, &x[fieldOffset], q, interpolant);
1677:           else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1678:           for (fc = 0; fc < Nc; ++fc) {
1679:             const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
1680:             elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
1681:           }
1682:         }
1683:       }
1684:       fieldOffset += Nb;
1685:       qc += Nc;
1686:     }
1687:     DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x);
1688:     VecSetValue(D, c - cStart, elemDiff, INSERT_VALUES);
1689:   }
1690:   PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ);
1691:   DMRestoreLocalVector(dm, &localX);
1692:   VecSqrtAbs(D);
1693:   return 0;
1694: }

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

1699:   Collective on dm

1701:   Input Parameters:
1702: + dm - The DM
1703: - locX  - The coefficient vector u_h

1705:   Output Parameter:
1706: . locC - A Vec which holds the Clement interpolant of the function

1708:   Notes:
1709:   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

1711:   Level: developer

1713: .seealso: `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
1714: @*/
1715: PetscErrorCode DMPlexComputeClementInterpolant(DM dm, Vec locX, Vec locC)
1716: {
1717:   PetscInt         debug = ((DM_Plex *)dm->data)->printFEM;
1718:   DM               dmc;
1719:   PetscQuadrature  quad;
1720:   PetscScalar     *interpolant, *valsum;
1721:   PetscFEGeom      fegeom;
1722:   PetscReal       *coords;
1723:   const PetscReal *quadPoints, *quadWeights;
1724:   PetscInt         dim, cdim, Nf, f, Nc = 0, Nq, qNc, cStart, cEnd, vStart, vEnd, v;

1726:   PetscCitationsRegister(ClementCitation, &Clementcite);
1727:   VecGetDM(locC, &dmc);
1728:   VecSet(locC, 0.0);
1729:   DMGetDimension(dm, &dim);
1730:   DMGetCoordinateDim(dm, &cdim);
1731:   fegeom.dimEmbed = cdim;
1732:   DMGetNumFields(dm, &Nf);
1734:   for (f = 0; f < Nf; ++f) {
1735:     PetscObject  obj;
1736:     PetscClassId id;
1737:     PetscInt     fNc;

1739:     DMGetField(dm, f, NULL, &obj);
1740:     PetscObjectGetClassId(obj, &id);
1741:     if (id == PETSCFE_CLASSID) {
1742:       PetscFE fe = (PetscFE)obj;

1744:       PetscFEGetQuadrature(fe, &quad);
1745:       PetscFEGetNumComponents(fe, &fNc);
1746:     } else if (id == PETSCFV_CLASSID) {
1747:       PetscFV fv = (PetscFV)obj;

1749:       PetscFVGetQuadrature(fv, &quad);
1750:       PetscFVGetNumComponents(fv, &fNc);
1751:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
1752:     Nc += fNc;
1753:   }
1754:   PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights);
1756:   PetscMalloc6(Nc * 2, &valsum, Nc, &interpolant, cdim * Nq, &coords, Nq, &fegeom.detJ, cdim * cdim * Nq, &fegeom.J, cdim * cdim * Nq, &fegeom.invJ);
1757:   DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd);
1758:   DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd);
1759:   for (v = vStart; v < vEnd; ++v) {
1760:     PetscScalar volsum = 0.0;
1761:     PetscInt   *star   = NULL;
1762:     PetscInt    starSize, st, fc;

1764:     PetscArrayzero(valsum, Nc);
1765:     DMPlexGetTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star);
1766:     for (st = 0; st < starSize * 2; st += 2) {
1767:       const PetscInt cell = star[st];
1768:       PetscScalar   *val  = &valsum[Nc];
1769:       PetscScalar   *x    = NULL;
1770:       PetscReal      vol  = 0.0;
1771:       PetscInt       foff = 0;

1773:       if ((cell < cStart) || (cell >= cEnd)) continue;
1774:       DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ);
1775:       DMPlexVecGetClosure(dm, NULL, locX, cell, NULL, &x);
1776:       for (f = 0; f < Nf; ++f) {
1777:         PetscObject  obj;
1778:         PetscClassId id;
1779:         PetscInt     Nb, fNc, q;

1781:         PetscArrayzero(val, Nc);
1782:         DMGetField(dm, f, NULL, &obj);
1783:         PetscObjectGetClassId(obj, &id);
1784:         if (id == PETSCFE_CLASSID) {
1785:           PetscFEGetNumComponents((PetscFE)obj, &fNc);
1786:           PetscFEGetDimension((PetscFE)obj, &Nb);
1787:         } else if (id == PETSCFV_CLASSID) {
1788:           PetscFVGetNumComponents((PetscFV)obj, &fNc);
1789:           Nb = 1;
1790:         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
1791:         for (q = 0; q < Nq; ++q) {
1792:           const PetscReal wt = quadWeights[q] * fegeom.detJ[q];
1793:           PetscFEGeom     qgeom;

1795:           qgeom.dimEmbed = fegeom.dimEmbed;
1796:           qgeom.J        = &fegeom.J[q * cdim * cdim];
1797:           qgeom.invJ     = &fegeom.invJ[q * cdim * cdim];
1798:           qgeom.detJ     = &fegeom.detJ[q];
1800:           if (id == PETSCFE_CLASSID) PetscFEInterpolate_Static((PetscFE)obj, &x[foff], &qgeom, q, interpolant);
1801:           else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
1802:           for (fc = 0; fc < fNc; ++fc) val[foff + fc] += interpolant[fc] * wt;
1803:           vol += wt;
1804:         }
1805:         foff += Nb;
1806:       }
1807:       DMPlexVecRestoreClosure(dm, NULL, locX, cell, NULL, &x);
1808:       for (fc = 0; fc < Nc; ++fc) valsum[fc] += val[fc];
1809:       volsum += vol;
1810:       if (debug) {
1811:         PetscPrintf(PETSC_COMM_SELF, "Vertex %" PetscInt_FMT " Cell %" PetscInt_FMT " value: [", v, cell);
1812:         for (fc = 0; fc < Nc; ++fc) {
1813:           if (fc) PetscPrintf(PETSC_COMM_SELF, ", ");
1814:           PetscPrintf(PETSC_COMM_SELF, "%g", (double)PetscRealPart(val[fc]));
1815:         }
1816:         PetscPrintf(PETSC_COMM_SELF, "]\n");
1817:       }
1818:     }
1819:     for (fc = 0; fc < Nc; ++fc) valsum[fc] /= volsum;
1820:     DMPlexRestoreTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star);
1821:     DMPlexVecSetClosure(dmc, NULL, locC, v, valsum, INSERT_VALUES);
1822:   }
1823:   PetscFree6(valsum, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ);
1824:   return 0;
1825: }

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

1830:   Collective on dm

1832:   Input Parameters:
1833: + dm - The DM
1834: - locX  - The coefficient vector u_h

1836:   Output Parameter:
1837: . locC - A Vec which holds the Clement interpolant of the gradient

1839:   Notes:
1840:   \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

1842:   Level: developer

1844: .seealso: `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
1845: @*/
1846: PetscErrorCode DMPlexComputeGradientClementInterpolant(DM dm, Vec locX, Vec locC)
1847: {
1848:   DM_Plex         *mesh  = (DM_Plex *)dm->data;
1849:   PetscInt         debug = mesh->printFEM;
1850:   DM               dmC;
1851:   PetscQuadrature  quad;
1852:   PetscScalar     *interpolant, *gradsum;
1853:   PetscFEGeom      fegeom;
1854:   PetscReal       *coords;
1855:   const PetscReal *quadPoints, *quadWeights;
1856:   PetscInt         dim, coordDim, numFields, numComponents = 0, qNc, Nq, cStart, cEnd, vStart, vEnd, v, field, fieldOffset;

1858:   PetscCitationsRegister(ClementCitation, &Clementcite);
1859:   VecGetDM(locC, &dmC);
1860:   VecSet(locC, 0.0);
1861:   DMGetDimension(dm, &dim);
1862:   DMGetCoordinateDim(dm, &coordDim);
1863:   fegeom.dimEmbed = coordDim;
1864:   DMGetNumFields(dm, &numFields);
1866:   for (field = 0; field < numFields; ++field) {
1867:     PetscObject  obj;
1868:     PetscClassId id;
1869:     PetscInt     Nc;

1871:     DMGetField(dm, field, NULL, &obj);
1872:     PetscObjectGetClassId(obj, &id);
1873:     if (id == PETSCFE_CLASSID) {
1874:       PetscFE fe = (PetscFE)obj;

1876:       PetscFEGetQuadrature(fe, &quad);
1877:       PetscFEGetNumComponents(fe, &Nc);
1878:     } else if (id == PETSCFV_CLASSID) {
1879:       PetscFV fv = (PetscFV)obj;

1881:       PetscFVGetQuadrature(fv, &quad);
1882:       PetscFVGetNumComponents(fv, &Nc);
1883:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1884:     numComponents += Nc;
1885:   }
1886:   PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights);
1888:   PetscMalloc6(coordDim * numComponents * 2, &gradsum, coordDim * numComponents, &interpolant, coordDim * Nq, &coords, Nq, &fegeom.detJ, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ);
1889:   DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd);
1890:   DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd);
1891:   for (v = vStart; v < vEnd; ++v) {
1892:     PetscScalar volsum = 0.0;
1893:     PetscInt   *star   = NULL;
1894:     PetscInt    starSize, st, d, fc;

1896:     PetscArrayzero(gradsum, coordDim * numComponents);
1897:     DMPlexGetTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star);
1898:     for (st = 0; st < starSize * 2; st += 2) {
1899:       const PetscInt cell = star[st];
1900:       PetscScalar   *grad = &gradsum[coordDim * numComponents];
1901:       PetscScalar   *x    = NULL;
1902:       PetscReal      vol  = 0.0;

1904:       if ((cell < cStart) || (cell >= cEnd)) continue;
1905:       DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ);
1906:       DMPlexVecGetClosure(dm, NULL, locX, cell, NULL, &x);
1907:       for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1908:         PetscObject  obj;
1909:         PetscClassId id;
1910:         PetscInt     Nb, Nc, q, qc = 0;

1912:         PetscArrayzero(grad, coordDim * numComponents);
1913:         DMGetField(dm, field, NULL, &obj);
1914:         PetscObjectGetClassId(obj, &id);
1915:         if (id == PETSCFE_CLASSID) {
1916:           PetscFEGetNumComponents((PetscFE)obj, &Nc);
1917:           PetscFEGetDimension((PetscFE)obj, &Nb);
1918:         } else if (id == PETSCFV_CLASSID) {
1919:           PetscFVGetNumComponents((PetscFV)obj, &Nc);
1920:           Nb = 1;
1921:         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1922:         for (q = 0; q < Nq; ++q) {
1923:           PetscFEGeom qgeom;

1925:           qgeom.dimEmbed = fegeom.dimEmbed;
1926:           qgeom.J        = &fegeom.J[q * coordDim * coordDim];
1927:           qgeom.invJ     = &fegeom.invJ[q * coordDim * coordDim];
1928:           qgeom.detJ     = &fegeom.detJ[q];
1930:           if (id == PETSCFE_CLASSID) PetscFEInterpolateGradient_Static((PetscFE)obj, 1, &x[fieldOffset], &qgeom, q, interpolant);
1931:           else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1932:           for (fc = 0; fc < Nc; ++fc) {
1933:             const PetscReal wt = quadWeights[q * qNc + qc];

1935:             for (d = 0; d < coordDim; ++d) grad[fc * coordDim + d] += interpolant[fc * dim + d] * wt * fegeom.detJ[q];
1936:           }
1937:           vol += quadWeights[q * qNc] * fegeom.detJ[q];
1938:         }
1939:         fieldOffset += Nb;
1940:         qc += Nc;
1941:       }
1942:       DMPlexVecRestoreClosure(dm, NULL, locX, cell, NULL, &x);
1943:       for (fc = 0; fc < numComponents; ++fc) {
1944:         for (d = 0; d < coordDim; ++d) gradsum[fc * coordDim + d] += grad[fc * coordDim + d];
1945:       }
1946:       volsum += vol;
1947:       if (debug) {
1948:         PetscPrintf(PETSC_COMM_SELF, "Vertex %" PetscInt_FMT " Cell %" PetscInt_FMT " gradient: [", v, cell);
1949:         for (fc = 0; fc < numComponents; ++fc) {
1950:           for (d = 0; d < coordDim; ++d) {
1951:             if (fc || d > 0) PetscPrintf(PETSC_COMM_SELF, ", ");
1952:             PetscPrintf(PETSC_COMM_SELF, "%g", (double)PetscRealPart(grad[fc * coordDim + d]));
1953:           }
1954:         }
1955:         PetscPrintf(PETSC_COMM_SELF, "]\n");
1956:       }
1957:     }
1958:     for (fc = 0; fc < numComponents; ++fc) {
1959:       for (d = 0; d < coordDim; ++d) gradsum[fc * coordDim + d] /= volsum;
1960:     }
1961:     DMPlexRestoreTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star);
1962:     DMPlexVecSetClosure(dmC, NULL, locC, v, gradsum, INSERT_VALUES);
1963:   }
1964:   PetscFree6(gradsum, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ);
1965:   return 0;
1966: }

1968: static PetscErrorCode DMPlexComputeIntegral_Internal(DM dm, Vec X, PetscInt cStart, PetscInt cEnd, PetscScalar *cintegral, void *user)
1969: {
1970:   DM           dmAux = NULL;
1971:   PetscDS      prob, probAux = NULL;
1972:   PetscSection section, sectionAux;
1973:   Vec          locX, locA;
1974:   PetscInt     dim, numCells = cEnd - cStart, c, f;
1975:   PetscBool    useFVM = PETSC_FALSE;
1976:   /* DS */
1977:   PetscInt           Nf, totDim, *uOff, *uOff_x, numConstants;
1978:   PetscInt           NfAux, totDimAux, *aOff;
1979:   PetscScalar       *u, *a;
1980:   const PetscScalar *constants;
1981:   /* Geometry */
1982:   PetscFEGeom       *cgeomFEM;
1983:   DM                 dmGrad;
1984:   PetscQuadrature    affineQuad      = NULL;
1985:   Vec                cellGeometryFVM = NULL, faceGeometryFVM = NULL, locGrad = NULL;
1986:   PetscFVCellGeom   *cgeomFVM;
1987:   const PetscScalar *lgrad;
1988:   PetscInt           maxDegree;
1989:   DMField            coordField;
1990:   IS                 cellIS;

1992:   DMGetDS(dm, &prob);
1993:   DMGetDimension(dm, &dim);
1994:   DMGetLocalSection(dm, &section);
1995:   DMGetNumFields(dm, &Nf);
1996:   /* Determine which discretizations we have */
1997:   for (f = 0; f < Nf; ++f) {
1998:     PetscObject  obj;
1999:     PetscClassId id;

2001:     PetscDSGetDiscretization(prob, f, &obj);
2002:     PetscObjectGetClassId(obj, &id);
2003:     if (id == PETSCFV_CLASSID) useFVM = PETSC_TRUE;
2004:   }
2005:   /* Get local solution with boundary values */
2006:   DMGetLocalVector(dm, &locX);
2007:   DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL);
2008:   DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX);
2009:   DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX);
2010:   /* Read DS information */
2011:   PetscDSGetTotalDimension(prob, &totDim);
2012:   PetscDSGetComponentOffsets(prob, &uOff);
2013:   PetscDSGetComponentDerivativeOffsets(prob, &uOff_x);
2014:   ISCreateStride(PETSC_COMM_SELF, numCells, cStart, 1, &cellIS);
2015:   PetscDSGetConstants(prob, &numConstants, &constants);
2016:   /* Read Auxiliary DS information */
2017:   DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA);
2018:   if (locA) {
2019:     VecGetDM(locA, &dmAux);
2020:     DMGetDS(dmAux, &probAux);
2021:     PetscDSGetNumFields(probAux, &NfAux);
2022:     DMGetLocalSection(dmAux, &sectionAux);
2023:     PetscDSGetTotalDimension(probAux, &totDimAux);
2024:     PetscDSGetComponentOffsets(probAux, &aOff);
2025:   }
2026:   /* Allocate data  arrays */
2027:   PetscCalloc1(numCells * totDim, &u);
2028:   if (dmAux) PetscMalloc1(numCells * totDimAux, &a);
2029:   /* Read out geometry */
2030:   DMGetCoordinateField(dm, &coordField);
2031:   DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree);
2032:   if (maxDegree <= 1) {
2033:     DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad);
2034:     if (affineQuad) DMFieldCreateFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &cgeomFEM);
2035:   }
2036:   if (useFVM) {
2037:     PetscFV   fv = NULL;
2038:     Vec       grad;
2039:     PetscInt  fStart, fEnd;
2040:     PetscBool compGrad;

2042:     for (f = 0; f < Nf; ++f) {
2043:       PetscObject  obj;
2044:       PetscClassId id;

2046:       PetscDSGetDiscretization(prob, f, &obj);
2047:       PetscObjectGetClassId(obj, &id);
2048:       if (id == PETSCFV_CLASSID) {
2049:         fv = (PetscFV)obj;
2050:         break;
2051:       }
2052:     }
2053:     PetscFVGetComputeGradients(fv, &compGrad);
2054:     PetscFVSetComputeGradients(fv, PETSC_TRUE);
2055:     DMPlexComputeGeometryFVM(dm, &cellGeometryFVM, &faceGeometryFVM);
2056:     DMPlexComputeGradientFVM(dm, fv, faceGeometryFVM, cellGeometryFVM, &dmGrad);
2057:     PetscFVSetComputeGradients(fv, compGrad);
2058:     VecGetArrayRead(cellGeometryFVM, (const PetscScalar **)&cgeomFVM);
2059:     /* Reconstruct and limit cell gradients */
2060:     DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd);
2061:     DMGetGlobalVector(dmGrad, &grad);
2062:     DMPlexReconstructGradients_Internal(dm, fv, fStart, fEnd, faceGeometryFVM, cellGeometryFVM, locX, grad);
2063:     /* Communicate gradient values */
2064:     DMGetLocalVector(dmGrad, &locGrad);
2065:     DMGlobalToLocalBegin(dmGrad, grad, INSERT_VALUES, locGrad);
2066:     DMGlobalToLocalEnd(dmGrad, grad, INSERT_VALUES, locGrad);
2067:     DMRestoreGlobalVector(dmGrad, &grad);
2068:     /* Handle non-essential (e.g. outflow) boundary values */
2069:     DMPlexInsertBoundaryValues(dm, PETSC_FALSE, locX, 0.0, faceGeometryFVM, cellGeometryFVM, locGrad);
2070:     VecGetArrayRead(locGrad, &lgrad);
2071:   }
2072:   /* Read out data from inputs */
2073:   for (c = cStart; c < cEnd; ++c) {
2074:     PetscScalar *x = NULL;
2075:     PetscInt     i;

2077:     DMPlexVecGetClosure(dm, section, locX, c, NULL, &x);
2078:     for (i = 0; i < totDim; ++i) u[c * totDim + i] = x[i];
2079:     DMPlexVecRestoreClosure(dm, section, locX, c, NULL, &x);
2080:     if (dmAux) {
2081:       DMPlexVecGetClosure(dmAux, sectionAux, locA, c, NULL, &x);
2082:       for (i = 0; i < totDimAux; ++i) a[c * totDimAux + i] = x[i];
2083:       DMPlexVecRestoreClosure(dmAux, sectionAux, locA, c, NULL, &x);
2084:     }
2085:   }
2086:   /* Do integration for each field */
2087:   for (f = 0; f < Nf; ++f) {
2088:     PetscObject  obj;
2089:     PetscClassId id;
2090:     PetscInt     numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;

2092:     PetscDSGetDiscretization(prob, f, &obj);
2093:     PetscObjectGetClassId(obj, &id);
2094:     if (id == PETSCFE_CLASSID) {
2095:       PetscFE         fe = (PetscFE)obj;
2096:       PetscQuadrature q;
2097:       PetscFEGeom    *chunkGeom = NULL;
2098:       PetscInt        Nq, Nb;

2100:       PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches);
2101:       PetscFEGetQuadrature(fe, &q);
2102:       PetscQuadratureGetData(q, NULL, NULL, &Nq, NULL, NULL);
2103:       PetscFEGetDimension(fe, &Nb);
2104:       blockSize = Nb * Nq;
2105:       batchSize = numBlocks * blockSize;
2106:       PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches);
2107:       numChunks = numCells / (numBatches * batchSize);
2108:       Ne        = numChunks * numBatches * batchSize;
2109:       Nr        = numCells % (numBatches * batchSize);
2110:       offset    = numCells - Nr;
2111:       if (!affineQuad) DMFieldCreateFEGeom(coordField, cellIS, q, PETSC_FALSE, &cgeomFEM);
2112:       PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom);
2113:       PetscFEIntegrate(prob, f, Ne, chunkGeom, u, probAux, a, cintegral);
2114:       PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &chunkGeom);
2115:       PetscFEIntegrate(prob, f, Nr, chunkGeom, &u[offset * totDim], probAux, &a[offset * totDimAux], &cintegral[offset * Nf]);
2116:       PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &chunkGeom);
2117:       if (!affineQuad) PetscFEGeomDestroy(&cgeomFEM);
2118:     } else if (id == PETSCFV_CLASSID) {
2119:       PetscInt       foff;
2120:       PetscPointFunc obj_func;
2121:       PetscScalar    lint;

2123:       PetscDSGetObjective(prob, f, &obj_func);
2124:       PetscDSGetFieldOffset(prob, f, &foff);
2125:       if (obj_func) {
2126:         for (c = 0; c < numCells; ++c) {
2127:           PetscScalar *u_x;

2129:           DMPlexPointLocalRead(dmGrad, c, lgrad, &u_x);
2130:           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);
2131:           cintegral[c * Nf + f] += PetscRealPart(lint) * cgeomFVM[c].volume;
2132:         }
2133:       }
2134:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
2135:   }
2136:   /* Cleanup data arrays */
2137:   if (useFVM) {
2138:     VecRestoreArrayRead(locGrad, &lgrad);
2139:     VecRestoreArrayRead(cellGeometryFVM, (const PetscScalar **)&cgeomFVM);
2140:     DMRestoreLocalVector(dmGrad, &locGrad);
2141:     VecDestroy(&faceGeometryFVM);
2142:     VecDestroy(&cellGeometryFVM);
2143:     DMDestroy(&dmGrad);
2144:   }
2145:   if (dmAux) PetscFree(a);
2146:   PetscFree(u);
2147:   /* Cleanup */
2148:   if (affineQuad) PetscFEGeomDestroy(&cgeomFEM);
2149:   PetscQuadratureDestroy(&affineQuad);
2150:   ISDestroy(&cellIS);
2151:   DMRestoreLocalVector(dm, &locX);
2152:   return 0;
2153: }

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

2158:   Input Parameters:
2159: + dm - The mesh
2160: . X  - Global input vector
2161: - user - The user context

2163:   Output Parameter:
2164: . integral - Integral for each field

2166:   Level: developer

2168: .seealso: `DMPlexSNESComputeResidualFEM()`
2169: @*/
2170: PetscErrorCode DMPlexComputeIntegralFEM(DM dm, Vec X, PetscScalar *integral, void *user)
2171: {
2172:   DM_Plex     *mesh = (DM_Plex *)dm->data;
2173:   PetscScalar *cintegral, *lintegral;
2174:   PetscInt     Nf, f, cellHeight, cStart, cEnd, cell;

2179:   PetscLogEventBegin(DMPLEX_IntegralFEM, dm, 0, 0, 0);
2180:   DMGetNumFields(dm, &Nf);
2181:   DMPlexGetVTKCellHeight(dm, &cellHeight);
2182:   DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd);
2183:   /* TODO Introduce a loop over large chunks (right now this is a single chunk) */
2184:   PetscCalloc2(Nf, &lintegral, (cEnd - cStart) * Nf, &cintegral);
2185:   DMPlexComputeIntegral_Internal(dm, X, cStart, cEnd, cintegral, user);
2186:   /* Sum up values */
2187:   for (cell = cStart; cell < cEnd; ++cell) {
2188:     const PetscInt c = cell - cStart;

2190:     if (mesh->printFEM > 1) DMPrintCellVector(cell, "Cell Integral", Nf, &cintegral[c * Nf]);
2191:     for (f = 0; f < Nf; ++f) lintegral[f] += cintegral[c * Nf + f];
2192:   }
2193:   MPIU_Allreduce(lintegral, integral, Nf, MPIU_SCALAR, MPIU_SUM, PetscObjectComm((PetscObject)dm));
2194:   if (mesh->printFEM) {
2195:     PetscPrintf(PetscObjectComm((PetscObject)dm), "Integral:");
2196:     for (f = 0; f < Nf; ++f) PetscPrintf(PetscObjectComm((PetscObject)dm), " %g", (double)PetscRealPart(integral[f]));
2197:     PetscPrintf(PetscObjectComm((PetscObject)dm), "\n");
2198:   }
2199:   PetscFree2(lintegral, cintegral);
2200:   PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0);
2201:   return 0;
2202: }

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

2207:   Input Parameters:
2208: + dm - The mesh
2209: . X  - Global input vector
2210: - user - The user context

2212:   Output Parameter:
2213: . integral - Cellwise integrals for each field

2215:   Level: developer

2217: .seealso: `DMPlexSNESComputeResidualFEM()`
2218: @*/
2219: PetscErrorCode DMPlexComputeCellwiseIntegralFEM(DM dm, Vec X, Vec F, void *user)
2220: {
2221:   DM_Plex     *mesh = (DM_Plex *)dm->data;
2222:   DM           dmF;
2223:   PetscSection sectionF;
2224:   PetscScalar *cintegral, *af;
2225:   PetscInt     Nf, f, cellHeight, cStart, cEnd, cell;

2230:   PetscLogEventBegin(DMPLEX_IntegralFEM, dm, 0, 0, 0);
2231:   DMGetNumFields(dm, &Nf);
2232:   DMPlexGetVTKCellHeight(dm, &cellHeight);
2233:   DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd);
2234:   /* TODO Introduce a loop over large chunks (right now this is a single chunk) */
2235:   PetscCalloc1((cEnd - cStart) * Nf, &cintegral);
2236:   DMPlexComputeIntegral_Internal(dm, X, cStart, cEnd, cintegral, user);
2237:   /* Put values in F*/
2238:   VecGetDM(F, &dmF);
2239:   DMGetLocalSection(dmF, &sectionF);
2240:   VecGetArray(F, &af);
2241:   for (cell = cStart; cell < cEnd; ++cell) {
2242:     const PetscInt c = cell - cStart;
2243:     PetscInt       dof, off;

2245:     if (mesh->printFEM > 1) DMPrintCellVector(cell, "Cell Integral", Nf, &cintegral[c * Nf]);
2246:     PetscSectionGetDof(sectionF, cell, &dof);
2247:     PetscSectionGetOffset(sectionF, cell, &off);
2249:     for (f = 0; f < Nf; ++f) af[off + f] = cintegral[c * Nf + f];
2250:   }
2251:   VecRestoreArray(F, &af);
2252:   PetscFree(cintegral);
2253:   PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0);
2254:   return 0;
2255: }

2257: 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)
2258: {
2259:   DM                 plex = NULL, plexA = NULL;
2260:   DMEnclosureType    encAux;
2261:   PetscDS            prob, probAux       = NULL;
2262:   PetscSection       section, sectionAux = NULL;
2263:   Vec                locA = NULL;
2264:   DMField            coordField;
2265:   PetscInt           Nf, totDim, *uOff, *uOff_x;
2266:   PetscInt           NfAux = 0, totDimAux = 0, *aOff = NULL;
2267:   PetscScalar       *u, *a = NULL;
2268:   const PetscScalar *constants;
2269:   PetscInt           numConstants, f;

2271:   DMGetCoordinateField(dm, &coordField);
2272:   DMConvert(dm, DMPLEX, &plex);
2273:   DMGetDS(dm, &prob);
2274:   DMGetLocalSection(dm, &section);
2275:   PetscSectionGetNumFields(section, &Nf);
2276:   /* Determine which discretizations we have */
2277:   for (f = 0; f < Nf; ++f) {
2278:     PetscObject  obj;
2279:     PetscClassId id;

2281:     PetscDSGetDiscretization(prob, f, &obj);
2282:     PetscObjectGetClassId(obj, &id);
2284:   }
2285:   /* Read DS information */
2286:   PetscDSGetTotalDimension(prob, &totDim);
2287:   PetscDSGetComponentOffsets(prob, &uOff);
2288:   PetscDSGetComponentDerivativeOffsets(prob, &uOff_x);
2289:   PetscDSGetConstants(prob, &numConstants, &constants);
2290:   /* Read Auxiliary DS information */
2291:   DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA);
2292:   if (locA) {
2293:     DM dmAux;

2295:     VecGetDM(locA, &dmAux);
2296:     DMGetEnclosureRelation(dmAux, dm, &encAux);
2297:     DMConvert(dmAux, DMPLEX, &plexA);
2298:     DMGetDS(dmAux, &probAux);
2299:     PetscDSGetNumFields(probAux, &NfAux);
2300:     DMGetLocalSection(dmAux, &sectionAux);
2301:     PetscDSGetTotalDimension(probAux, &totDimAux);
2302:     PetscDSGetComponentOffsets(probAux, &aOff);
2303:   }
2304:   /* Integrate over points */
2305:   {
2306:     PetscFEGeom    *fgeom, *chunkGeom = NULL;
2307:     PetscInt        maxDegree;
2308:     PetscQuadrature qGeom = NULL;
2309:     const PetscInt *points;
2310:     PetscInt        numFaces, face, Nq, field;
2311:     PetscInt        numChunks, chunkSize, chunk, Nr, offset;

2313:     ISGetLocalSize(pointIS, &numFaces);
2314:     ISGetIndices(pointIS, &points);
2315:     PetscCalloc2(numFaces * totDim, &u, locA ? numFaces * totDimAux : 0, &a);
2316:     DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree);
2317:     for (field = 0; field < Nf; ++field) {
2318:       PetscFE fe;

2320:       PetscDSGetDiscretization(prob, field, (PetscObject *)&fe);
2321:       if (maxDegree <= 1) DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom);
2322:       if (!qGeom) {
2323:         PetscFEGetFaceQuadrature(fe, &qGeom);
2324:         PetscObjectReference((PetscObject)qGeom);
2325:       }
2326:       PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL);
2327:       DMPlexGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom);
2328:       for (face = 0; face < numFaces; ++face) {
2329:         const PetscInt point = points[face], *support;
2330:         PetscScalar   *x     = NULL;
2331:         PetscInt       i;

2333:         DMPlexGetSupport(dm, point, &support);
2334:         DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x);
2335:         for (i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
2336:         DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x);
2337:         if (locA) {
2338:           PetscInt subp;
2339:           DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp);
2340:           DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x);
2341:           for (i = 0; i < totDimAux; ++i) a[f * totDimAux + i] = x[i];
2342:           DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x);
2343:         }
2344:       }
2345:       /* Get blocking */
2346:       {
2347:         PetscQuadrature q;
2348:         PetscInt        numBatches, batchSize, numBlocks, blockSize;
2349:         PetscInt        Nq, Nb;

2351:         PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches);
2352:         PetscFEGetQuadrature(fe, &q);
2353:         PetscQuadratureGetData(q, NULL, NULL, &Nq, NULL, NULL);
2354:         PetscFEGetDimension(fe, &Nb);
2355:         blockSize = Nb * Nq;
2356:         batchSize = numBlocks * blockSize;
2357:         chunkSize = numBatches * batchSize;
2358:         PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches);
2359:         numChunks = numFaces / chunkSize;
2360:         Nr        = numFaces % chunkSize;
2361:         offset    = numFaces - Nr;
2362:       }
2363:       /* Do integration for each field */
2364:       for (chunk = 0; chunk < numChunks; ++chunk) {
2365:         PetscFEGeomGetChunk(fgeom, chunk * chunkSize, (chunk + 1) * chunkSize, &chunkGeom);
2366:         PetscFEIntegrateBd(prob, field, func, chunkSize, chunkGeom, u, probAux, a, fintegral);
2367:         PetscFEGeomRestoreChunk(fgeom, 0, offset, &chunkGeom);
2368:       }
2369:       PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom);
2370:       PetscFEIntegrateBd(prob, field, func, Nr, chunkGeom, &u[offset * totDim], probAux, a ? &a[offset * totDimAux] : NULL, &fintegral[offset * Nf]);
2371:       PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom);
2372:       /* Cleanup data arrays */
2373:       DMPlexRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom);
2374:       PetscQuadratureDestroy(&qGeom);
2375:       PetscFree2(u, a);
2376:       ISRestoreIndices(pointIS, &points);
2377:     }
2378:   }
2379:   if (plex) DMDestroy(&plex);
2380:   if (plexA) DMDestroy(&plexA);
2381:   return 0;
2382: }

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

2387:   Input Parameters:
2388: + dm      - The mesh
2389: . X       - Global input vector
2390: . label   - The boundary DMLabel
2391: . numVals - The number of label values to use, or PETSC_DETERMINE for all values
2392: . vals    - The label values to use, or PETSC_NULL for all values
2393: . func    - The function to integrate along the boundary
2394: - user    - The user context

2396:   Output Parameter:
2397: . integral - Integral for each field

2399:   Level: developer

2401: .seealso: `DMPlexComputeIntegralFEM()`, `DMPlexComputeBdResidualFEM()`
2402: @*/
2403: 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)
2404: {
2405:   Vec          locX;
2406:   PetscSection section;
2407:   DMLabel      depthLabel;
2408:   IS           facetIS;
2409:   PetscInt     dim, Nf, f, v;

2416:   PetscLogEventBegin(DMPLEX_IntegralFEM, dm, 0, 0, 0);
2417:   DMPlexGetDepthLabel(dm, &depthLabel);
2418:   DMGetDimension(dm, &dim);
2419:   DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS);
2420:   DMGetLocalSection(dm, &section);
2421:   PetscSectionGetNumFields(section, &Nf);
2422:   /* Get local solution with boundary values */
2423:   DMGetLocalVector(dm, &locX);
2424:   DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL);
2425:   DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX);
2426:   DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX);
2427:   /* Loop over label values */
2428:   PetscArrayzero(integral, Nf);
2429:   for (v = 0; v < numVals; ++v) {
2430:     IS           pointIS;
2431:     PetscInt     numFaces, face;
2432:     PetscScalar *fintegral;

2434:     DMLabelGetStratumIS(label, vals[v], &pointIS);
2435:     if (!pointIS) continue; /* No points with that id on this process */
2436:     {
2437:       IS isectIS;

2439:       /* TODO: Special cases of ISIntersect where it is quick to check a priori if one is a superset of the other */
2440:       ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS);
2441:       ISDestroy(&pointIS);
2442:       pointIS = isectIS;
2443:     }
2444:     ISGetLocalSize(pointIS, &numFaces);
2445:     PetscCalloc1(numFaces * Nf, &fintegral);
2446:     DMPlexComputeBdIntegral_Internal(dm, locX, pointIS, func, fintegral, user);
2447:     /* Sum point contributions into integral */
2448:     for (f = 0; f < Nf; ++f)
2449:       for (face = 0; face < numFaces; ++face) integral[f] += fintegral[face * Nf + f];
2450:     PetscFree(fintegral);
2451:     ISDestroy(&pointIS);
2452:   }
2453:   DMRestoreLocalVector(dm, &locX);
2454:   ISDestroy(&facetIS);
2455:   PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0);
2456:   return 0;
2457: }

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

2462:   Input Parameters:
2463: + dmc  - The coarse mesh
2464: . dmf  - The fine mesh
2465: . isRefined - Flag indicating regular refinement, rather than the same topology
2466: - user - The user context

2468:   Output Parameter:
2469: . In  - The interpolation matrix

2471:   Level: developer

2473: .seealso: `DMPlexComputeInterpolatorGeneral()`, `DMPlexComputeJacobianFEM()`
2474: @*/
2475: PetscErrorCode DMPlexComputeInterpolatorNested(DM dmc, DM dmf, PetscBool isRefined, Mat In, void *user)
2476: {
2477:   DM_Plex     *mesh = (DM_Plex *)dmc->data;
2478:   const char  *name = "Interpolator";
2479:   PetscFE     *feRef;
2480:   PetscFV     *fvRef;
2481:   PetscSection fsection, fglobalSection;
2482:   PetscSection csection, cglobalSection;
2483:   PetscScalar *elemMat;
2484:   PetscInt     dim, Nf, f, fieldI, fieldJ, offsetI, offsetJ, cStart, cEnd, c;
2485:   PetscInt     cTotDim = 0, rTotDim = 0;

2487:   PetscLogEventBegin(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0);
2488:   DMGetDimension(dmf, &dim);
2489:   DMGetLocalSection(dmf, &fsection);
2490:   DMGetGlobalSection(dmf, &fglobalSection);
2491:   DMGetLocalSection(dmc, &csection);
2492:   DMGetGlobalSection(dmc, &cglobalSection);
2493:   PetscSectionGetNumFields(fsection, &Nf);
2494:   DMPlexGetSimplexOrBoxCells(dmc, 0, &cStart, &cEnd);
2495:   PetscCalloc2(Nf, &feRef, Nf, &fvRef);
2496:   for (f = 0; f < Nf; ++f) {
2497:     PetscObject  obj, objc;
2498:     PetscClassId id, idc;
2499:     PetscInt     rNb = 0, Nc = 0, cNb = 0;

2501:     DMGetField(dmf, f, NULL, &obj);
2502:     PetscObjectGetClassId(obj, &id);
2503:     if (id == PETSCFE_CLASSID) {
2504:       PetscFE fe = (PetscFE)obj;

2506:       if (isRefined) {
2507:         PetscFERefine(fe, &feRef[f]);
2508:       } else {
2509:         PetscObjectReference((PetscObject)fe);
2510:         feRef[f] = fe;
2511:       }
2512:       PetscFEGetDimension(feRef[f], &rNb);
2513:       PetscFEGetNumComponents(fe, &Nc);
2514:     } else if (id == PETSCFV_CLASSID) {
2515:       PetscFV        fv = (PetscFV)obj;
2516:       PetscDualSpace Q;

2518:       if (isRefined) {
2519:         PetscFVRefine(fv, &fvRef[f]);
2520:       } else {
2521:         PetscObjectReference((PetscObject)fv);
2522:         fvRef[f] = fv;
2523:       }
2524:       PetscFVGetDualSpace(fvRef[f], &Q);
2525:       PetscDualSpaceGetDimension(Q, &rNb);
2526:       PetscFVGetDualSpace(fv, &Q);
2527:       PetscFVGetNumComponents(fv, &Nc);
2528:     }
2529:     DMGetField(dmc, f, NULL, &objc);
2530:     PetscObjectGetClassId(objc, &idc);
2531:     if (idc == PETSCFE_CLASSID) {
2532:       PetscFE fe = (PetscFE)objc;

2534:       PetscFEGetDimension(fe, &cNb);
2535:     } else if (id == PETSCFV_CLASSID) {
2536:       PetscFV        fv = (PetscFV)obj;
2537:       PetscDualSpace Q;

2539:       PetscFVGetDualSpace(fv, &Q);
2540:       PetscDualSpaceGetDimension(Q, &cNb);
2541:     }
2542:     rTotDim += rNb;
2543:     cTotDim += cNb;
2544:   }
2545:   PetscMalloc1(rTotDim * cTotDim, &elemMat);
2546:   PetscArrayzero(elemMat, rTotDim * cTotDim);
2547:   for (fieldI = 0, offsetI = 0; fieldI < Nf; ++fieldI) {
2548:     PetscDualSpace   Qref;
2549:     PetscQuadrature  f;
2550:     const PetscReal *qpoints, *qweights;
2551:     PetscReal       *points;
2552:     PetscInt         npoints = 0, Nc, Np, fpdim, i, k, p, d;

2554:     /* Compose points from all dual basis functionals */
2555:     if (feRef[fieldI]) {
2556:       PetscFEGetDualSpace(feRef[fieldI], &Qref);
2557:       PetscFEGetNumComponents(feRef[fieldI], &Nc);
2558:     } else {
2559:       PetscFVGetDualSpace(fvRef[fieldI], &Qref);
2560:       PetscFVGetNumComponents(fvRef[fieldI], &Nc);
2561:     }
2562:     PetscDualSpaceGetDimension(Qref, &fpdim);
2563:     for (i = 0; i < fpdim; ++i) {
2564:       PetscDualSpaceGetFunctional(Qref, i, &f);
2565:       PetscQuadratureGetData(f, NULL, NULL, &Np, NULL, NULL);
2566:       npoints += Np;
2567:     }
2568:     PetscMalloc1(npoints * dim, &points);
2569:     for (i = 0, k = 0; i < fpdim; ++i) {
2570:       PetscDualSpaceGetFunctional(Qref, i, &f);
2571:       PetscQuadratureGetData(f, NULL, NULL, &Np, &qpoints, NULL);
2572:       for (p = 0; p < Np; ++p, ++k)
2573:         for (d = 0; d < dim; ++d) points[k * dim + d] = qpoints[p * dim + d];
2574:     }

2576:     for (fieldJ = 0, offsetJ = 0; fieldJ < Nf; ++fieldJ) {
2577:       PetscObject  obj;
2578:       PetscClassId id;
2579:       PetscInt     NcJ = 0, cpdim = 0, j, qNc;

2581:       DMGetField(dmc, fieldJ, NULL, &obj);
2582:       PetscObjectGetClassId(obj, &id);
2583:       if (id == PETSCFE_CLASSID) {
2584:         PetscFE         fe = (PetscFE)obj;
2585:         PetscTabulation T  = NULL;

2587:         /* Evaluate basis at points */
2588:         PetscFEGetNumComponents(fe, &NcJ);
2589:         PetscFEGetDimension(fe, &cpdim);
2590:         /* For now, fields only interpolate themselves */
2591:         if (fieldI == fieldJ) {
2593:           PetscFECreateTabulation(fe, 1, npoints, points, 0, &T);
2594:           for (i = 0, k = 0; i < fpdim; ++i) {
2595:             PetscDualSpaceGetFunctional(Qref, i, &f);
2596:             PetscQuadratureGetData(f, NULL, &qNc, &Np, NULL, &qweights);
2598:             for (p = 0; p < Np; ++p, ++k) {
2599:               for (j = 0; j < cpdim; ++j) {
2600:                 /*
2601:                    cTotDim:            Total columns in element interpolation matrix, sum of number of dual basis functionals in each field
2602:                    offsetI, offsetJ:   Offsets into the larger element interpolation matrix for different fields
2603:                    fpdim, i, cpdim, j: Dofs for fine and coarse grids, correspond to dual space basis functionals
2604:                    qNC, Nc, Ncj, c:    Number of components in this field
2605:                    Np, p:              Number of quad points in the fine grid functional i
2606:                    k:                  i*Np + p, overall point number for the interpolation
2607:                 */
2608:                 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];
2609:               }
2610:             }
2611:           }
2612:           PetscTabulationDestroy(&T);
2613:         }
2614:       } else if (id == PETSCFV_CLASSID) {
2615:         PetscFV fv = (PetscFV)obj;

2617:         /* Evaluate constant function at points */
2618:         PetscFVGetNumComponents(fv, &NcJ);
2619:         cpdim = 1;
2620:         /* For now, fields only interpolate themselves */
2621:         if (fieldI == fieldJ) {
2623:           for (i = 0, k = 0; i < fpdim; ++i) {
2624:             PetscDualSpaceGetFunctional(Qref, i, &f);
2625:             PetscQuadratureGetData(f, NULL, &qNc, &Np, NULL, &qweights);
2627:             for (p = 0; p < Np; ++p, ++k) {
2628:               for (j = 0; j < cpdim; ++j) {
2629:                 for (c = 0; c < Nc; ++c) elemMat[(offsetI + i) * cTotDim + offsetJ + j] += 1.0 * qweights[p * qNc + c];
2630:               }
2631:             }
2632:           }
2633:         }
2634:       }
2635:       offsetJ += cpdim;
2636:     }
2637:     offsetI += fpdim;
2638:     PetscFree(points);
2639:   }
2640:   if (mesh->printFEM > 1) DMPrintCellMatrix(0, name, rTotDim, cTotDim, elemMat);
2641:   /* Preallocate matrix */
2642:   {
2643:     Mat          preallocator;
2644:     PetscScalar *vals;
2645:     PetscInt    *cellCIndices, *cellFIndices;
2646:     PetscInt     locRows, locCols, cell;

2648:     MatGetLocalSize(In, &locRows, &locCols);
2649:     MatCreate(PetscObjectComm((PetscObject)In), &preallocator);
2650:     MatSetType(preallocator, MATPREALLOCATOR);
2651:     MatSetSizes(preallocator, locRows, locCols, PETSC_DETERMINE, PETSC_DETERMINE);
2652:     MatSetUp(preallocator);
2653:     PetscCalloc3(rTotDim * cTotDim, &vals, cTotDim, &cellCIndices, rTotDim, &cellFIndices);
2654:     for (cell = cStart; cell < cEnd; ++cell) {
2655:       if (isRefined) {
2656:         DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, cell, cellCIndices, cellFIndices);
2657:         MatSetValues(preallocator, rTotDim, cellFIndices, cTotDim, cellCIndices, vals, INSERT_VALUES);
2658:       } else {
2659:         DMPlexMatSetClosureGeneral(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, preallocator, cell, vals, INSERT_VALUES);
2660:       }
2661:     }
2662:     PetscFree3(vals, cellCIndices, cellFIndices);
2663:     MatAssemblyBegin(preallocator, MAT_FINAL_ASSEMBLY);
2664:     MatAssemblyEnd(preallocator, MAT_FINAL_ASSEMBLY);
2665:     MatPreallocatorPreallocate(preallocator, PETSC_TRUE, In);
2666:     MatDestroy(&preallocator);
2667:   }
2668:   /* Fill matrix */
2669:   MatZeroEntries(In);
2670:   for (c = cStart; c < cEnd; ++c) {
2671:     if (isRefined) {
2672:       DMPlexMatSetClosureRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, In, c, elemMat, INSERT_VALUES);
2673:     } else {
2674:       DMPlexMatSetClosureGeneral(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, In, c, elemMat, INSERT_VALUES);
2675:     }
2676:   }
2677:   for (f = 0; f < Nf; ++f) PetscFEDestroy(&feRef[f]);
2678:   PetscFree2(feRef, fvRef);
2679:   PetscFree(elemMat);
2680:   MatAssemblyBegin(In, MAT_FINAL_ASSEMBLY);
2681:   MatAssemblyEnd(In, MAT_FINAL_ASSEMBLY);
2682:   if (mesh->printFEM > 1) {
2683:     PetscPrintf(PetscObjectComm((PetscObject)In), "%s:\n", name);
2684:     MatChop(In, 1.0e-10);
2685:     MatView(In, NULL);
2686:   }
2687:   PetscLogEventEnd(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0);
2688:   return 0;
2689: }

2691: PetscErrorCode DMPlexComputeMassMatrixNested(DM dmc, DM dmf, Mat mass, void *user)
2692: {
2693:   SETERRQ(PetscObjectComm((PetscObject)dmc), PETSC_ERR_SUP, "Laziness");
2694: }

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

2699:   Input Parameters:
2700: + dmf  - The fine mesh
2701: . dmc  - The coarse mesh
2702: - user - The user context

2704:   Output Parameter:
2705: . In  - The interpolation matrix

2707:   Level: developer

2709: .seealso: `DMPlexComputeInterpolatorNested()`, `DMPlexComputeJacobianFEM()`
2710: @*/
2711: PetscErrorCode DMPlexComputeInterpolatorGeneral(DM dmc, DM dmf, Mat In, void *user)
2712: {
2713:   DM_Plex     *mesh = (DM_Plex *)dmf->data;
2714:   const char  *name = "Interpolator";
2715:   PetscDS      prob;
2716:   Mat          interp;
2717:   PetscSection fsection, globalFSection;
2718:   PetscSection csection, globalCSection;
2719:   PetscInt     locRows, locCols;
2720:   PetscReal   *x, *v0, *J, *invJ, detJ;
2721:   PetscReal   *v0c, *Jc, *invJc, detJc;
2722:   PetscScalar *elemMat;
2723:   PetscInt     dim, Nf, field, totDim, cStart, cEnd, cell, ccell, s;

2725:   PetscLogEventBegin(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0);
2726:   DMGetCoordinateDim(dmc, &dim);
2727:   DMGetDS(dmc, &prob);
2728:   PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL);
2729:   PetscDSGetNumFields(prob, &Nf);
2730:   PetscMalloc3(dim, &v0, dim * dim, &J, dim * dim, &invJ);
2731:   PetscMalloc3(dim, &v0c, dim * dim, &Jc, dim * dim, &invJc);
2732:   DMGetLocalSection(dmf, &fsection);
2733:   DMGetGlobalSection(dmf, &globalFSection);
2734:   DMGetLocalSection(dmc, &csection);
2735:   DMGetGlobalSection(dmc, &globalCSection);
2736:   DMPlexGetSimplexOrBoxCells(dmf, 0, &cStart, &cEnd);
2737:   PetscDSGetTotalDimension(prob, &totDim);
2738:   PetscMalloc1(totDim, &elemMat);

2740:   MatGetLocalSize(In, &locRows, &locCols);
2741:   MatCreate(PetscObjectComm((PetscObject)In), &interp);
2742:   MatSetType(interp, MATPREALLOCATOR);
2743:   MatSetSizes(interp, locRows, locCols, PETSC_DETERMINE, PETSC_DETERMINE);
2744:   MatSetUp(interp);
2745:   for (s = 0; s < 2; ++s) {
2746:     for (field = 0; field < Nf; ++field) {
2747:       PetscObject      obj;
2748:       PetscClassId     id;
2749:       PetscDualSpace   Q = NULL;
2750:       PetscTabulation  T = NULL;
2751:       PetscQuadrature  f;
2752:       const PetscReal *qpoints, *qweights;
2753:       PetscInt         Nc, qNc, Np, fpdim, off, i, d;

2755:       PetscDSGetFieldOffset(prob, field, &off);
2756:       PetscDSGetDiscretization(prob, field, &obj);
2757:       PetscObjectGetClassId(obj, &id);
2758:       if (id == PETSCFE_CLASSID) {
2759:         PetscFE fe = (PetscFE)obj;

2761:         PetscFEGetDualSpace(fe, &Q);
2762:         PetscFEGetNumComponents(fe, &Nc);
2763:         if (s) PetscFECreateTabulation(fe, 1, 1, x, 0, &T);
2764:       } else if (id == PETSCFV_CLASSID) {
2765:         PetscFV fv = (PetscFV)obj;

2767:         PetscFVGetDualSpace(fv, &Q);
2768:         Nc = 1;
2769:       } else SETERRQ(PetscObjectComm((PetscObject)dmc), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
2770:       PetscDualSpaceGetDimension(Q, &fpdim);
2771:       /* For each fine grid cell */
2772:       for (cell = cStart; cell < cEnd; ++cell) {
2773:         PetscInt *findices, *cindices;
2774:         PetscInt  numFIndices, numCIndices;

2776:         DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL);
2777:         DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ);
2779:         for (i = 0; i < fpdim; ++i) {
2780:           Vec                pointVec;
2781:           PetscScalar       *pV;
2782:           PetscSF            coarseCellSF = NULL;
2783:           const PetscSFNode *coarseCells;
2784:           PetscInt           numCoarseCells, cpdim, row = findices[i + off], q, c, j;

2786:           /* Get points from the dual basis functional quadrature */
2787:           PetscDualSpaceGetFunctional(Q, i, &f);
2788:           PetscQuadratureGetData(f, NULL, &qNc, &Np, &qpoints, &qweights);
2790:           VecCreateSeq(PETSC_COMM_SELF, Np * dim, &pointVec);
2791:           VecSetBlockSize(pointVec, dim);
2792:           VecGetArray(pointVec, &pV);
2793:           for (q = 0; q < Np; ++q) {
2794:             const PetscReal xi0[3] = {-1., -1., -1.};

2796:             /* Transform point to real space */
2797:             CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x);
2798:             for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d];
2799:           }
2800:           VecRestoreArray(pointVec, &pV);
2801:           /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
2802:           /* OPT: Read this out from preallocation information */
2803:           DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF);
2804:           /* Update preallocation info */
2805:           PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells);
2807:           VecGetArray(pointVec, &pV);
2808:           for (ccell = 0; ccell < numCoarseCells; ++ccell) {
2809:             PetscReal       pVReal[3];
2810:             const PetscReal xi0[3] = {-1., -1., -1.};

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

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

2822:               if (id == PETSCFE_CLASSID) {
2823:                 /* Evaluate coarse basis on contained point */
2824:                 PetscFEComputeTabulation((PetscFE)obj, 1, x, 0, T);
2825:                 PetscArrayzero(elemMat, cpdim);
2826:                 /* Get elemMat entries by multiplying by weight */
2827:                 for (j = 0; j < cpdim; ++j) {
2828:                   for (c = 0; c < Nc; ++c) elemMat[j] += T->T[0][j * Nc + c] * qweights[ccell * qNc + c];
2829:                 }
2830:               } else {
2831:                 for (j = 0; j < cpdim; ++j) {
2832:                   for (c = 0; c < Nc; ++c) elemMat[j] += 1.0 * qweights[ccell * qNc + c];
2833:                 }
2834:               }
2835:               if (mesh->printFEM > 1) DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat);
2836:             }
2837:             /* Update interpolator */
2839:             MatSetValues(interp, 1, &row, cpdim, &cindices[off], elemMat, INSERT_VALUES);
2840:             DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL);
2841:           }
2842:           VecRestoreArray(pointVec, &pV);
2843:           PetscSFDestroy(&coarseCellSF);
2844:           VecDestroy(&pointVec);
2845:         }
2846:         DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL);
2847:       }
2848:       if (s && id == PETSCFE_CLASSID) PetscTabulationDestroy(&T);
2849:     }
2850:     if (!s) {
2851:       MatAssemblyBegin(interp, MAT_FINAL_ASSEMBLY);
2852:       MatAssemblyEnd(interp, MAT_FINAL_ASSEMBLY);
2853:       MatPreallocatorPreallocate(interp, PETSC_TRUE, In);
2854:       MatDestroy(&interp);
2855:       interp = In;
2856:     }
2857:   }
2858:   PetscFree3(v0, J, invJ);
2859:   PetscFree3(v0c, Jc, invJc);
2860:   PetscFree(elemMat);
2861:   MatAssemblyBegin(In, MAT_FINAL_ASSEMBLY);
2862:   MatAssemblyEnd(In, MAT_FINAL_ASSEMBLY);
2863:   PetscLogEventEnd(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0);
2864:   return 0;
2865: }

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

2870:   Input Parameters:
2871: + dmf  - The fine mesh
2872: . dmc  - The coarse mesh
2873: - user - The user context

2875:   Output Parameter:
2876: . mass  - The mass matrix

2878:   Level: developer

2880: .seealso: `DMPlexComputeMassMatrixNested()`, `DMPlexComputeInterpolatorNested()`, `DMPlexComputeInterpolatorGeneral()`, `DMPlexComputeJacobianFEM()`
2881: @*/
2882: PetscErrorCode DMPlexComputeMassMatrixGeneral(DM dmc, DM dmf, Mat mass, void *user)
2883: {
2884:   DM_Plex     *mesh = (DM_Plex *)dmf->data;
2885:   const char  *name = "Mass Matrix";
2886:   PetscDS      prob;
2887:   PetscSection fsection, csection, globalFSection, globalCSection;
2888:   PetscHSetIJ  ht;
2889:   PetscLayout  rLayout;
2890:   PetscInt    *dnz, *onz;
2891:   PetscInt     locRows, rStart, rEnd;
2892:   PetscReal   *x, *v0, *J, *invJ, detJ;
2893:   PetscReal   *v0c, *Jc, *invJc, detJc;
2894:   PetscScalar *elemMat;
2895:   PetscInt     dim, Nf, field, totDim, cStart, cEnd, cell, ccell;

2897:   DMGetCoordinateDim(dmc, &dim);
2898:   DMGetDS(dmc, &prob);
2899:   PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL);
2900:   PetscDSGetNumFields(prob, &Nf);
2901:   PetscMalloc3(dim, &v0, dim * dim, &J, dim * dim, &invJ);
2902:   PetscMalloc3(dim, &v0c, dim * dim, &Jc, dim * dim, &invJc);
2903:   DMGetLocalSection(dmf, &fsection);
2904:   DMGetGlobalSection(dmf, &globalFSection);
2905:   DMGetLocalSection(dmc, &csection);
2906:   DMGetGlobalSection(dmc, &globalCSection);
2907:   DMPlexGetHeightStratum(dmf, 0, &cStart, &cEnd);
2908:   PetscDSGetTotalDimension(prob, &totDim);
2909:   PetscMalloc1(totDim, &elemMat);

2911:   MatGetLocalSize(mass, &locRows, NULL);
2912:   PetscLayoutCreate(PetscObjectComm((PetscObject)mass), &rLayout);
2913:   PetscLayoutSetLocalSize(rLayout, locRows);
2914:   PetscLayoutSetBlockSize(rLayout, 1);
2915:   PetscLayoutSetUp(rLayout);
2916:   PetscLayoutGetRange(rLayout, &rStart, &rEnd);
2917:   PetscLayoutDestroy(&rLayout);
2918:   PetscCalloc2(locRows, &dnz, locRows, &onz);
2919:   PetscHSetIJCreate(&ht);
2920:   for (field = 0; field < Nf; ++field) {
2921:     PetscObject      obj;
2922:     PetscClassId     id;
2923:     PetscQuadrature  quad;
2924:     const PetscReal *qpoints;
2925:     PetscInt         Nq, Nc, i, d;

2927:     PetscDSGetDiscretization(prob, field, &obj);
2928:     PetscObjectGetClassId(obj, &id);
2929:     if (id == PETSCFE_CLASSID) PetscFEGetQuadrature((PetscFE)obj, &quad);
2930:     else PetscFVGetQuadrature((PetscFV)obj, &quad);
2931:     PetscQuadratureGetData(quad, NULL, &Nc, &Nq, &qpoints, NULL);
2932:     /* For each fine grid cell */
2933:     for (cell = cStart; cell < cEnd; ++cell) {
2934:       Vec                pointVec;
2935:       PetscScalar       *pV;
2936:       PetscSF            coarseCellSF = NULL;
2937:       const PetscSFNode *coarseCells;
2938:       PetscInt           numCoarseCells, q, c;
2939:       PetscInt          *findices, *cindices;
2940:       PetscInt           numFIndices, numCIndices;

2942:       DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL);
2943:       DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ);
2944:       /* Get points from the quadrature */
2945:       VecCreateSeq(PETSC_COMM_SELF, Nq * dim, &pointVec);
2946:       VecSetBlockSize(pointVec, dim);
2947:       VecGetArray(pointVec, &pV);
2948:       for (q = 0; q < Nq; ++q) {
2949:         const PetscReal xi0[3] = {-1., -1., -1.};

2951:         /* Transform point to real space */
2952:         CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x);
2953:         for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d];
2954:       }
2955:       VecRestoreArray(pointVec, &pV);
2956:       /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
2957:       DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF);
2958:       PetscSFViewFromOptions(coarseCellSF, NULL, "-interp_sf_view");
2959:       /* Update preallocation info */
2960:       PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells);
2962:       {
2963:         PetscHashIJKey key;
2964:         PetscBool      missing;

2966:         for (i = 0; i < numFIndices; ++i) {
2967:           key.i = findices[i];
2968:           if (key.i >= 0) {
2969:             /* Get indices for coarse elements */
2970:             for (ccell = 0; ccell < numCoarseCells; ++ccell) {
2971:               DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL);
2972:               for (c = 0; c < numCIndices; ++c) {
2973:                 key.j = cindices[c];
2974:                 if (key.j < 0) continue;
2975:                 PetscHSetIJQueryAdd(ht, key, &missing);
2976:                 if (missing) {
2977:                   if ((key.j >= rStart) && (key.j < rEnd)) ++dnz[key.i - rStart];
2978:                   else ++onz[key.i - rStart];
2979:                 }
2980:               }
2981:               DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL);
2982:             }
2983:           }
2984:         }
2985:       }
2986:       PetscSFDestroy(&coarseCellSF);
2987:       VecDestroy(&pointVec);
2988:       DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL);
2989:     }
2990:   }
2991:   PetscHSetIJDestroy(&ht);
2992:   MatXAIJSetPreallocation(mass, 1, dnz, onz, NULL, NULL);
2993:   MatSetOption(mass, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_TRUE);
2994:   PetscFree2(dnz, onz);
2995:   for (field = 0; field < Nf; ++field) {
2996:     PetscObject      obj;
2997:     PetscClassId     id;
2998:     PetscTabulation  T, Tfine;
2999:     PetscQuadrature  quad;
3000:     const PetscReal *qpoints, *qweights;
3001:     PetscInt         Nq, Nc, i, d;

3003:     PetscDSGetDiscretization(prob, field, &obj);
3004:     PetscObjectGetClassId(obj, &id);
3005:     if (id == PETSCFE_CLASSID) {
3006:       PetscFEGetQuadrature((PetscFE)obj, &quad);
3007:       PetscFEGetCellTabulation((PetscFE)obj, 1, &Tfine);
3008:       PetscFECreateTabulation((PetscFE)obj, 1, 1, x, 0, &T);
3009:     } else {
3010:       PetscFVGetQuadrature((PetscFV)obj, &quad);
3011:     }
3012:     PetscQuadratureGetData(quad, NULL, &Nc, &Nq, &qpoints, &qweights);
3013:     /* For each fine grid cell */
3014:     for (cell = cStart; cell < cEnd; ++cell) {
3015:       Vec                pointVec;
3016:       PetscScalar       *pV;
3017:       PetscSF            coarseCellSF = NULL;
3018:       const PetscSFNode *coarseCells;
3019:       PetscInt           numCoarseCells, cpdim, q, c, j;
3020:       PetscInt          *findices, *cindices;
3021:       PetscInt           numFIndices, numCIndices;

3023:       DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL);
3024:       DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ);
3025:       /* Get points from the quadrature */
3026:       VecCreateSeq(PETSC_COMM_SELF, Nq * dim, &pointVec);
3027:       VecSetBlockSize(pointVec, dim);
3028:       VecGetArray(pointVec, &pV);
3029:       for (q = 0; q < Nq; ++q) {
3030:         const PetscReal xi0[3] = {-1., -1., -1.};

3032:         /* Transform point to real space */
3033:         CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x);
3034:         for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d];
3035:       }
3036:       VecRestoreArray(pointVec, &pV);
3037:       /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
3038:       DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF);
3039:       /* Update matrix */
3040:       PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells);
3042:       VecGetArray(pointVec, &pV);
3043:       for (ccell = 0; ccell < numCoarseCells; ++ccell) {
3044:         PetscReal       pVReal[3];
3045:         const PetscReal xi0[3] = {-1., -1., -1.};

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

3053:         if (id == PETSCFE_CLASSID) {
3054:           PetscFE fe = (PetscFE)obj;

3056:           /* Evaluate coarse basis on contained point */
3057:           PetscFEGetDimension(fe, &cpdim);
3058:           PetscFEComputeTabulation(fe, 1, x, 0, T);
3059:           /* Get elemMat entries by multiplying by weight */
3060:           for (i = 0; i < numFIndices; ++i) {
3061:             PetscArrayzero(elemMat, cpdim);
3062:             for (j = 0; j < cpdim; ++j) {
3063:               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;
3064:             }
3065:             /* Update interpolator */
3066:             if (mesh->printFEM > 1) DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat);
3068:             MatSetValues(mass, 1, &findices[i], numCIndices, cindices, elemMat, ADD_VALUES);
3069:           }
3070:         } else {
3071:           cpdim = 1;
3072:           for (i = 0; i < numFIndices; ++i) {
3073:             PetscArrayzero(elemMat, cpdim);
3074:             for (j = 0; j < cpdim; ++j) {
3075:               for (c = 0; c < Nc; ++c) elemMat[j] += 1.0 * 1.0 * qweights[ccell * Nc + c] * detJ;
3076:             }
3077:             /* Update interpolator */
3078:             if (mesh->printFEM > 1) DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat);
3079:             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);
3081:             MatSetValues(mass, 1, &findices[i], numCIndices, cindices, elemMat, ADD_VALUES);
3082:           }
3083:         }
3084:         DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL);
3085:       }
3086:       VecRestoreArray(pointVec, &pV);
3087:       PetscSFDestroy(&coarseCellSF);
3088:       VecDestroy(&pointVec);
3089:       DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL);
3090:     }
3091:     if (id == PETSCFE_CLASSID) PetscTabulationDestroy(&T);
3092:   }
3093:   PetscFree3(v0, J, invJ);
3094:   PetscFree3(v0c, Jc, invJc);
3095:   PetscFree(elemMat);
3096:   MatAssemblyBegin(mass, MAT_FINAL_ASSEMBLY);
3097:   MatAssemblyEnd(mass, MAT_FINAL_ASSEMBLY);
3098:   return 0;
3099: }

3101: /*@
3102:   DMPlexComputeInjectorFEM - Compute a mapping from coarse unknowns to fine unknowns

3104:   Input Parameters:
3105: + dmc  - The coarse mesh
3106: - dmf  - The fine mesh
3107: - user - The user context

3109:   Output Parameter:
3110: . sc   - The mapping

3112:   Level: developer

3114: .seealso: `DMPlexComputeInterpolatorNested()`, `DMPlexComputeJacobianFEM()`
3115: @*/
3116: PetscErrorCode DMPlexComputeInjectorFEM(DM dmc, DM dmf, VecScatter *sc, void *user)
3117: {
3118:   PetscDS      prob;
3119:   PetscFE     *feRef;
3120:   PetscFV     *fvRef;
3121:   Vec          fv, cv;
3122:   IS           fis, cis;
3123:   PetscSection fsection, fglobalSection, csection, cglobalSection;
3124:   PetscInt    *cmap, *cellCIndices, *cellFIndices, *cindices, *findices;
3125:   PetscInt     cTotDim, fTotDim = 0, Nf, f, field, cStart, cEnd, c, dim, d, startC, endC, offsetC, offsetF, m;
3126:   PetscBool   *needAvg;

3128:   PetscLogEventBegin(DMPLEX_InjectorFEM, dmc, dmf, 0, 0);
3129:   DMGetDimension(dmf, &dim);
3130:   DMGetLocalSection(dmf, &fsection);
3131:   DMGetGlobalSection(dmf, &fglobalSection);
3132:   DMGetLocalSection(dmc, &csection);
3133:   DMGetGlobalSection(dmc, &cglobalSection);
3134:   PetscSectionGetNumFields(fsection, &Nf);
3135:   DMPlexGetSimplexOrBoxCells(dmc, 0, &cStart, &cEnd);
3136:   DMGetDS(dmc, &prob);
3137:   PetscCalloc3(Nf, &feRef, Nf, &fvRef, Nf, &needAvg);
3138:   for (f = 0; f < Nf; ++f) {
3139:     PetscObject  obj;
3140:     PetscClassId id;
3141:     PetscInt     fNb = 0, Nc = 0;

3143:     PetscDSGetDiscretization(prob, f, &obj);
3144:     PetscObjectGetClassId(obj, &id);
3145:     if (id == PETSCFE_CLASSID) {
3146:       PetscFE    fe = (PetscFE)obj;
3147:       PetscSpace sp;
3148:       PetscInt   maxDegree;

3150:       PetscFERefine(fe, &feRef[f]);
3151:       PetscFEGetDimension(feRef[f], &fNb);
3152:       PetscFEGetNumComponents(fe, &Nc);
3153:       PetscFEGetBasisSpace(fe, &sp);
3154:       PetscSpaceGetDegree(sp, NULL, &maxDegree);
3155:       if (!maxDegree) needAvg[f] = PETSC_TRUE;
3156:     } else if (id == PETSCFV_CLASSID) {
3157:       PetscFV        fv = (PetscFV)obj;
3158:       PetscDualSpace Q;

3160:       PetscFVRefine(fv, &fvRef[f]);
3161:       PetscFVGetDualSpace(fvRef[f], &Q);
3162:       PetscDualSpaceGetDimension(Q, &fNb);
3163:       PetscFVGetNumComponents(fv, &Nc);
3164:       needAvg[f] = PETSC_TRUE;
3165:     }
3166:     fTotDim += fNb;
3167:   }
3168:   PetscDSGetTotalDimension(prob, &cTotDim);
3169:   PetscMalloc1(cTotDim, &cmap);
3170:   for (field = 0, offsetC = 0, offsetF = 0; field < Nf; ++field) {
3171:     PetscFE        feC;
3172:     PetscFV        fvC;
3173:     PetscDualSpace QF, QC;
3174:     PetscInt       order = -1, NcF, NcC, fpdim, cpdim;

3176:     if (feRef[field]) {
3177:       PetscDSGetDiscretization(prob, field, (PetscObject *)&feC);
3178:       PetscFEGetNumComponents(feC, &NcC);
3179:       PetscFEGetNumComponents(feRef[field], &NcF);
3180:       PetscFEGetDualSpace(feRef[field], &QF);
3181:       PetscDualSpaceGetOrder(QF, &order);
3182:       PetscDualSpaceGetDimension(QF, &fpdim);
3183:       PetscFEGetDualSpace(feC, &QC);
3184:       PetscDualSpaceGetDimension(QC, &cpdim);
3185:     } else {
3186:       PetscDSGetDiscretization(prob, field, (PetscObject *)&fvC);
3187:       PetscFVGetNumComponents(fvC, &NcC);
3188:       PetscFVGetNumComponents(fvRef[field], &NcF);
3189:       PetscFVGetDualSpace(fvRef[field], &QF);
3190:       PetscDualSpaceGetDimension(QF, &fpdim);
3191:       PetscFVGetDualSpace(fvC, &QC);
3192:       PetscDualSpaceGetDimension(QC, &cpdim);
3193:     }
3195:     for (c = 0; c < cpdim; ++c) {
3196:       PetscQuadrature  cfunc;
3197:       const PetscReal *cqpoints, *cqweights;
3198:       PetscInt         NqcC, NpC;
3199:       PetscBool        found = PETSC_FALSE;

3201:       PetscDualSpaceGetFunctional(QC, c, &cfunc);
3202:       PetscQuadratureGetData(cfunc, NULL, &NqcC, &NpC, &cqpoints, &cqweights);
3205:       for (f = 0; f < fpdim; ++f) {
3206:         PetscQuadrature  ffunc;
3207:         const PetscReal *fqpoints, *fqweights;
3208:         PetscReal        sum = 0.0;
3209:         PetscInt         NqcF, NpF;

3211:         PetscDualSpaceGetFunctional(QF, f, &ffunc);
3212:         PetscQuadratureGetData(ffunc, NULL, &NqcF, &NpF, &fqpoints, &fqweights);
3214:         if (NpC != NpF) continue;
3215:         for (d = 0; d < dim; ++d) sum += PetscAbsReal(cqpoints[d] - fqpoints[d]);
3216:         if (sum > 1.0e-9) continue;
3217:         for (d = 0; d < NcC; ++d) sum += PetscAbsReal(cqweights[d] * fqweights[d]);
3218:         if (sum < 1.0e-9) continue;
3219:         cmap[offsetC + c] = offsetF + f;
3220:         found             = PETSC_TRUE;
3221:         break;
3222:       }
3223:       if (!found) {
3224:         /* TODO We really want the average here, but some asshole put VecScatter in the interface */
3225:         if (fvRef[field] || (feRef[field] && order == 0)) {
3226:           cmap[offsetC + c] = offsetF + 0;
3227:         } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Could not locate matching functional for injection");
3228:       }
3229:     }
3230:     offsetC += cpdim;
3231:     offsetF += fpdim;
3232:   }
3233:   for (f = 0; f < Nf; ++f) {
3234:     PetscFEDestroy(&feRef[f]);
3235:     PetscFVDestroy(&fvRef[f]);
3236:   }
3237:   PetscFree3(feRef, fvRef, needAvg);

3239:   DMGetGlobalVector(dmf, &fv);
3240:   DMGetGlobalVector(dmc, &cv);
3241:   VecGetOwnershipRange(cv, &startC, &endC);
3242:   PetscSectionGetConstrainedStorageSize(cglobalSection, &m);
3243:   PetscMalloc2(cTotDim, &cellCIndices, fTotDim, &cellFIndices);
3244:   PetscMalloc1(m, &cindices);
3245:   PetscMalloc1(m, &findices);
3246:   for (d = 0; d < m; ++d) cindices[d] = findices[d] = -1;
3247:   for (c = cStart; c < cEnd; ++c) {
3248:     DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, c, cellCIndices, cellFIndices);
3249:     for (d = 0; d < cTotDim; ++d) {
3250:       if ((cellCIndices[d] < startC) || (cellCIndices[d] >= endC)) continue;
3252:       cindices[cellCIndices[d] - startC] = cellCIndices[d];
3253:       findices[cellCIndices[d] - startC] = cellFIndices[cmap[d]];
3254:     }
3255:   }
3256:   PetscFree(cmap);
3257:   PetscFree2(cellCIndices, cellFIndices);

3259:   ISCreateGeneral(PETSC_COMM_SELF, m, cindices, PETSC_OWN_POINTER, &cis);
3260:   ISCreateGeneral(PETSC_COMM_SELF, m, findices, PETSC_OWN_POINTER, &fis);
3261:   VecScatterCreate(cv, cis, fv, fis, sc);
3262:   ISDestroy(&cis);
3263:   ISDestroy(&fis);
3264:   DMRestoreGlobalVector(dmf, &fv);
3265:   DMRestoreGlobalVector(dmc, &cv);
3266:   PetscLogEventEnd(DMPLEX_InjectorFEM, dmc, dmf, 0, 0);
3267:   return 0;
3268: }

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

3273:   Input Parameters:
3274: + dm     - The DM
3275: . cellIS - The cells to include
3276: . locX   - A local vector with the solution fields
3277: . locX_t - A local vector with solution field time derivatives, or NULL
3278: - locA   - A local vector with auxiliary fields, or NULL

3280:   Output Parameters:
3281: + u   - The field coefficients
3282: . u_t - The fields derivative coefficients
3283: - a   - The auxiliary field coefficients

3285:   Level: developer

3287: .seealso: `DMPlexGetFaceFields()`
3288: @*/
3289: PetscErrorCode DMPlexGetCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
3290: {
3291:   DM              plex, plexA = NULL;
3292:   DMEnclosureType encAux;
3293:   PetscSection    section, sectionAux;
3294:   PetscDS         prob;
3295:   const PetscInt *cells;
3296:   PetscInt        cStart, cEnd, numCells, totDim, totDimAux, c;

3305:   DMPlexConvertPlex(dm, &plex, PETSC_FALSE);
3306:   ISGetPointRange(cellIS, &cStart, &cEnd, &cells);
3307:   DMGetLocalSection(dm, &section);
3308:   DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob);
3309:   PetscDSGetTotalDimension(prob, &totDim);
3310:   if (locA) {
3311:     DM      dmAux;
3312:     PetscDS probAux;

3314:     VecGetDM(locA, &dmAux);
3315:     DMGetEnclosureRelation(dmAux, dm, &encAux);
3316:     DMPlexConvertPlex(dmAux, &plexA, PETSC_FALSE);
3317:     DMGetLocalSection(dmAux, &sectionAux);
3318:     DMGetDS(dmAux, &probAux);
3319:     PetscDSGetTotalDimension(probAux, &totDimAux);
3320:   }
3321:   numCells = cEnd - cStart;
3322:   DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u);
3323:   if (locX_t) DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u_t);
3324:   else *u_t = NULL;
3325:   if (locA) DMGetWorkArray(dm, numCells * totDimAux, MPIU_SCALAR, a);
3326:   else *a = NULL;
3327:   for (c = cStart; c < cEnd; ++c) {
3328:     const PetscInt cell = cells ? cells[c] : c;
3329:     const PetscInt cind = c - cStart;
3330:     PetscScalar   *x = NULL, *x_t = NULL, *ul = *u, *ul_t = *u_t, *al = *a;
3331:     PetscInt       i;

3333:     DMPlexVecGetClosure(plex, section, locX, cell, NULL, &x);
3334:     for (i = 0; i < totDim; ++i) ul[cind * totDim + i] = x[i];
3335:     DMPlexVecRestoreClosure(plex, section, locX, cell, NULL, &x);
3336:     if (locX_t) {
3337:       DMPlexVecGetClosure(plex, section, locX_t, cell, NULL, &x_t);
3338:       for (i = 0; i < totDim; ++i) ul_t[cind * totDim + i] = x_t[i];
3339:       DMPlexVecRestoreClosure(plex, section, locX_t, cell, NULL, &x_t);
3340:     }
3341:     if (locA) {
3342:       PetscInt subcell;
3343:       DMGetEnclosurePoint(plexA, dm, encAux, cell, &subcell);
3344:       DMPlexVecGetClosure(plexA, sectionAux, locA, subcell, NULL, &x);
3345:       for (i = 0; i < totDimAux; ++i) al[cind * totDimAux + i] = x[i];
3346:       DMPlexVecRestoreClosure(plexA, sectionAux, locA, subcell, NULL, &x);
3347:     }
3348:   }
3349:   DMDestroy(&plex);
3350:   if (locA) DMDestroy(&plexA);
3351:   ISRestorePointRange(cellIS, &cStart, &cEnd, &cells);
3352:   return 0;
3353: }

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

3358:   Input Parameters:
3359: + dm     - The DM
3360: . cellIS - The cells to include
3361: . locX   - A local vector with the solution fields
3362: . locX_t - A local vector with solution field time derivatives, or NULL
3363: - locA   - A local vector with auxiliary fields, or NULL

3365:   Output Parameters:
3366: + u   - The field coefficients
3367: . u_t - The fields derivative coefficients
3368: - a   - The auxiliary field coefficients

3370:   Level: developer

3372: .seealso: `DMPlexGetFaceFields()`
3373: @*/
3374: PetscErrorCode DMPlexRestoreCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
3375: {
3376:   DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u);
3377:   if (locX_t) DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u_t);
3378:   if (locA) DMRestoreWorkArray(dm, 0, MPIU_SCALAR, a);
3379:   return 0;
3380: }

3382: /*
3383:   Get the auxiliary field vectors for the negative side (s = 0) and positive side (s = 1) of the interfaace
3384: */
3385: static PetscErrorCode DMPlexGetHybridAuxFields(DM dm, DM dmAux[], PetscDS dsAux[], IS cellIS, Vec locA[], PetscScalar *a[])
3386: {
3387:   DM              plexA[2];
3388:   DMEnclosureType encAux[2];
3389:   PetscSection    sectionAux[2];
3390:   const PetscInt *cells;
3391:   PetscInt        cStart, cEnd, numCells, c, s, totDimAux[2];

3394:   if (!locA[0] || !locA[1]) return 0;
3398:   ISGetPointRange(cellIS, &cStart, &cEnd, &cells);
3399:   numCells = cEnd - cStart;
3400:   for (s = 0; s < 2; ++s) {
3404:     DMPlexConvertPlex(dmAux[s], &plexA[s], PETSC_FALSE);
3405:     DMGetEnclosureRelation(dmAux[s], dm, &encAux[s]);
3406:     DMGetLocalSection(dmAux[s], &sectionAux[s]);
3407:     PetscDSGetTotalDimension(dsAux[s], &totDimAux[s]);
3408:     DMGetWorkArray(dmAux[s], numCells * totDimAux[s], MPIU_SCALAR, &a[s]);
3409:   }
3410:   for (c = cStart; c < cEnd; ++c) {
3411:     const PetscInt  cell = cells ? cells[c] : c;
3412:     const PetscInt  cind = c - cStart;
3413:     const PetscInt *cone, *ornt;

3415:     DMPlexGetCone(dm, cell, &cone);
3416:     DMPlexGetConeOrientation(dm, cell, &ornt);
3417:     for (s = 0; s < 2; ++s) {
3418:       const PetscInt *support;
3419:       PetscScalar    *x = NULL, *al = a[s];
3420:       const PetscInt  tdA = totDimAux[s];
3421:       PetscInt        ssize, scell;
3422:       PetscInt        subface, Na, i;

3424:       DMPlexGetSupport(dm, cone[s], &support);
3425:       DMPlexGetSupportSize(dm, cone[s], &ssize);
3427:       if (support[0] == cell) scell = support[1];
3428:       else if (support[1] == cell) scell = support[0];
3429:       else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[s], cell);

3431:       DMGetEnclosurePoint(plexA[s], dm, encAux[s], scell, &subface);
3432:       DMPlexVecGetClosure(plexA[s], sectionAux[s], locA[s], subface, &Na, &x);
3433:       for (i = 0; i < Na; ++i) al[cind * tdA + i] = x[i];
3434:       DMPlexVecRestoreClosure(plexA[s], sectionAux[s], locA[s], subface, &Na, &x);
3435:     }
3436:   }
3437:   for (s = 0; s < 2; ++s) DMDestroy(&plexA[s]);
3438:   ISRestorePointRange(cellIS, &cStart, &cEnd, &cells);
3439:   return 0;
3440: }

3442: static PetscErrorCode DMPlexRestoreHybridAuxFields(DM dmAux[], PetscDS dsAux[], IS cellIS, Vec locA[], PetscScalar *a[])
3443: {
3444:   if (!locA[0] || !locA[1]) return 0;
3445:   DMRestoreWorkArray(dmAux[0], 0, MPIU_SCALAR, &a[0]);
3446:   DMRestoreWorkArray(dmAux[1], 0, MPIU_SCALAR, &a[1]);
3447:   return 0;
3448: }

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

3453:   Input Parameters:
3454: + dm     - The DM
3455: . fStart - The first face to include
3456: . fEnd   - The first face to exclude
3457: . locX   - A local vector with the solution fields
3458: . locX_t - A local vector with solution field time derivatives, or NULL
3459: . faceGeometry - A local vector with face geometry
3460: . cellGeometry - A local vector with cell geometry
3461: - locaGrad - A local vector with field gradients, or NULL

3463:   Output Parameters:
3464: + Nface - The number of faces with field values
3465: . uL - The field values at the left side of the face
3466: - uR - The field values at the right side of the face

3468:   Level: developer

3470: .seealso: `DMPlexGetCellFields()`
3471: @*/
3472: 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)
3473: {
3474:   DM                 dmFace, dmCell, dmGrad = NULL;
3475:   PetscSection       section;
3476:   PetscDS            prob;
3477:   DMLabel            ghostLabel;
3478:   const PetscScalar *facegeom, *cellgeom, *x, *lgrad;
3479:   PetscBool         *isFE;
3480:   PetscInt           dim, Nf, f, Nc, numFaces = fEnd - fStart, iface, face;

3490:   DMGetDimension(dm, &dim);
3491:   DMGetDS(dm, &prob);
3492:   DMGetLocalSection(dm, &section);
3493:   PetscDSGetNumFields(prob, &Nf);
3494:   PetscDSGetTotalComponents(prob, &Nc);
3495:   PetscMalloc1(Nf, &isFE);
3496:   for (f = 0; f < Nf; ++f) {
3497:     PetscObject  obj;
3498:     PetscClassId id;

3500:     PetscDSGetDiscretization(prob, f, &obj);
3501:     PetscObjectGetClassId(obj, &id);
3502:     if (id == PETSCFE_CLASSID) {
3503:       isFE[f] = PETSC_TRUE;
3504:     } else if (id == PETSCFV_CLASSID) {
3505:       isFE[f] = PETSC_FALSE;
3506:     } else {
3507:       isFE[f] = PETSC_FALSE;
3508:     }
3509:   }
3510:   DMGetLabel(dm, "ghost", &ghostLabel);
3511:   VecGetArrayRead(locX, &x);
3512:   VecGetDM(faceGeometry, &dmFace);
3513:   VecGetArrayRead(faceGeometry, &facegeom);
3514:   VecGetDM(cellGeometry, &dmCell);
3515:   VecGetArrayRead(cellGeometry, &cellgeom);
3516:   if (locGrad) {
3517:     VecGetDM(locGrad, &dmGrad);
3518:     VecGetArrayRead(locGrad, &lgrad);
3519:   }
3520:   DMGetWorkArray(dm, numFaces * Nc, MPIU_SCALAR, uL);
3521:   DMGetWorkArray(dm, numFaces * Nc, MPIU_SCALAR, uR);
3522:   /* Right now just eat the extra work for FE (could make a cell loop) */
3523:   for (face = fStart, iface = 0; face < fEnd; ++face) {
3524:     const PetscInt  *cells;
3525:     PetscFVFaceGeom *fg;
3526:     PetscFVCellGeom *cgL, *cgR;
3527:     PetscScalar     *xL, *xR, *gL, *gR;
3528:     PetscScalar     *uLl = *uL, *uRl = *uR;
3529:     PetscInt         ghost, nsupp, nchild;

3531:     DMLabelGetValue(ghostLabel, face, &ghost);
3532:     DMPlexGetSupportSize(dm, face, &nsupp);
3533:     DMPlexGetTreeChildren(dm, face, &nchild, NULL);
3534:     if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
3535:     DMPlexPointLocalRead(dmFace, face, facegeom, &fg);
3536:     DMPlexGetSupport(dm, face, &cells);
3537:     DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL);
3538:     DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR);
3539:     for (f = 0; f < Nf; ++f) {
3540:       PetscInt off;

3542:       PetscDSGetComponentOffset(prob, f, &off);
3543:       if (isFE[f]) {
3544:         const PetscInt *cone;
3545:         PetscInt        comp, coneSizeL, coneSizeR, faceLocL, faceLocR, ldof, rdof, d;

3547:         xL = xR = NULL;
3548:         PetscSectionGetFieldComponents(section, f, &comp);
3549:         DMPlexVecGetClosure(dm, section, locX, cells[0], &ldof, (PetscScalar **)&xL);
3550:         DMPlexVecGetClosure(dm, section, locX, cells[1], &rdof, (PetscScalar **)&xR);
3551:         DMPlexGetCone(dm, cells[0], &cone);
3552:         DMPlexGetConeSize(dm, cells[0], &coneSizeL);
3553:         for (faceLocL = 0; faceLocL < coneSizeL; ++faceLocL)
3554:           if (cone[faceLocL] == face) break;
3555:         DMPlexGetCone(dm, cells[1], &cone);
3556:         DMPlexGetConeSize(dm, cells[1], &coneSizeR);
3557:         for (faceLocR = 0; faceLocR < coneSizeR; ++faceLocR)
3558:           if (cone[faceLocR] == face) break;
3560:         /* Check that FEM field has values in the right cell (sometimes its an FV ghost cell) */
3561:         /* TODO: this is a hack that might not be right for nonconforming */
3562:         if (faceLocL < coneSizeL) {
3563:           PetscFEEvaluateFaceFields_Internal(prob, f, faceLocL, xL, &uLl[iface * Nc + off]);
3564:           if (rdof == ldof && faceLocR < coneSizeR) PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface * Nc + off]);
3565:           else {
3566:             for (d = 0; d < comp; ++d) uRl[iface * Nc + off + d] = uLl[iface * Nc + off + d];
3567:           }
3568:         } else {
3569:           PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface * Nc + off]);
3570:           PetscSectionGetFieldComponents(section, f, &comp);
3571:           for (d = 0; d < comp; ++d) uLl[iface * Nc + off + d] = uRl[iface * Nc + off + d];
3572:         }
3573:         DMPlexVecRestoreClosure(dm, section, locX, cells[0], &ldof, (PetscScalar **)&xL);
3574:         DMPlexVecRestoreClosure(dm, section, locX, cells[1], &rdof, (PetscScalar **)&xR);
3575:       } else {
3576:         PetscFV  fv;
3577:         PetscInt numComp, c;

3579:         PetscDSGetDiscretization(prob, f, (PetscObject *)&fv);
3580:         PetscFVGetNumComponents(fv, &numComp);
3581:         DMPlexPointLocalFieldRead(dm, cells[0], f, x, &xL);
3582:         DMPlexPointLocalFieldRead(dm, cells[1], f, x, &xR);
3583:         if (dmGrad) {
3584:           PetscReal dxL[3], dxR[3];

3586:           DMPlexPointLocalRead(dmGrad, cells[0], lgrad, &gL);
3587:           DMPlexPointLocalRead(dmGrad, cells[1], lgrad, &gR);
3588:           DMPlex_WaxpyD_Internal(dim, -1, cgL->centroid, fg->centroid, dxL);
3589:           DMPlex_WaxpyD_Internal(dim, -1, cgR->centroid, fg->centroid, dxR);
3590:           for (c = 0; c < numComp; ++c) {
3591:             uLl[iface * Nc + off + c] = xL[c] + DMPlex_DotD_Internal(dim, &gL[c * dim], dxL);
3592:             uRl[iface * Nc + off + c] = xR[c] + DMPlex_DotD_Internal(dim, &gR[c * dim], dxR);
3593:           }
3594:         } else {
3595:           for (c = 0; c < numComp; ++c) {
3596:             uLl[iface * Nc + off + c] = xL[c];
3597:             uRl[iface * Nc + off + c] = xR[c];
3598:           }
3599:         }
3600:       }
3601:     }
3602:     ++iface;
3603:   }
3604:   *Nface = iface;
3605:   VecRestoreArrayRead(locX, &x);
3606:   VecRestoreArrayRead(faceGeometry, &facegeom);
3607:   VecRestoreArrayRead(cellGeometry, &cellgeom);
3608:   if (locGrad) VecRestoreArrayRead(locGrad, &lgrad);
3609:   PetscFree(isFE);
3610:   return 0;
3611: }

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

3616:   Input Parameters:
3617: + dm     - The DM
3618: . fStart - The first face to include
3619: . fEnd   - The first face to exclude
3620: . locX   - A local vector with the solution fields
3621: . locX_t - A local vector with solution field time derivatives, or NULL
3622: . faceGeometry - A local vector with face geometry
3623: . cellGeometry - A local vector with cell geometry
3624: - locaGrad - A local vector with field gradients, or NULL

3626:   Output Parameters:
3627: + Nface - The number of faces with field values
3628: . uL - The field values at the left side of the face
3629: - uR - The field values at the right side of the face

3631:   Level: developer

3633: .seealso: `DMPlexGetFaceFields()`
3634: @*/
3635: 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)
3636: {
3637:   DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uL);
3638:   DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uR);
3639:   return 0;
3640: }

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

3645:   Input Parameters:
3646: + dm     - The DM
3647: . fStart - The first face to include
3648: . fEnd   - The first face to exclude
3649: . faceGeometry - A local vector with face geometry
3650: - cellGeometry - A local vector with cell geometry

3652:   Output Parameters:
3653: + Nface - The number of faces with field values
3654: . fgeom - The extract the face centroid and normal
3655: - vol   - The cell volume

3657:   Level: developer

3659: .seealso: `DMPlexGetCellFields()`
3660: @*/
3661: PetscErrorCode DMPlexGetFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom **fgeom, PetscReal **vol)
3662: {
3663:   DM                 dmFace, dmCell;
3664:   DMLabel            ghostLabel;
3665:   const PetscScalar *facegeom, *cellgeom;
3666:   PetscInt           dim, numFaces = fEnd - fStart, iface, face;

3673:   DMGetDimension(dm, &dim);
3674:   DMGetLabel(dm, "ghost", &ghostLabel);
3675:   VecGetDM(faceGeometry, &dmFace);
3676:   VecGetArrayRead(faceGeometry, &facegeom);
3677:   VecGetDM(cellGeometry, &dmCell);
3678:   VecGetArrayRead(cellGeometry, &cellgeom);
3679:   PetscMalloc1(numFaces, fgeom);
3680:   DMGetWorkArray(dm, numFaces * 2, MPIU_SCALAR, vol);
3681:   for (face = fStart, iface = 0; face < fEnd; ++face) {
3682:     const PetscInt  *cells;
3683:     PetscFVFaceGeom *fg;
3684:     PetscFVCellGeom *cgL, *cgR;
3685:     PetscFVFaceGeom *fgeoml = *fgeom;
3686:     PetscReal       *voll   = *vol;
3687:     PetscInt         ghost, d, nchild, nsupp;

3689:     DMLabelGetValue(ghostLabel, face, &ghost);
3690:     DMPlexGetSupportSize(dm, face, &nsupp);
3691:     DMPlexGetTreeChildren(dm, face, &nchild, NULL);
3692:     if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
3693:     DMPlexPointLocalRead(dmFace, face, facegeom, &fg);
3694:     DMPlexGetSupport(dm, face, &cells);
3695:     DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL);
3696:     DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR);
3697:     for (d = 0; d < dim; ++d) {
3698:       fgeoml[iface].centroid[d] = fg->centroid[d];
3699:       fgeoml[iface].normal[d]   = fg->normal[d];
3700:     }
3701:     voll[iface * 2 + 0] = cgL->volume;
3702:     voll[iface * 2 + 1] = cgR->volume;
3703:     ++iface;
3704:   }
3705:   *Nface = iface;
3706:   VecRestoreArrayRead(faceGeometry, &facegeom);
3707:   VecRestoreArrayRead(cellGeometry, &cellgeom);
3708:   return 0;
3709: }

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

3714:   Input Parameters:
3715: + dm     - The DM
3716: . fStart - The first face to include
3717: . fEnd   - The first face to exclude
3718: . faceGeometry - A local vector with face geometry
3719: - cellGeometry - A local vector with cell geometry

3721:   Output Parameters:
3722: + Nface - The number of faces with field values
3723: . fgeom - The extract the face centroid and normal
3724: - vol   - The cell volume

3726:   Level: developer

3728: .seealso: `DMPlexGetFaceFields()`
3729: @*/
3730: PetscErrorCode DMPlexRestoreFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom **fgeom, PetscReal **vol)
3731: {
3732:   PetscFree(*fgeom);
3733:   DMRestoreWorkArray(dm, 0, MPIU_REAL, vol);
3734:   return 0;
3735: }

3737: PetscErrorCode DMSNESGetFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
3738: {
3739:   char           composeStr[33] = {0};
3740:   PetscObjectId  id;
3741:   PetscContainer container;

3743:   PetscObjectGetId((PetscObject)quad, &id);
3744:   PetscSNPrintf(composeStr, 32, "DMSNESGetFEGeom_%" PetscInt64_FMT "\n", id);
3745:   PetscObjectQuery((PetscObject)pointIS, composeStr, (PetscObject *)&container);
3746:   if (container) {
3747:     PetscContainerGetPointer(container, (void **)geom);
3748:   } else {
3749:     DMFieldCreateFEGeom(coordField, pointIS, quad, faceData, geom);
3750:     PetscContainerCreate(PETSC_COMM_SELF, &container);
3751:     PetscContainerSetPointer(container, (void *)*geom);
3752:     PetscContainerSetUserDestroy(container, PetscContainerUserDestroy_PetscFEGeom);
3753:     PetscObjectCompose((PetscObject)pointIS, composeStr, (PetscObject)container);
3754:     PetscContainerDestroy(&container);
3755:   }
3756:   return 0;
3757: }

3759: PetscErrorCode DMSNESRestoreFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
3760: {
3761:   *geom = NULL;
3762:   return 0;
3763: }

3765: PetscErrorCode DMPlexComputeResidual_Patch_Internal(DM dm, PetscSection section, IS cellIS, PetscReal t, Vec locX, Vec locX_t, Vec locF, void *user)
3766: {
3767:   DM_Plex        *mesh       = (DM_Plex *)dm->data;
3768:   const char     *name       = "Residual";
3769:   DM              dmAux      = NULL;
3770:   DMLabel         ghostLabel = NULL;
3771:   PetscDS         prob       = NULL;
3772:   PetscDS         probAux    = NULL;
3773:   PetscBool       useFEM     = PETSC_FALSE;
3774:   PetscBool       isImplicit = (locX_t || t == PETSC_MIN_REAL) ? PETSC_TRUE : PETSC_FALSE;
3775:   DMField         coordField = NULL;
3776:   Vec             locA;
3777:   PetscScalar    *u = NULL, *u_t, *a, *uL = NULL, *uR = NULL;
3778:   IS              chunkIS;
3779:   const PetscInt *cells;
3780:   PetscInt        cStart, cEnd, numCells;
3781:   PetscInt        Nf, f, totDim, totDimAux, numChunks, cellChunkSize, chunk, fStart, fEnd;
3782:   PetscInt        maxDegree = PETSC_MAX_INT;
3783:   PetscFormKey    key;
3784:   PetscQuadrature affineQuad = NULL, *quads = NULL;
3785:   PetscFEGeom    *affineGeom = NULL, **geoms = NULL;

3787:   PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0);
3788:   /* FEM+FVM */
3789:   /* 1: Get sizes from dm and dmAux */
3790:   DMGetLabel(dm, "ghost", &ghostLabel);
3791:   DMGetDS(dm, &prob);
3792:   PetscDSGetNumFields(prob, &Nf);
3793:   PetscDSGetTotalDimension(prob, &totDim);
3794:   DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA);
3795:   if (locA) {
3796:     VecGetDM(locA, &dmAux);
3797:     DMGetDS(dmAux, &probAux);
3798:     PetscDSGetTotalDimension(probAux, &totDimAux);
3799:   }
3800:   /* 2: Get geometric data */
3801:   for (f = 0; f < Nf; ++f) {
3802:     PetscObject  obj;
3803:     PetscClassId id;
3804:     PetscBool    fimp;

3806:     PetscDSGetImplicit(prob, f, &fimp);
3807:     if (isImplicit != fimp) continue;
3808:     PetscDSGetDiscretization(prob, f, &obj);
3809:     PetscObjectGetClassId(obj, &id);
3810:     if (id == PETSCFE_CLASSID) useFEM = PETSC_TRUE;
3812:   }
3813:   if (useFEM) {
3814:     DMGetCoordinateField(dm, &coordField);
3815:     DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree);
3816:     if (maxDegree <= 1) {
3817:       DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad);
3818:       if (affineQuad) DMSNESGetFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom);
3819:     } else {
3820:       PetscCalloc2(Nf, &quads, Nf, &geoms);
3821:       for (f = 0; f < Nf; ++f) {
3822:         PetscObject  obj;
3823:         PetscClassId id;
3824:         PetscBool    fimp;

3826:         PetscDSGetImplicit(prob, f, &fimp);
3827:         if (isImplicit != fimp) continue;
3828:         PetscDSGetDiscretization(prob, f, &obj);
3829:         PetscObjectGetClassId(obj, &id);
3830:         if (id == PETSCFE_CLASSID) {
3831:           PetscFE fe = (PetscFE)obj;

3833:           PetscFEGetQuadrature(fe, &quads[f]);
3834:           PetscObjectReference((PetscObject)quads[f]);
3835:           DMSNESGetFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]);
3836:         }
3837:       }
3838:     }
3839:   }
3840:   /* Loop over chunks */
3841:   ISGetPointRange(cellIS, &cStart, &cEnd, &cells);
3842:   DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd);
3843:   if (useFEM) ISCreate(PETSC_COMM_SELF, &chunkIS);
3844:   numCells      = cEnd - cStart;
3845:   numChunks     = 1;
3846:   cellChunkSize = numCells / numChunks;
3847:   numChunks     = PetscMin(1, numCells);
3848:   key.label     = NULL;
3849:   key.value     = 0;
3850:   key.part      = 0;
3851:   for (chunk = 0; chunk < numChunks; ++chunk) {
3852:     PetscScalar     *elemVec, *fluxL = NULL, *fluxR = NULL;
3853:     PetscReal       *vol   = NULL;
3854:     PetscFVFaceGeom *fgeom = NULL;
3855:     PetscInt         cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
3856:     PetscInt         numFaces = 0;

3858:     /* Extract field coefficients */
3859:     if (useFEM) {
3860:       ISGetPointSubrange(chunkIS, cS, cE, cells);
3861:       DMPlexGetCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a);
3862:       DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec);
3863:       PetscArrayzero(elemVec, numCells * totDim);
3864:     }
3865:     /* TODO We will interlace both our field coefficients (u, u_t, uL, uR, etc.) and our output (elemVec, fL, fR). I think this works */
3866:     /* Loop over fields */
3867:     for (f = 0; f < Nf; ++f) {
3868:       PetscObject  obj;
3869:       PetscClassId id;
3870:       PetscBool    fimp;
3871:       PetscInt     numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;

3873:       key.field = f;
3874:       PetscDSGetImplicit(prob, f, &fimp);
3875:       if (isImplicit != fimp) continue;
3876:       PetscDSGetDiscretization(prob, f, &obj);
3877:       PetscObjectGetClassId(obj, &id);
3878:       if (id == PETSCFE_CLASSID) {
3879:         PetscFE         fe        = (PetscFE)obj;
3880:         PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[f];
3881:         PetscFEGeom    *chunkGeom = NULL;
3882:         PetscQuadrature quad      = affineQuad ? affineQuad : quads[f];
3883:         PetscInt        Nq, Nb;

3885:         PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches);
3886:         PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL);
3887:         PetscFEGetDimension(fe, &Nb);
3888:         blockSize = Nb;
3889:         batchSize = numBlocks * blockSize;
3890:         PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches);
3891:         numChunks = numCells / (numBatches * batchSize);
3892:         Ne        = numChunks * numBatches * batchSize;
3893:         Nr        = numCells % (numBatches * batchSize);
3894:         offset    = numCells - Nr;
3895:         /* Integrate FE residual to get elemVec (need fields at quadrature points) */
3896:         /*   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) */
3897:         PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom);
3898:         PetscFEIntegrateResidual(prob, key, Ne, chunkGeom, u, u_t, probAux, a, t, elemVec);
3899:         PetscFEGeomGetChunk(geom, offset, numCells, &chunkGeom);
3900:         PetscFEIntegrateResidual(prob, key, Nr, chunkGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, &a[offset * totDimAux], t, &elemVec[offset * totDim]);
3901:         PetscFEGeomRestoreChunk(geom, offset, numCells, &chunkGeom);
3902:       } else if (id == PETSCFV_CLASSID) {
3903:         PetscFV fv = (PetscFV)obj;

3905:         Ne = numFaces;
3906:         /* Riemann solve over faces (need fields at face centroids) */
3907:         /*   We need to evaluate FE fields at those coordinates */
3908:         PetscFVIntegrateRHSFunction(fv, prob, f, Ne, fgeom, vol, uL, uR, fluxL, fluxR);
3909:       } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
3910:     }
3911:     /* Loop over domain */
3912:     if (useFEM) {
3913:       /* Add elemVec to locX */
3914:       for (c = cS; c < cE; ++c) {
3915:         const PetscInt cell = cells ? cells[c] : c;
3916:         const PetscInt cind = c - cStart;

3918:         if (mesh->printFEM > 1) DMPrintCellVector(cell, name, totDim, &elemVec[cind * totDim]);
3919:         if (ghostLabel) {
3920:           PetscInt ghostVal;

3922:           DMLabelGetValue(ghostLabel, cell, &ghostVal);
3923:           if (ghostVal > 0) continue;
3924:         }
3925:         DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind * totDim], ADD_ALL_VALUES);
3926:       }
3927:     }
3928:     /* Handle time derivative */
3929:     if (locX_t) {
3930:       PetscScalar *x_t, *fa;

3932:       VecGetArray(locF, &fa);
3933:       VecGetArray(locX_t, &x_t);
3934:       for (f = 0; f < Nf; ++f) {
3935:         PetscFV      fv;
3936:         PetscObject  obj;
3937:         PetscClassId id;
3938:         PetscInt     pdim, d;

3940:         PetscDSGetDiscretization(prob, f, &obj);
3941:         PetscObjectGetClassId(obj, &id);
3942:         if (id != PETSCFV_CLASSID) continue;
3943:         fv = (PetscFV)obj;
3944:         PetscFVGetNumComponents(fv, &pdim);
3945:         for (c = cS; c < cE; ++c) {
3946:           const PetscInt cell = cells ? cells[c] : c;
3947:           PetscScalar   *u_t, *r;

3949:           if (ghostLabel) {
3950:             PetscInt ghostVal;

3952:             DMLabelGetValue(ghostLabel, cell, &ghostVal);
3953:             if (ghostVal > 0) continue;
3954:           }
3955:           DMPlexPointLocalFieldRead(dm, cell, f, x_t, &u_t);
3956:           DMPlexPointLocalFieldRef(dm, cell, f, fa, &r);
3957:           for (d = 0; d < pdim; ++d) r[d] += u_t[d];
3958:         }
3959:       }
3960:       VecRestoreArray(locX_t, &x_t);
3961:       VecRestoreArray(locF, &fa);
3962:     }
3963:     if (useFEM) {
3964:       DMPlexRestoreCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a);
3965:       DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec);
3966:     }
3967:   }
3968:   if (useFEM) ISDestroy(&chunkIS);
3969:   ISRestorePointRange(cellIS, &cStart, &cEnd, &cells);
3970:   /* TODO Could include boundary residual here (see DMPlexComputeResidual_Internal) */
3971:   if (useFEM) {
3972:     if (maxDegree <= 1) {
3973:       DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom);
3974:       PetscQuadratureDestroy(&affineQuad);
3975:     } else {
3976:       for (f = 0; f < Nf; ++f) {
3977:         DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]);
3978:         PetscQuadratureDestroy(&quads[f]);
3979:       }
3980:       PetscFree2(quads, geoms);
3981:     }
3982:   }
3983:   PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0);
3984:   return 0;
3985: }

3987: /*
3988:   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

3990:   X   - The local solution vector
3991:   X_t - The local solution time derivative vector, or NULL
3992: */
3993: 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)
3994: {
3995:   DM_Plex        *mesh = (DM_Plex *)dm->data;
3996:   const char     *name = "Jacobian", *nameP = "JacobianPre";
3997:   DM              dmAux = NULL;
3998:   PetscDS         prob, probAux = NULL;
3999:   PetscSection    sectionAux = NULL;
4000:   Vec             A;
4001:   DMField         coordField;
4002:   PetscFEGeom    *cgeomFEM;
4003:   PetscQuadrature qGeom = NULL;
4004:   Mat             J = Jac, JP = JacP;
4005:   PetscScalar    *work, *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL, *elemMatP = NULL, *elemMatD = NULL;
4006:   PetscBool       hasJac, hasPrec, hasDyn, assembleJac, *isFE, hasFV = PETSC_FALSE;
4007:   const PetscInt *cells;
4008:   PetscFormKey    key;
4009:   PetscInt        Nf, fieldI, fieldJ, maxDegree, numCells, cStart, cEnd, numChunks, chunkSize, chunk, totDim, totDimAux = 0, sz, wsz, off = 0, offCell = 0;

4011:   ISGetLocalSize(cellIS, &numCells);
4012:   ISGetPointRange(cellIS, &cStart, &cEnd, &cells);
4013:   PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0);
4014:   DMGetDS(dm, &prob);
4015:   DMGetAuxiliaryVec(dm, NULL, 0, 0, &A);
4016:   if (A) {
4017:     VecGetDM(A, &dmAux);
4018:     DMGetLocalSection(dmAux, &sectionAux);
4019:     DMGetDS(dmAux, &probAux);
4020:   }
4021:   /* Get flags */
4022:   PetscDSGetNumFields(prob, &Nf);
4023:   DMGetWorkArray(dm, Nf, MPIU_BOOL, &isFE);
4024:   for (fieldI = 0; fieldI < Nf; ++fieldI) {
4025:     PetscObject  disc;
4026:     PetscClassId id;
4027:     PetscDSGetDiscretization(prob, fieldI, &disc);
4028:     PetscObjectGetClassId(disc, &id);
4029:     if (id == PETSCFE_CLASSID) {
4030:       isFE[fieldI] = PETSC_TRUE;
4031:     } else if (id == PETSCFV_CLASSID) {
4032:       hasFV        = PETSC_TRUE;
4033:       isFE[fieldI] = PETSC_FALSE;
4034:     }
4035:   }
4036:   PetscDSHasJacobian(prob, &hasJac);
4037:   PetscDSHasJacobianPreconditioner(prob, &hasPrec);
4038:   PetscDSHasDynamicJacobian(prob, &hasDyn);
4039:   assembleJac = hasJac && hasPrec && (Jac != JacP) ? PETSC_TRUE : PETSC_FALSE;
4040:   hasDyn      = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
4041:   if (hasFV) MatSetOption(JP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE); /* No allocated space for FV stuff, so ignore the zero entries */
4042:   PetscDSGetTotalDimension(prob, &totDim);
4043:   if (probAux) PetscDSGetTotalDimension(probAux, &totDimAux);
4044:   /* Compute batch sizes */
4045:   if (isFE[0]) {
4046:     PetscFE         fe;
4047:     PetscQuadrature q;
4048:     PetscInt        numQuadPoints, numBatches, batchSize, numBlocks, blockSize, Nb;

4050:     PetscDSGetDiscretization(prob, 0, (PetscObject *)&fe);
4051:     PetscFEGetQuadrature(fe, &q);
4052:     PetscQuadratureGetData(q, NULL, NULL, &numQuadPoints, NULL, NULL);
4053:     PetscFEGetDimension(fe, &Nb);
4054:     PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches);
4055:     blockSize = Nb * numQuadPoints;
4056:     batchSize = numBlocks * blockSize;
4057:     chunkSize = numBatches * batchSize;
4058:     numChunks = numCells / chunkSize + numCells % chunkSize;
4059:     PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches);
4060:   } else {
4061:     chunkSize = numCells;
4062:     numChunks = 1;
4063:   }
4064:   /* Get work space */
4065:   wsz = (((X ? 1 : 0) + (X_t ? 1 : 0) + (dmAux ? 1 : 0)) * totDim + ((hasJac ? 1 : 0) + (hasPrec ? 1 : 0) + (hasDyn ? 1 : 0)) * totDim * totDim) * chunkSize;
4066:   DMGetWorkArray(dm, wsz, MPIU_SCALAR, &work);
4067:   PetscArrayzero(work, wsz);
4068:   off      = 0;
4069:   u        = X ? (sz = chunkSize * totDim, off += sz, work + off - sz) : NULL;
4070:   u_t      = X_t ? (sz = chunkSize * totDim, off += sz, work + off - sz) : NULL;
4071:   a        = dmAux ? (sz = chunkSize * totDimAux, off += sz, work + off - sz) : NULL;
4072:   elemMat  = hasJac ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL;
4073:   elemMatP = hasPrec ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL;
4074:   elemMatD = hasDyn ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL;
4076:   /* Setup geometry */
4077:   DMGetCoordinateField(dm, &coordField);
4078:   DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree);
4079:   if (maxDegree <= 1) DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom);
4080:   if (!qGeom) {
4081:     PetscFE fe;

4083:     PetscDSGetDiscretization(prob, 0, (PetscObject *)&fe);
4084:     PetscFEGetQuadrature(fe, &qGeom);
4085:     PetscObjectReference((PetscObject)qGeom);
4086:   }
4087:   DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM);
4088:   /* Compute volume integrals */
4089:   if (assembleJac) MatZeroEntries(J);
4090:   MatZeroEntries(JP);
4091:   key.label = NULL;
4092:   key.value = 0;
4093:   key.part  = 0;
4094:   for (chunk = 0; chunk < numChunks; ++chunk, offCell += chunkSize) {
4095:     const PetscInt Ncell = PetscMin(chunkSize, numCells - offCell);
4096:     PetscInt       c;

4098:     /* Extract values */
4099:     for (c = 0; c < Ncell; ++c) {
4100:       const PetscInt cell = cells ? cells[c + offCell] : c + offCell;
4101:       PetscScalar   *x = NULL, *x_t = NULL;
4102:       PetscInt       i;

4104:       if (X) {
4105:         DMPlexVecGetClosure(dm, section, X, cell, NULL, &x);
4106:         for (i = 0; i < totDim; ++i) u[c * totDim + i] = x[i];
4107:         DMPlexVecRestoreClosure(dm, section, X, cell, NULL, &x);
4108:       }
4109:       if (X_t) {
4110:         DMPlexVecGetClosure(dm, section, X_t, cell, NULL, &x_t);
4111:         for (i = 0; i < totDim; ++i) u_t[c * totDim + i] = x_t[i];
4112:         DMPlexVecRestoreClosure(dm, section, X_t, cell, NULL, &x_t);
4113:       }
4114:       if (dmAux) {
4115:         DMPlexVecGetClosure(dmAux, sectionAux, A, cell, NULL, &x);
4116:         for (i = 0; i < totDimAux; ++i) a[c * totDimAux + i] = x[i];
4117:         DMPlexVecRestoreClosure(dmAux, sectionAux, A, cell, NULL, &x);
4118:       }
4119:     }
4120:     for (fieldI = 0; fieldI < Nf; ++fieldI) {
4121:       PetscFE fe;
4122:       PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe);
4123:       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
4124:         key.field = fieldI * Nf + fieldJ;
4125:         if (hasJac) PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMat);
4126:         if (hasPrec) PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatP);
4127:         if (hasDyn) PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatD);
4128:       }
4129:       /* For finite volume, add the identity */
4130:       if (!isFE[fieldI]) {
4131:         PetscFV  fv;
4132:         PetscInt eOffset = 0, Nc, fc, foff;

4134:         PetscDSGetFieldOffset(prob, fieldI, &foff);
4135:         PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fv);
4136:         PetscFVGetNumComponents(fv, &Nc);
4137:         for (c = 0; c < chunkSize; ++c, eOffset += totDim * totDim) {
4138:           for (fc = 0; fc < Nc; ++fc) {
4139:             const PetscInt i = foff + fc;
4140:             if (hasJac) elemMat[eOffset + i * totDim + i] = 1.0;
4141:             if (hasPrec) elemMatP[eOffset + i * totDim + i] = 1.0;
4142:           }
4143:         }
4144:       }
4145:     }
4146:     /*   Add contribution from X_t */
4147:     if (hasDyn) {
4148:       for (c = 0; c < chunkSize * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
4149:     }
4150:     /* Insert values into matrix */
4151:     for (c = 0; c < Ncell; ++c) {
4152:       const PetscInt cell = cells ? cells[c + offCell] : c + offCell;
4153:       if (mesh->printFEM > 1) {
4154:         if (hasJac) DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[(c - cStart) * totDim * totDim]);
4155:         if (hasPrec) DMPrintCellMatrix(cell, nameP, totDim, totDim, &elemMatP[(c - cStart) * totDim * totDim]);
4156:       }
4157:       if (assembleJac) DMPlexMatSetClosure(dm, section, globalSection, Jac, cell, &elemMat[(c - cStart) * totDim * totDim], ADD_VALUES);
4158:       DMPlexMatSetClosure(dm, section, globalSection, JP, cell, &elemMat[(c - cStart) * totDim * totDim], ADD_VALUES);
4159:     }
4160:   }
4161:   /* Cleanup */
4162:   DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM);
4163:   PetscQuadratureDestroy(&qGeom);
4164:   if (hasFV) MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE);
4165:   DMRestoreWorkArray(dm, Nf, MPIU_BOOL, &isFE);
4166:   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);
4167:   /* Compute boundary integrals */
4168:   /* DMPlexComputeBdJacobian_Internal(dm, X, X_t, t, X_tShift, Jac, JacP, ctx); */
4169:   /* Assemble matrix */
4170:   if (assembleJac) {
4171:     MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY);
4172:     MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY);
4173:   }
4174:   MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY);
4175:   MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY);
4176:   PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0);
4177:   return 0;
4178: }

4180: /******** FEM Assembly Function ********/

4182: static PetscErrorCode DMConvertPlex_Internal(DM dm, DM *plex, PetscBool copy)
4183: {
4184:   PetscBool isPlex;

4186:   PetscObjectTypeCompare((PetscObject)dm, DMPLEX, &isPlex);
4187:   if (isPlex) {
4188:     *plex = dm;
4189:     PetscObjectReference((PetscObject)dm);
4190:   } else {
4191:     PetscObjectQuery((PetscObject)dm, "dm_plex", (PetscObject *)plex);
4192:     if (!*plex) {
4193:       DMConvert(dm, DMPLEX, plex);
4194:       PetscObjectCompose((PetscObject)dm, "dm_plex", (PetscObject)*plex);
4195:       if (copy) DMCopyAuxiliaryVec(dm, *plex);
4196:     } else {
4197:       PetscObjectReference((PetscObject)*plex);
4198:     }
4199:   }
4200:   return 0;
4201: }

4203: /*@
4204:   DMPlexGetGeometryFVM - Return precomputed geometric data

4206:   Collective on DM

4208:   Input Parameter:
4209: . dm - The DM

4211:   Output Parameters:
4212: + facegeom - The values precomputed from face geometry
4213: . cellgeom - The values precomputed from cell geometry
4214: - minRadius - The minimum radius over the mesh of an inscribed sphere in a cell

4216:   Level: developer

4218: .seealso: `DMTSSetRHSFunctionLocal()`
4219: @*/
4220: PetscErrorCode DMPlexGetGeometryFVM(DM dm, Vec *facegeom, Vec *cellgeom, PetscReal *minRadius)
4221: {
4222:   DM plex;

4225:   DMConvertPlex_Internal(dm, &plex, PETSC_TRUE);
4226:   DMPlexGetDataFVM(plex, NULL, cellgeom, facegeom, NULL);
4227:   if (minRadius) DMPlexGetMinRadius(plex, minRadius);
4228:   DMDestroy(&plex);
4229:   return 0;
4230: }

4232: /*@
4233:   DMPlexGetGradientDM - Return gradient data layout

4235:   Collective on DM

4237:   Input Parameters:
4238: + dm - The DM
4239: - fv - The PetscFV

4241:   Output Parameter:
4242: . dmGrad - The layout for gradient values

4244:   Level: developer

4246: .seealso: `DMPlexGetGeometryFVM()`
4247: @*/
4248: PetscErrorCode DMPlexGetGradientDM(DM dm, PetscFV fv, DM *dmGrad)
4249: {
4250:   DM        plex;
4251:   PetscBool computeGradients;

4256:   PetscFVGetComputeGradients(fv, &computeGradients);
4257:   if (!computeGradients) {
4258:     *dmGrad = NULL;
4259:     return 0;
4260:   }
4261:   DMConvertPlex_Internal(dm, &plex, PETSC_TRUE);
4262:   DMPlexGetDataFVM(plex, fv, NULL, NULL, dmGrad);
4263:   DMDestroy(&plex);
4264:   return 0;
4265: }

4267: static PetscErrorCode DMPlexComputeBdResidual_Single_Internal(DM dm, PetscReal t, PetscWeakForm wf, PetscFormKey key, Vec locX, Vec locX_t, Vec locF, DMField coordField, IS facetIS)
4268: {
4269:   DM_Plex        *mesh = (DM_Plex *)dm->data;
4270:   DM              plex = NULL, plexA = NULL;
4271:   DMEnclosureType encAux;
4272:   PetscDS         prob, probAux       = NULL;
4273:   PetscSection    section, sectionAux = NULL;
4274:   Vec             locA = NULL;
4275:   PetscScalar    *u = NULL, *u_t = NULL, *a = NULL, *elemVec = NULL;
4276:   PetscInt        totDim, totDimAux = 0;

4278:   DMConvert(dm, DMPLEX, &plex);
4279:   DMGetLocalSection(dm, &section);
4280:   DMGetDS(dm, &prob);
4281:   PetscDSGetTotalDimension(prob, &totDim);
4282:   DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &locA);
4283:   if (locA) {
4284:     DM dmAux;

4286:     VecGetDM(locA, &dmAux);
4287:     DMGetEnclosureRelation(dmAux, dm, &encAux);
4288:     DMConvert(dmAux, DMPLEX, &plexA);
4289:     DMGetDS(plexA, &probAux);
4290:     PetscDSGetTotalDimension(probAux, &totDimAux);
4291:     DMGetLocalSection(plexA, &sectionAux);
4292:   }
4293:   {
4294:     PetscFEGeom    *fgeom;
4295:     PetscInt        maxDegree;
4296:     PetscQuadrature qGeom = NULL;
4297:     IS              pointIS;
4298:     const PetscInt *points;
4299:     PetscInt        numFaces, face, Nq;

4301:     DMLabelGetStratumIS(key.label, key.value, &pointIS);
4302:     if (!pointIS) goto end; /* No points with that id on this process */
4303:     {
4304:       IS isectIS;

4306:       /* TODO: Special cases of ISIntersect where it is quick to check a priori if one is a superset of the other */
4307:       ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS);
4308:       ISDestroy(&pointIS);
4309:       pointIS = isectIS;
4310:     }
4311:     ISGetLocalSize(pointIS, &numFaces);
4312:     ISGetIndices(pointIS, &points);
4313:     PetscMalloc4(numFaces * totDim, &u, locX_t ? numFaces * totDim : 0, &u_t, numFaces * totDim, &elemVec, locA ? numFaces * totDimAux : 0, &a);
4314:     DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree);
4315:     if (maxDegree <= 1) DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom);
4316:     if (!qGeom) {
4317:       PetscFE fe;

4319:       PetscDSGetDiscretization(prob, key.field, (PetscObject *)&fe);
4320:       PetscFEGetFaceQuadrature(fe, &qGeom);
4321:       PetscObjectReference((PetscObject)qGeom);
4322:     }
4323:     PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL);
4324:     DMSNESGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom);
4325:     for (face = 0; face < numFaces; ++face) {
4326:       const PetscInt point = points[face], *support;
4327:       PetscScalar   *x     = NULL;
4328:       PetscInt       i;

4330:       DMPlexGetSupport(dm, point, &support);
4331:       DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x);
4332:       for (i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
4333:       DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x);
4334:       if (locX_t) {
4335:         DMPlexVecGetClosure(plex, section, locX_t, support[0], NULL, &x);
4336:         for (i = 0; i < totDim; ++i) u_t[face * totDim + i] = x[i];
4337:         DMPlexVecRestoreClosure(plex, section, locX_t, support[0], NULL, &x);
4338:       }
4339:       if (locA) {
4340:         PetscInt subp;

4342:         DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp);
4343:         DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x);
4344:         for (i = 0; i < totDimAux; ++i) a[face * totDimAux + i] = x[i];
4345:         DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x);
4346:       }
4347:     }
4348:     PetscArrayzero(elemVec, numFaces * totDim);
4349:     {
4350:       PetscFE      fe;
4351:       PetscInt     Nb;
4352:       PetscFEGeom *chunkGeom = NULL;
4353:       /* Conforming batches */
4354:       PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
4355:       /* Remainder */
4356:       PetscInt Nr, offset;

4358:       PetscDSGetDiscretization(prob, key.field, (PetscObject *)&fe);
4359:       PetscFEGetDimension(fe, &Nb);
4360:       PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches);
4361:       /* TODO: documentation is unclear about what is going on with these numbers: how should Nb / Nq factor in ? */
4362:       blockSize = Nb;
4363:       batchSize = numBlocks * blockSize;
4364:       PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches);
4365:       numChunks = numFaces / (numBatches * batchSize);
4366:       Ne        = numChunks * numBatches * batchSize;
4367:       Nr        = numFaces % (numBatches * batchSize);
4368:       offset    = numFaces - Nr;
4369:       PetscFEGeomGetChunk(fgeom, 0, offset, &chunkGeom);
4370:       PetscFEIntegrateBdResidual(prob, wf, key, Ne, chunkGeom, u, u_t, probAux, a, t, elemVec);
4371:       PetscFEGeomRestoreChunk(fgeom, 0, offset, &chunkGeom);
4372:       PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom);
4373:       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]);
4374:       PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom);
4375:     }
4376:     for (face = 0; face < numFaces; ++face) {
4377:       const PetscInt point = points[face], *support;

4379:       if (mesh->printFEM > 1) DMPrintCellVector(point, "BdResidual", totDim, &elemVec[face * totDim]);
4380:       DMPlexGetSupport(plex, point, &support);
4381:       DMPlexVecSetClosure(plex, NULL, locF, support[0], &elemVec[face * totDim], ADD_ALL_VALUES);
4382:     }
4383:     DMSNESRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom);
4384:     PetscQuadratureDestroy(&qGeom);
4385:     ISRestoreIndices(pointIS, &points);
4386:     ISDestroy(&pointIS);
4387:     PetscFree4(u, u_t, elemVec, a);
4388:   }
4389: end:
4390:   DMDestroy(&plex);
4391:   DMDestroy(&plexA);
4392:   return 0;
4393: }

4395: PetscErrorCode DMPlexComputeBdResidualSingle(DM dm, PetscReal t, PetscWeakForm wf, PetscFormKey key, Vec locX, Vec locX_t, Vec locF)
4396: {
4397:   DMField  coordField;
4398:   DMLabel  depthLabel;
4399:   IS       facetIS;
4400:   PetscInt dim;

4402:   DMGetDimension(dm, &dim);
4403:   DMPlexGetDepthLabel(dm, &depthLabel);
4404:   DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS);
4405:   DMGetCoordinateField(dm, &coordField);
4406:   DMPlexComputeBdResidual_Single_Internal(dm, t, wf, key, locX, locX_t, locF, coordField, facetIS);
4407:   ISDestroy(&facetIS);
4408:   return 0;
4409: }

4411: PetscErrorCode DMPlexComputeBdResidual_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
4412: {
4413:   PetscDS  prob;
4414:   PetscInt numBd, bd;
4415:   DMField  coordField = NULL;
4416:   IS       facetIS    = NULL;
4417:   DMLabel  depthLabel;
4418:   PetscInt dim;

4420:   DMGetDS(dm, &prob);
4421:   DMPlexGetDepthLabel(dm, &depthLabel);
4422:   DMGetDimension(dm, &dim);
4423:   DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS);
4424:   PetscDSGetNumBoundary(prob, &numBd);
4425:   for (bd = 0; bd < numBd; ++bd) {
4426:     PetscWeakForm           wf;
4427:     DMBoundaryConditionType type;
4428:     DMLabel                 label;
4429:     const PetscInt         *values;
4430:     PetscInt                field, numValues, v;
4431:     PetscObject             obj;
4432:     PetscClassId            id;
4433:     PetscFormKey            key;

4435:     PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &field, NULL, NULL, NULL, NULL, NULL);
4436:     if (type & DM_BC_ESSENTIAL) continue;
4437:     PetscDSGetDiscretization(prob, field, &obj);
4438:     PetscObjectGetClassId(obj, &id);
4439:     if (id != PETSCFE_CLASSID) continue;
4440:     if (!facetIS) {
4441:       DMLabel  depthLabel;
4442:       PetscInt dim;

4444:       DMPlexGetDepthLabel(dm, &depthLabel);
4445:       DMGetDimension(dm, &dim);
4446:       DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS);
4447:     }
4448:     DMGetCoordinateField(dm, &coordField);
4449:     for (v = 0; v < numValues; ++v) {
4450:       key.label = label;
4451:       key.value = values[v];
4452:       key.field = field;
4453:       key.part  = 0;
4454:       DMPlexComputeBdResidual_Single_Internal(dm, t, wf, key, locX, locX_t, locF, coordField, facetIS);
4455:     }
4456:   }
4457:   ISDestroy(&facetIS);
4458:   return 0;
4459: }

4461: PetscErrorCode DMPlexComputeResidual_Internal(DM dm, PetscFormKey key, IS cellIS, PetscReal time, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
4462: {
4463:   DM_Plex         *mesh       = (DM_Plex *)dm->data;
4464:   const char      *name       = "Residual";
4465:   DM               dmAux      = NULL;
4466:   DM               dmGrad     = NULL;
4467:   DMLabel          ghostLabel = NULL;
4468:   PetscDS          ds         = NULL;
4469:   PetscDS          dsAux      = NULL;
4470:   PetscSection     section    = NULL;
4471:   PetscBool        useFEM     = PETSC_FALSE;
4472:   PetscBool        useFVM     = PETSC_FALSE;
4473:   PetscBool        isImplicit = (locX_t || time == PETSC_MIN_REAL) ? PETSC_TRUE : PETSC_FALSE;
4474:   PetscFV          fvm        = NULL;
4475:   PetscFVCellGeom *cgeomFVM   = NULL;
4476:   PetscFVFaceGeom *fgeomFVM   = NULL;
4477:   DMField          coordField = NULL;
4478:   Vec              locA, cellGeometryFVM = NULL, faceGeometryFVM = NULL, grad, locGrad = NULL;
4479:   PetscScalar     *u = NULL, *u_t, *a, *uL, *uR;
4480:   IS               chunkIS;
4481:   const PetscInt  *cells;
4482:   PetscInt         cStart, cEnd, numCells;
4483:   PetscInt         Nf, f, totDim, totDimAux, numChunks, cellChunkSize, faceChunkSize, chunk, fStart, fEnd;
4484:   PetscInt         maxDegree  = PETSC_MAX_INT;
4485:   PetscQuadrature  affineQuad = NULL, *quads = NULL;
4486:   PetscFEGeom     *affineGeom = NULL, **geoms = NULL;

4488:   if (!cellIS) return 0;
4489:   PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0);
4490:   /* TODO The places where we have to use isFE are probably the member functions for the PetscDisc class */
4491:   /* TODO The FVM geometry is over-manipulated. Make the precalc functions return exactly what we need */
4492:   /* FEM+FVM */
4493:   ISGetPointRange(cellIS, &cStart, &cEnd, &cells);
4494:   DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd);
4495:   /* 1: Get sizes from dm and dmAux */
4496:   DMGetLocalSection(dm, &section);
4497:   DMGetLabel(dm, "ghost", &ghostLabel);
4498:   DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds);
4499:   PetscDSGetNumFields(ds, &Nf);
4500:   PetscDSGetTotalDimension(ds, &totDim);
4501:   DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &locA);
4502:   if (locA) {
4503:     PetscInt subcell;
4504:     VecGetDM(locA, &dmAux);
4505:     DMGetEnclosurePoint(dmAux, dm, DM_ENC_UNKNOWN, cells ? cells[cStart] : cStart, &subcell);
4506:     DMGetCellDS(dmAux, subcell, &dsAux);
4507:     PetscDSGetTotalDimension(dsAux, &totDimAux);
4508:   }
4509:   /* 2: Get geometric data */
4510:   for (f = 0; f < Nf; ++f) {
4511:     PetscObject  obj;
4512:     PetscClassId id;
4513:     PetscBool    fimp;

4515:     PetscDSGetImplicit(ds, f, &fimp);
4516:     if (isImplicit != fimp) continue;
4517:     PetscDSGetDiscretization(ds, f, &obj);
4518:     PetscObjectGetClassId(obj, &id);
4519:     if (id == PETSCFE_CLASSID) useFEM = PETSC_TRUE;
4520:     if (id == PETSCFV_CLASSID) {
4521:       useFVM = PETSC_TRUE;
4522:       fvm    = (PetscFV)obj;
4523:     }
4524:   }
4525:   if (useFEM) {
4526:     DMGetCoordinateField(dm, &coordField);
4527:     DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree);
4528:     if (maxDegree <= 1) {
4529:       DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad);
4530:       if (affineQuad) DMSNESGetFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom);
4531:     } else {
4532:       PetscCalloc2(Nf, &quads, Nf, &geoms);
4533:       for (f = 0; f < Nf; ++f) {
4534:         PetscObject  obj;
4535:         PetscClassId id;
4536:         PetscBool    fimp;

4538:         PetscDSGetImplicit(ds, f, &fimp);
4539:         if (isImplicit != fimp) continue;
4540:         PetscDSGetDiscretization(ds, f, &obj);
4541:         PetscObjectGetClassId(obj, &id);
4542:         if (id == PETSCFE_CLASSID) {
4543:           PetscFE fe = (PetscFE)obj;

4545:           PetscFEGetQuadrature(fe, &quads[f]);
4546:           PetscObjectReference((PetscObject)quads[f]);
4547:           DMSNESGetFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]);
4548:         }
4549:       }
4550:     }
4551:   }
4552:   if (useFVM) {
4553:     DMPlexGetGeometryFVM(dm, &faceGeometryFVM, &cellGeometryFVM, NULL);
4554:     VecGetArrayRead(faceGeometryFVM, (const PetscScalar **)&fgeomFVM);
4555:     VecGetArrayRead(cellGeometryFVM, (const PetscScalar **)&cgeomFVM);
4556:     /* Reconstruct and limit cell gradients */
4557:     DMPlexGetGradientDM(dm, fvm, &dmGrad);
4558:     if (dmGrad) {
4559:       DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd);
4560:       DMGetGlobalVector(dmGrad, &grad);
4561:       DMPlexReconstructGradients_Internal(dm, fvm, fStart, fEnd, faceGeometryFVM, cellGeometryFVM, locX, grad);
4562:       /* Communicate gradient values */
4563:       DMGetLocalVector(dmGrad, &locGrad);
4564:       DMGlobalToLocalBegin(dmGrad, grad, INSERT_VALUES, locGrad);
4565:       DMGlobalToLocalEnd(dmGrad, grad, INSERT_VALUES, locGrad);
4566:       DMRestoreGlobalVector(dmGrad, &grad);
4567:     }
4568:     /* Handle non-essential (e.g. outflow) boundary values */
4569:     DMPlexInsertBoundaryValues(dm, PETSC_FALSE, locX, time, faceGeometryFVM, cellGeometryFVM, locGrad);
4570:   }
4571:   /* Loop over chunks */
4572:   if (useFEM) ISCreate(PETSC_COMM_SELF, &chunkIS);
4573:   numCells      = cEnd - cStart;
4574:   numChunks     = 1;
4575:   cellChunkSize = numCells / numChunks;
4576:   faceChunkSize = (fEnd - fStart) / numChunks;
4577:   numChunks     = PetscMin(1, numCells);
4578:   for (chunk = 0; chunk < numChunks; ++chunk) {
4579:     PetscScalar     *elemVec, *fluxL, *fluxR;
4580:     PetscReal       *vol;
4581:     PetscFVFaceGeom *fgeom;
4582:     PetscInt         cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
4583:     PetscInt         fS = fStart + chunk * faceChunkSize, fE = PetscMin(fS + faceChunkSize, fEnd), numFaces = 0, face;

4585:     /* Extract field coefficients */
4586:     if (useFEM) {
4587:       ISGetPointSubrange(chunkIS, cS, cE, cells);
4588:       DMPlexGetCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a);
4589:       DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec);
4590:       PetscArrayzero(elemVec, numCells * totDim);
4591:     }
4592:     if (useFVM) {
4593:       DMPlexGetFaceFields(dm, fS, fE, locX, locX_t, faceGeometryFVM, cellGeometryFVM, locGrad, &numFaces, &uL, &uR);
4594:       DMPlexGetFaceGeometry(dm, fS, fE, faceGeometryFVM, cellGeometryFVM, &numFaces, &fgeom, &vol);
4595:       DMGetWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxL);
4596:       DMGetWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxR);
4597:       PetscArrayzero(fluxL, numFaces * totDim);
4598:       PetscArrayzero(fluxR, numFaces * totDim);
4599:     }
4600:     /* TODO We will interlace both our field coefficients (u, u_t, uL, uR, etc.) and our output (elemVec, fL, fR). I think this works */
4601:     /* Loop over fields */
4602:     for (f = 0; f < Nf; ++f) {
4603:       PetscObject  obj;
4604:       PetscClassId id;
4605:       PetscBool    fimp;
4606:       PetscInt     numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;

4608:       key.field = f;
4609:       PetscDSGetImplicit(ds, f, &fimp);
4610:       if (isImplicit != fimp) continue;
4611:       PetscDSGetDiscretization(ds, f, &obj);
4612:       PetscObjectGetClassId(obj, &id);
4613:       if (id == PETSCFE_CLASSID) {
4614:         PetscFE         fe        = (PetscFE)obj;
4615:         PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[f];
4616:         PetscFEGeom    *chunkGeom = NULL;
4617:         PetscQuadrature quad      = affineQuad ? affineQuad : quads[f];
4618:         PetscInt        Nq, Nb;

4620:         PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches);
4621:         PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL);
4622:         PetscFEGetDimension(fe, &Nb);
4623:         blockSize = Nb;
4624:         batchSize = numBlocks * blockSize;
4625:         PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches);
4626:         numChunks = numCells / (numBatches * batchSize);
4627:         Ne        = numChunks * numBatches * batchSize;
4628:         Nr        = numCells % (numBatches * batchSize);
4629:         offset    = numCells - Nr;
4630:         /* Integrate FE residual to get elemVec (need fields at quadrature points) */
4631:         /*   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) */
4632:         PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom);
4633:         PetscFEIntegrateResidual(ds, key, Ne, chunkGeom, u, u_t, dsAux, a, t, elemVec);
4634:         PetscFEGeomGetChunk(geom, offset, numCells, &chunkGeom);
4635:         PetscFEIntegrateResidual(ds, key, Nr, chunkGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, dsAux, &a[offset * totDimAux], t, &elemVec[offset * totDim]);
4636:         PetscFEGeomRestoreChunk(geom, offset, numCells, &chunkGeom);
4637:       } else if (id == PETSCFV_CLASSID) {
4638:         PetscFV fv = (PetscFV)obj;

4640:         Ne = numFaces;
4641:         /* Riemann solve over faces (need fields at face centroids) */
4642:         /*   We need to evaluate FE fields at those coordinates */
4643:         PetscFVIntegrateRHSFunction(fv, ds, f, Ne, fgeom, vol, uL, uR, fluxL, fluxR);
4644:       } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
4645:     }
4646:     /* Loop over domain */
4647:     if (useFEM) {
4648:       /* Add elemVec to locX */
4649:       for (c = cS; c < cE; ++c) {
4650:         const PetscInt cell = cells ? cells[c] : c;
4651:         const PetscInt cind = c - cStart;

4653:         if (mesh->printFEM > 1) DMPrintCellVector(cell, name, totDim, &elemVec[cind * totDim]);
4654:         if (ghostLabel) {
4655:           PetscInt ghostVal;

4657:           DMLabelGetValue(ghostLabel, cell, &ghostVal);
4658:           if (ghostVal > 0) continue;
4659:         }
4660:         DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind * totDim], ADD_ALL_VALUES);
4661:       }
4662:     }
4663:     if (useFVM) {
4664:       PetscScalar *fa;
4665:       PetscInt     iface;

4667:       VecGetArray(locF, &fa);
4668:       for (f = 0; f < Nf; ++f) {
4669:         PetscFV      fv;
4670:         PetscObject  obj;
4671:         PetscClassId id;
4672:         PetscInt     foff, pdim;

4674:         PetscDSGetDiscretization(ds, f, &obj);
4675:         PetscDSGetFieldOffset(ds, f, &foff);
4676:         PetscObjectGetClassId(obj, &id);
4677:         if (id != PETSCFV_CLASSID) continue;
4678:         fv = (PetscFV)obj;
4679:         PetscFVGetNumComponents(fv, &pdim);
4680:         /* Accumulate fluxes to cells */
4681:         for (face = fS, iface = 0; face < fE; ++face) {
4682:           const PetscInt *scells;
4683:           PetscScalar    *fL = NULL, *fR = NULL;
4684:           PetscInt        ghost, d, nsupp, nchild;

4686:           DMLabelGetValue(ghostLabel, face, &ghost);
4687:           DMPlexGetSupportSize(dm, face, &nsupp);
4688:           DMPlexGetTreeChildren(dm, face, &nchild, NULL);
4689:           if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
4690:           DMPlexGetSupport(dm, face, &scells);
4691:           DMLabelGetValue(ghostLabel, scells[0], &ghost);
4692:           if (ghost <= 0) DMPlexPointLocalFieldRef(dm, scells[0], f, fa, &fL);
4693:           DMLabelGetValue(ghostLabel, scells[1], &ghost);
4694:           if (ghost <= 0) DMPlexPointLocalFieldRef(dm, scells[1], f, fa, &fR);
4695:           for (d = 0; d < pdim; ++d) {
4696:             if (fL) fL[d] -= fluxL[iface * totDim + foff + d];
4697:             if (fR) fR[d] += fluxR[iface * totDim + foff + d];
4698:           }
4699:           ++iface;
4700:         }
4701:       }
4702:       VecRestoreArray(locF, &fa);
4703:     }
4704:     /* Handle time derivative */
4705:     if (locX_t) {
4706:       PetscScalar *x_t, *fa;

4708:       VecGetArray(locF, &fa);
4709:       VecGetArray(locX_t, &x_t);
4710:       for (f = 0; f < Nf; ++f) {
4711:         PetscFV      fv;
4712:         PetscObject  obj;
4713:         PetscClassId id;
4714:         PetscInt     pdim, d;

4716:         PetscDSGetDiscretization(ds, f, &obj);
4717:         PetscObjectGetClassId(obj, &id);
4718:         if (id != PETSCFV_CLASSID) continue;
4719:         fv = (PetscFV)obj;
4720:         PetscFVGetNumComponents(fv, &pdim);
4721:         for (c = cS; c < cE; ++c) {
4722:           const PetscInt cell = cells ? cells[c] : c;
4723:           PetscScalar   *u_t, *r;

4725:           if (ghostLabel) {
4726:             PetscInt ghostVal;

4728:             DMLabelGetValue(ghostLabel, cell, &ghostVal);
4729:             if (ghostVal > 0) continue;
4730:           }
4731:           DMPlexPointLocalFieldRead(dm, cell, f, x_t, &u_t);
4732:           DMPlexPointLocalFieldRef(dm, cell, f, fa, &r);
4733:           for (d = 0; d < pdim; ++d) r[d] += u_t[d];
4734:         }
4735:       }
4736:       VecRestoreArray(locX_t, &x_t);
4737:       VecRestoreArray(locF, &fa);
4738:     }
4739:     if (useFEM) {
4740:       DMPlexRestoreCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a);
4741:       DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec);
4742:     }
4743:     if (useFVM) {
4744:       DMPlexRestoreFaceFields(dm, fS, fE, locX, locX_t, faceGeometryFVM, cellGeometryFVM, locGrad, &numFaces, &uL, &uR);
4745:       DMPlexRestoreFaceGeometry(dm, fS, fE, faceGeometryFVM, cellGeometryFVM, &numFaces, &fgeom, &vol);
4746:       DMRestoreWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxL);
4747:       DMRestoreWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxR);
4748:       if (dmGrad) DMRestoreLocalVector(dmGrad, &locGrad);
4749:     }
4750:   }
4751:   if (useFEM) ISDestroy(&chunkIS);
4752:   ISRestorePointRange(cellIS, &cStart, &cEnd, &cells);

4754:   if (useFEM) {
4755:     DMPlexComputeBdResidual_Internal(dm, locX, locX_t, t, locF, user);

4757:     if (maxDegree <= 1) {
4758:       DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom);
4759:       PetscQuadratureDestroy(&affineQuad);
4760:     } else {
4761:       for (f = 0; f < Nf; ++f) {
4762:         DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]);
4763:         PetscQuadratureDestroy(&quads[f]);
4764:       }
4765:       PetscFree2(quads, geoms);
4766:     }
4767:   }

4769:   /* FEM */
4770:   /* 1: Get sizes from dm and dmAux */
4771:   /* 2: Get geometric data */
4772:   /* 3: Handle boundary values */
4773:   /* 4: Loop over domain */
4774:   /*   Extract coefficients */
4775:   /* Loop over fields */
4776:   /*   Set tiling for FE*/
4777:   /*   Integrate FE residual to get elemVec */
4778:   /*     Loop over subdomain */
4779:   /*       Loop over quad points */
4780:   /*         Transform coords to real space */
4781:   /*         Evaluate field and aux fields at point */
4782:   /*         Evaluate residual at point */
4783:   /*         Transform residual to real space */
4784:   /*       Add residual to elemVec */
4785:   /* Loop over domain */
4786:   /*   Add elemVec to locX */

4788:   /* FVM */
4789:   /* Get geometric data */
4790:   /* If using gradients */
4791:   /*   Compute gradient data */
4792:   /*   Loop over domain faces */
4793:   /*     Count computational faces */
4794:   /*     Reconstruct cell gradient */
4795:   /*   Loop over domain cells */
4796:   /*     Limit cell gradients */
4797:   /* Handle boundary values */
4798:   /* Loop over domain faces */
4799:   /*   Read out field, centroid, normal, volume for each side of face */
4800:   /* Riemann solve over faces */
4801:   /* Loop over domain faces */
4802:   /*   Accumulate fluxes to cells */
4803:   /* TODO Change printFEM to printDisc here */
4804:   if (mesh->printFEM) {
4805:     Vec          locFbc;
4806:     PetscInt     pStart, pEnd, p, maxDof;
4807:     PetscScalar *zeroes;

4809:     VecDuplicate(locF, &locFbc);
4810:     VecCopy(locF, locFbc);
4811:     PetscSectionGetChart(section, &pStart, &pEnd);
4812:     PetscSectionGetMaxDof(section, &maxDof);
4813:     PetscCalloc1(maxDof, &zeroes);
4814:     for (p = pStart; p < pEnd; p++) VecSetValuesSection(locFbc, section, p, zeroes, INSERT_BC_VALUES);
4815:     PetscFree(zeroes);
4816:     DMPrintLocalVec(dm, name, mesh->printTol, locFbc);
4817:     VecDestroy(&locFbc);
4818:   }
4819:   PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0);
4820:   return 0;
4821: }

4823: /*
4824:   1) Allow multiple kernels for BdResidual for hybrid DS

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

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

4831:   4) Run both kernels for each non-hybrid field with correct dsAux, and then hybrid field as before
4832: */
4833: PetscErrorCode DMPlexComputeResidual_Hybrid_Internal(DM dm, PetscFormKey key[], IS cellIS, PetscReal time, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
4834: {
4835:   DM_Plex        *mesh       = (DM_Plex *)dm->data;
4836:   const char     *name       = "Hybrid Residual";
4837:   DM              dmAux[3]   = {NULL, NULL, NULL};
4838:   DMLabel         ghostLabel = NULL;
4839:   PetscDS         ds         = NULL;
4840:   PetscDS         dsAux[3]   = {NULL, NULL, NULL};
4841:   Vec             locA[3]    = {NULL, NULL, NULL};
4842:   PetscScalar    *a[3]       = {NULL, NULL, NULL};
4843:   PetscSection    section    = NULL;
4844:   DMField         coordField = NULL;
4845:   PetscScalar    *u          = NULL, *u_t;
4846:   PetscScalar    *elemVec;
4847:   IS              chunkIS;
4848:   const PetscInt *cells;
4849:   PetscInt       *faces;
4850:   PetscInt        cStart, cEnd, numCells;
4851:   PetscInt        Nf, f, totDim, totDimAux[3], numChunks, cellChunkSize, chunk;
4852:   PetscInt        maxDegree  = PETSC_MAX_INT;
4853:   PetscQuadrature affineQuad = NULL, *quads = NULL;
4854:   PetscFEGeom    *affineGeom = NULL, **geoms = NULL;

4856:   if ((key[0].label == key[1].label) && (key[0].value == key[1].value) && (key[0].part == key[1].part)) {
4857:     const char *name;
4858:     PetscObjectGetName((PetscObject)key[0].label, &name);
4859:     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);
4860:   }
4861:   PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0);
4862:   /* TODO The places where we have to use isFE are probably the member functions for the PetscDisc class */
4863:   /* FEM */
4864:   ISGetLocalSize(cellIS, &numCells);
4865:   ISGetPointRange(cellIS, &cStart, &cEnd, &cells);
4866:   /* 1: Get sizes from dm and dmAux */
4867:   DMGetSection(dm, &section);
4868:   DMGetLabel(dm, "ghost", &ghostLabel);
4869:   DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds);
4870:   PetscDSGetNumFields(ds, &Nf);
4871:   PetscDSGetTotalDimension(ds, &totDim);
4872:   DMGetAuxiliaryVec(dm, key[2].label, key[2].value, key[2].part, &locA[2]);
4873:   if (locA[2]) {
4874:     const PetscInt cellStart = cells ? cells[cStart] : cStart;

4876:     VecGetDM(locA[2], &dmAux[2]);
4877:     DMGetCellDS(dmAux[2], cellStart, &dsAux[2]);
4878:     PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]);
4879:     {
4880:       const PetscInt *cone;
4881:       PetscInt        c;

4883:       DMPlexGetCone(dm, cellStart, &cone);
4884:       for (c = 0; c < 2; ++c) {
4885:         const PetscInt *support;
4886:         PetscInt        ssize, s;

4888:         DMPlexGetSupport(dm, cone[c], &support);
4889:         DMPlexGetSupportSize(dm, cone[c], &ssize);
4891:         if (support[0] == cellStart) s = 1;
4892:         else if (support[1] == cellStart) s = 0;
4893:         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
4894:         DMGetAuxiliaryVec(dm, key[c].label, key[c].value, key[c].part, &locA[c]);
4896:         if (locA[c]) VecGetDM(locA[c], &dmAux[c]);
4897:         else dmAux[c] = dmAux[2];
4898:         DMGetCellDS(dmAux[c], support[s], &dsAux[c]);
4899:         PetscDSGetTotalDimension(dsAux[c], &totDimAux[c]);
4900:       }
4901:     }
4902:   }
4903:   /* 2: Setup geometric data */
4904:   DMGetCoordinateField(dm, &coordField);
4905:   DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree);
4906:   if (maxDegree > 1) {
4907:     PetscCalloc2(Nf, &quads, Nf, &geoms);
4908:     for (f = 0; f < Nf; ++f) {
4909:       PetscFE fe;

4911:       PetscDSGetDiscretization(ds, f, (PetscObject *)&fe);
4912:       if (fe) {
4913:         PetscFEGetQuadrature(fe, &quads[f]);
4914:         PetscObjectReference((PetscObject)quads[f]);
4915:       }
4916:     }
4917:   }
4918:   /* Loop over chunks */
4919:   cellChunkSize = numCells;
4920:   numChunks     = !numCells ? 0 : PetscCeilReal(((PetscReal)numCells) / cellChunkSize);
4921:   PetscCalloc1(1 * cellChunkSize, &faces);
4922:   ISCreateGeneral(PETSC_COMM_SELF, 1 * cellChunkSize, faces, PETSC_USE_POINTER, &chunkIS);
4923:   /* Extract field coefficients */
4924:   /* NOTE This needs the end cap faces to have identical orientations */
4925:   DMPlexGetCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]);
4926:   DMPlexGetHybridAuxFields(dm, dmAux, dsAux, cellIS, locA, a);
4927:   DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVec);
4928:   for (chunk = 0; chunk < numChunks; ++chunk) {
4929:     PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;

4931:     PetscMemzero(elemVec, cellChunkSize * totDim * sizeof(PetscScalar));
4932:     /* Get faces */
4933:     for (c = cS; c < cE; ++c) {
4934:       const PetscInt  cell = cells ? cells[c] : c;
4935:       const PetscInt *cone;
4936:       DMPlexGetCone(dm, cell, &cone);
4937:       faces[0 * cellChunkSize + (c - cS)] = cone[0];
4938:       /*faces[1*cellChunkSize+(c-cS)] = cone[1];*/
4939:     }
4940:     ISGeneralSetIndices(chunkIS, 1 * cellChunkSize, faces, PETSC_USE_POINTER);
4941:     /* Get geometric data */
4942:     if (maxDegree <= 1) {
4943:       if (!affineQuad) DMFieldCreateDefaultQuadrature(coordField, chunkIS, &affineQuad);
4944:       if (affineQuad) DMSNESGetFEGeom(coordField, chunkIS, affineQuad, PETSC_TRUE, &affineGeom);
4945:     } else {
4946:       for (f = 0; f < Nf; ++f) {
4947:         if (quads[f]) DMSNESGetFEGeom(coordField, chunkIS, quads[f], PETSC_TRUE, &geoms[f]);
4948:       }
4949:     }
4950:     /* Loop over fields */
4951:     for (f = 0; f < Nf; ++f) {
4952:       PetscFE         fe;
4953:       PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[f];
4954:       PetscFEGeom    *chunkGeom = NULL, *remGeom = NULL;
4955:       PetscQuadrature quad = affineQuad ? affineQuad : quads[f];
4956:       PetscInt        numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb;
4957:       PetscBool       isCohesiveField;

4959:       PetscDSGetDiscretization(ds, f, (PetscObject *)&fe);
4960:       if (!fe) continue;
4961:       PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches);
4962:       PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL);
4963:       PetscFEGetDimension(fe, &Nb);
4964:       blockSize = Nb;
4965:       batchSize = numBlocks * blockSize;
4966:       PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches);
4967:       numChunks = numCells / (numBatches * batchSize);
4968:       Ne        = numChunks * numBatches * batchSize;
4969:       Nr        = numCells % (numBatches * batchSize);
4970:       offset    = numCells - Nr;
4971:       PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom);
4972:       PetscFEGeomGetChunk(geom, offset, numCells, &remGeom);
4973:       PetscDSGetCohesive(ds, f, &isCohesiveField);
4974:       chunkGeom->isCohesive = remGeom->isCohesive = PETSC_TRUE;
4975:       key[0].field                                = f;
4976:       key[1].field                                = f;
4977:       key[2].field                                = f;
4978:       PetscFEIntegrateHybridResidual(ds, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, elemVec);
4979:       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]);
4980:       PetscFEIntegrateHybridResidual(ds, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, elemVec);
4981:       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]);
4982:       PetscFEIntegrateHybridResidual(ds, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, elemVec);
4983:       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]);
4984:       PetscFEGeomRestoreChunk(geom, offset, numCells, &remGeom);
4985:       PetscFEGeomRestoreChunk(geom, 0, offset, &chunkGeom);
4986:     }
4987:     /* Add elemVec to locX */
4988:     for (c = cS; c < cE; ++c) {
4989:       const PetscInt cell = cells ? cells[c] : c;
4990:       const PetscInt cind = c - cStart;

4992:       if (mesh->printFEM > 1) DMPrintCellVector(cell, name, totDim, &elemVec[cind * totDim]);
4993:       if (ghostLabel) {
4994:         PetscInt ghostVal;

4996:         DMLabelGetValue(ghostLabel, cell, &ghostVal);
4997:         if (ghostVal > 0) continue;
4998:       }
4999:       DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind * totDim], ADD_ALL_VALUES);
5000:     }
5001:   }
5002:   DMPlexRestoreCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]);
5003:   DMPlexRestoreHybridAuxFields(dmAux, dsAux, cellIS, locA, a);
5004:   DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec);
5005:   PetscFree(faces);
5006:   ISDestroy(&chunkIS);
5007:   ISRestorePointRange(cellIS, &cStart, &cEnd, &cells);
5008:   if (maxDegree <= 1) {
5009:     DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom);
5010:     PetscQuadratureDestroy(&affineQuad);
5011:   } else {
5012:     for (f = 0; f < Nf; ++f) {
5013:       if (geoms) DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]);
5014:       if (quads) PetscQuadratureDestroy(&quads[f]);
5015:     }
5016:     PetscFree2(quads, geoms);
5017:   }
5018:   PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0);
5019:   return 0;
5020: }

5022: 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)
5023: {
5024:   DM_Plex        *mesh = (DM_Plex *)dm->data;
5025:   DM              plex = NULL, plexA = NULL, tdm;
5026:   DMEnclosureType encAux;
5027:   PetscDS         prob, probAux       = NULL;
5028:   PetscSection    section, sectionAux = NULL;
5029:   PetscSection    globalSection;
5030:   Vec             locA = NULL, tv;
5031:   PetscScalar    *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL;
5032:   PetscInt        v;
5033:   PetscInt        Nf, totDim, totDimAux = 0;
5034:   PetscBool       transform;

5036:   DMConvert(dm, DMPLEX, &plex);
5037:   DMHasBasisTransform(dm, &transform);
5038:   DMGetBasisTransformDM_Internal(dm, &tdm);
5039:   DMGetBasisTransformVec_Internal(dm, &tv);
5040:   DMGetLocalSection(dm, &section);
5041:   DMGetDS(dm, &prob);
5042:   PetscDSGetNumFields(prob, &Nf);
5043:   PetscDSGetTotalDimension(prob, &totDim);
5044:   DMGetAuxiliaryVec(dm, label, values[0], 0, &locA);
5045:   if (locA) {
5046:     DM dmAux;

5048:     VecGetDM(locA, &dmAux);
5049:     DMGetEnclosureRelation(dmAux, dm, &encAux);
5050:     DMConvert(dmAux, DMPLEX, &plexA);
5051:     DMGetDS(plexA, &probAux);
5052:     PetscDSGetTotalDimension(probAux, &totDimAux);
5053:     DMGetLocalSection(plexA, &sectionAux);
5054:   }

5056:   DMGetGlobalSection(dm, &globalSection);
5057:   for (v = 0; v < numValues; ++v) {
5058:     PetscFEGeom    *fgeom;
5059:     PetscInt        maxDegree;
5060:     PetscQuadrature qGeom = NULL;
5061:     IS              pointIS;
5062:     const PetscInt *points;
5063:     PetscFormKey    key;
5064:     PetscInt        numFaces, face, Nq;

5066:     key.label = label;
5067:     key.value = values[v];
5068:     key.part  = 0;
5069:     DMLabelGetStratumIS(label, values[v], &pointIS);
5070:     if (!pointIS) continue; /* No points with that id on this process */
5071:     {
5072:       IS isectIS;

5074:       /* TODO: Special cases of ISIntersect where it is quick to check a prior if one is a superset of the other */
5075:       ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS);
5076:       ISDestroy(&pointIS);
5077:       pointIS = isectIS;
5078:     }
5079:     ISGetLocalSize(pointIS, &numFaces);
5080:     ISGetIndices(pointIS, &points);
5081:     PetscMalloc4(numFaces * totDim, &u, locX_t ? numFaces * totDim : 0, &u_t, numFaces * totDim * totDim, &elemMat, locA ? numFaces * totDimAux : 0, &a);
5082:     DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree);
5083:     if (maxDegree <= 1) DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom);
5084:     if (!qGeom) {
5085:       PetscFE fe;

5087:       PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe);
5088:       PetscFEGetFaceQuadrature(fe, &qGeom);
5089:       PetscObjectReference((PetscObject)qGeom);
5090:     }
5091:     PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL);
5092:     DMSNESGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom);
5093:     for (face = 0; face < numFaces; ++face) {
5094:       const PetscInt point = points[face], *support;
5095:       PetscScalar   *x     = NULL;
5096:       PetscInt       i;

5098:       DMPlexGetSupport(dm, point, &support);
5099:       DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x);
5100:       for (i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
5101:       DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x);
5102:       if (locX_t) {
5103:         DMPlexVecGetClosure(plex, section, locX_t, support[0], NULL, &x);
5104:         for (i = 0; i < totDim; ++i) u_t[face * totDim + i] = x[i];
5105:         DMPlexVecRestoreClosure(plex, section, locX_t, support[0], NULL, &x);
5106:       }
5107:       if (locA) {
5108:         PetscInt subp;
5109:         DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp);
5110:         DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x);
5111:         for (i = 0; i < totDimAux; ++i) a[face * totDimAux + i] = x[i];
5112:         DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x);
5113:       }
5114:     }
5115:     PetscArrayzero(elemMat, numFaces * totDim * totDim);
5116:     {
5117:       PetscFE  fe;
5118:       PetscInt Nb;
5119:       /* Conforming batches */
5120:       PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
5121:       /* Remainder */
5122:       PetscFEGeom *chunkGeom = NULL;
5123:       PetscInt     fieldJ, Nr, offset;

5125:       PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe);
5126:       PetscFEGetDimension(fe, &Nb);
5127:       PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches);
5128:       blockSize = Nb;
5129:       batchSize = numBlocks * blockSize;
5130:       PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches);
5131:       numChunks = numFaces / (numBatches * batchSize);
5132:       Ne        = numChunks * numBatches * batchSize;
5133:       Nr        = numFaces % (numBatches * batchSize);
5134:       offset    = numFaces - Nr;
5135:       PetscFEGeomGetChunk(fgeom, 0, offset, &chunkGeom);
5136:       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5137:         key.field = fieldI * Nf + fieldJ;
5138:         PetscFEIntegrateBdJacobian(prob, wf, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat);
5139:       }
5140:       PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom);
5141:       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5142:         key.field = fieldI * Nf + fieldJ;
5143:         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]);
5144:       }
5145:       PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom);
5146:     }
5147:     for (face = 0; face < numFaces; ++face) {
5148:       const PetscInt point = points[face], *support;

5150:       /* Transform to global basis before insertion in Jacobian */
5151:       DMPlexGetSupport(plex, point, &support);
5152:       if (transform) DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, support[0], PETSC_TRUE, totDim, &elemMat[face * totDim * totDim]);
5153:       if (mesh->printFEM > 1) DMPrintCellMatrix(point, "BdJacobian", totDim, totDim, &elemMat[face * totDim * totDim]);
5154:       DMPlexMatSetClosure(plex, section, globalSection, JacP, support[0], &elemMat[face * totDim * totDim], ADD_VALUES);
5155:     }
5156:     DMSNESRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom);
5157:     PetscQuadratureDestroy(&qGeom);
5158:     ISRestoreIndices(pointIS, &points);
5159:     ISDestroy(&pointIS);
5160:     PetscFree4(u, u_t, elemMat, a);
5161:   }
5162:   if (plex) DMDestroy(&plex);
5163:   if (plexA) DMDestroy(&plexA);
5164:   return 0;
5165: }

5167: 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)
5168: {
5169:   DMField  coordField;
5170:   DMLabel  depthLabel;
5171:   IS       facetIS;
5172:   PetscInt dim;

5174:   DMGetDimension(dm, &dim);
5175:   DMPlexGetDepthLabel(dm, &depthLabel);
5176:   DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS);
5177:   DMGetCoordinateField(dm, &coordField);
5178:   DMPlexComputeBdJacobian_Single_Internal(dm, t, wf, label, numValues, values, field, locX, locX_t, X_tShift, Jac, JacP, coordField, facetIS);
5179:   ISDestroy(&facetIS);
5180:   return 0;
5181: }

5183: PetscErrorCode DMPlexComputeBdJacobian_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, PetscReal X_tShift, Mat Jac, Mat JacP, void *user)
5184: {
5185:   PetscDS  prob;
5186:   PetscInt dim, numBd, bd;
5187:   DMLabel  depthLabel;
5188:   DMField  coordField = NULL;
5189:   IS       facetIS;

5191:   DMGetDS(dm, &prob);
5192:   DMPlexGetDepthLabel(dm, &depthLabel);
5193:   DMGetDimension(dm, &dim);
5194:   DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS);
5195:   PetscDSGetNumBoundary(prob, &numBd);
5196:   DMGetCoordinateField(dm, &coordField);
5197:   for (bd = 0; bd < numBd; ++bd) {
5198:     PetscWeakForm           wf;
5199:     DMBoundaryConditionType type;
5200:     DMLabel                 label;
5201:     const PetscInt         *values;
5202:     PetscInt                fieldI, numValues;
5203:     PetscObject             obj;
5204:     PetscClassId            id;

5206:     PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &fieldI, NULL, NULL, NULL, NULL, NULL);
5207:     if (type & DM_BC_ESSENTIAL) continue;
5208:     PetscDSGetDiscretization(prob, fieldI, &obj);
5209:     PetscObjectGetClassId(obj, &id);
5210:     if (id != PETSCFE_CLASSID) continue;
5211:     DMPlexComputeBdJacobian_Single_Internal(dm, t, wf, label, numValues, values, fieldI, locX, locX_t, X_tShift, Jac, JacP, coordField, facetIS);
5212:   }
5213:   ISDestroy(&facetIS);
5214:   return 0;
5215: }

5217: 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)
5218: {
5219:   DM_Plex        *mesh  = (DM_Plex *)dm->data;
5220:   const char     *name  = "Jacobian";
5221:   DM              dmAux = NULL, plex, tdm;
5222:   DMEnclosureType encAux;
5223:   Vec             A, tv;
5224:   DMField         coordField;
5225:   PetscDS         prob, probAux = NULL;
5226:   PetscSection    section, globalSection, sectionAux;
5227:   PetscScalar    *elemMat, *elemMatP, *elemMatD, *u, *u_t, *a = NULL;
5228:   const PetscInt *cells;
5229:   PetscInt        Nf, fieldI, fieldJ;
5230:   PetscInt        totDim, totDimAux = 0, cStart, cEnd, numCells, c;
5231:   PetscBool       hasJac = PETSC_FALSE, hasPrec = PETSC_FALSE, hasDyn, hasFV = PETSC_FALSE, transform;

5233:   if (!cellIS) goto end;
5234:   PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0);
5235:   ISGetLocalSize(cellIS, &numCells);
5236:   ISGetPointRange(cellIS, &cStart, &cEnd, &cells);
5237:   DMHasBasisTransform(dm, &transform);
5238:   DMGetBasisTransformDM_Internal(dm, &tdm);
5239:   DMGetBasisTransformVec_Internal(dm, &tv);
5240:   DMGetLocalSection(dm, &section);
5241:   DMGetGlobalSection(dm, &globalSection);
5242:   DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob);
5243:   PetscDSGetNumFields(prob, &Nf);
5244:   PetscDSGetTotalDimension(prob, &totDim);
5245:   PetscDSHasJacobian(prob, &hasJac);
5246:   PetscDSHasJacobianPreconditioner(prob, &hasPrec);
5247:   /* user passed in the same matrix, avoid double contributions and
5248:      only assemble the Jacobian */
5249:   if (hasJac && Jac == JacP) hasPrec = PETSC_FALSE;
5250:   PetscDSHasDynamicJacobian(prob, &hasDyn);
5251:   hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
5252:   DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &A);
5253:   if (A) {
5254:     VecGetDM(A, &dmAux);
5255:     DMGetEnclosureRelation(dmAux, dm, &encAux);
5256:     DMConvert(dmAux, DMPLEX, &plex);
5257:     DMGetLocalSection(plex, &sectionAux);
5258:     DMGetDS(dmAux, &probAux);
5259:     PetscDSGetTotalDimension(probAux, &totDimAux);
5260:   }
5261:   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);
5262:   if (dmAux) PetscMalloc1(numCells * totDimAux, &a);
5263:   DMGetCoordinateField(dm, &coordField);
5264:   for (c = cStart; c < cEnd; ++c) {
5265:     const PetscInt cell = cells ? cells[c] : c;
5266:     const PetscInt cind = c - cStart;
5267:     PetscScalar   *x = NULL, *x_t = NULL;
5268:     PetscInt       i;

5270:     DMPlexVecGetClosure(dm, section, X, cell, NULL, &x);
5271:     for (i = 0; i < totDim; ++i) u[cind * totDim + i] = x[i];
5272:     DMPlexVecRestoreClosure(dm, section, X, cell, NULL, &x);
5273:     if (X_t) {
5274:       DMPlexVecGetClosure(dm, section, X_t, cell, NULL, &x_t);
5275:       for (i = 0; i < totDim; ++i) u_t[cind * totDim + i] = x_t[i];
5276:       DMPlexVecRestoreClosure(dm, section, X_t, cell, NULL, &x_t);
5277:     }
5278:     if (dmAux) {
5279:       PetscInt subcell;
5280:       DMGetEnclosurePoint(dmAux, dm, encAux, cell, &subcell);
5281:       DMPlexVecGetClosure(plex, sectionAux, A, subcell, NULL, &x);
5282:       for (i = 0; i < totDimAux; ++i) a[cind * totDimAux + i] = x[i];
5283:       DMPlexVecRestoreClosure(plex, sectionAux, A, subcell, NULL, &x);
5284:     }
5285:   }
5286:   if (hasJac) PetscArrayzero(elemMat, numCells * totDim * totDim);
5287:   if (hasPrec) PetscArrayzero(elemMatP, numCells * totDim * totDim);
5288:   if (hasDyn) PetscArrayzero(elemMatD, numCells * totDim * totDim);
5289:   for (fieldI = 0; fieldI < Nf; ++fieldI) {
5290:     PetscClassId    id;
5291:     PetscFE         fe;
5292:     PetscQuadrature qGeom = NULL;
5293:     PetscInt        Nb;
5294:     /* Conforming batches */
5295:     PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
5296:     /* Remainder */
5297:     PetscInt     Nr, offset, Nq;
5298:     PetscInt     maxDegree;
5299:     PetscFEGeom *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL;

5301:     PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe);
5302:     PetscObjectGetClassId((PetscObject)fe, &id);
5303:     if (id == PETSCFV_CLASSID) {
5304:       hasFV = PETSC_TRUE;
5305:       continue;
5306:     }
5307:     PetscFEGetDimension(fe, &Nb);
5308:     PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches);
5309:     DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree);
5310:     if (maxDegree <= 1) DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom);
5311:     if (!qGeom) {
5312:       PetscFEGetQuadrature(fe, &qGeom);
5313:       PetscObjectReference((PetscObject)qGeom);
5314:     }
5315:     PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL);
5316:     DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM);
5317:     blockSize = Nb;
5318:     batchSize = numBlocks * blockSize;
5319:     PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches);
5320:     numChunks = numCells / (numBatches * batchSize);
5321:     Ne        = numChunks * numBatches * batchSize;
5322:     Nr        = numCells % (numBatches * batchSize);
5323:     offset    = numCells - Nr;
5324:     PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom);
5325:     PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &remGeom);
5326:     for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5327:       key.field = fieldI * Nf + fieldJ;
5328:       if (hasJac) {
5329:         PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat);
5330:         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]);
5331:       }
5332:       if (hasPrec) {
5333:         PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatP);
5334:         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]);
5335:       }
5336:       if (hasDyn) {
5337:         PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatD);
5338:         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]);
5339:       }
5340:     }
5341:     PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &remGeom);
5342:     PetscFEGeomRestoreChunk(cgeomFEM, 0, offset, &chunkGeom);
5343:     DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM);
5344:     PetscQuadratureDestroy(&qGeom);
5345:   }
5346:   /*   Add contribution from X_t */
5347:   if (hasDyn) {
5348:     for (c = 0; c < numCells * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
5349:   }
5350:   if (hasFV) {
5351:     PetscClassId id;
5352:     PetscFV      fv;
5353:     PetscInt     offsetI, NcI, NbI = 1, fc, f;

5355:     for (fieldI = 0; fieldI < Nf; ++fieldI) {
5356:       PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fv);
5357:       PetscDSGetFieldOffset(prob, fieldI, &offsetI);
5358:       PetscObjectGetClassId((PetscObject)fv, &id);
5359:       if (id != PETSCFV_CLASSID) continue;
5360:       /* Put in the identity */
5361:       PetscFVGetNumComponents(fv, &NcI);
5362:       for (c = cStart; c < cEnd; ++c) {
5363:         const PetscInt cind    = c - cStart;
5364:         const PetscInt eOffset = cind * totDim * totDim;
5365:         for (fc = 0; fc < NcI; ++fc) {
5366:           for (f = 0; f < NbI; ++f) {
5367:             const PetscInt i = offsetI + f * NcI + fc;
5368:             if (hasPrec) {
5369:               if (hasJac) elemMat[eOffset + i * totDim + i] = 1.0;
5370:               elemMatP[eOffset + i * totDim + i] = 1.0;
5371:             } else {
5372:               elemMat[eOffset + i * totDim + i] = 1.0;
5373:             }
5374:           }
5375:         }
5376:       }
5377:     }
5378:     /* No allocated space for FV stuff, so ignore the zero entries */
5379:     MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE);
5380:   }
5381:   /* Insert values into matrix */
5382:   for (c = cStart; c < cEnd; ++c) {
5383:     const PetscInt cell = cells ? cells[c] : c;
5384:     const PetscInt cind = c - cStart;

5386:     /* Transform to global basis before insertion in Jacobian */
5387:     if (transform) DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, cell, PETSC_TRUE, totDim, &elemMat[cind * totDim * totDim]);
5388:     if (hasPrec) {
5389:       if (hasJac) {
5390:         if (mesh->printFEM > 1) DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim]);
5391:         DMPlexMatSetClosure(dm, section, globalSection, Jac, cell, &elemMat[cind * totDim * totDim], ADD_VALUES);
5392:       }
5393:       if (mesh->printFEM > 1) DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatP[cind * totDim * totDim]);
5394:       DMPlexMatSetClosure(dm, section, globalSection, JacP, cell, &elemMatP[cind * totDim * totDim], ADD_VALUES);
5395:     } else {
5396:       if (hasJac) {
5397:         if (mesh->printFEM > 1) DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim]);
5398:         DMPlexMatSetClosure(dm, section, globalSection, JacP, cell, &elemMat[cind * totDim * totDim], ADD_VALUES);
5399:       }
5400:     }
5401:   }
5402:   ISRestorePointRange(cellIS, &cStart, &cEnd, &cells);
5403:   if (hasFV) MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE);
5404:   PetscFree5(u, u_t, elemMat, elemMatP, elemMatD);
5405:   if (dmAux) {
5406:     PetscFree(a);
5407:     DMDestroy(&plex);
5408:   }
5409:   /* Compute boundary integrals */
5410:   DMPlexComputeBdJacobian_Internal(dm, X, X_t, t, X_tShift, Jac, JacP, user);
5411:   /* Assemble matrix */
5412: end : {
5413:   PetscBool assOp = hasJac && hasPrec ? PETSC_TRUE : PETSC_FALSE, gassOp;

5415:   MPI_Allreduce(&assOp, &gassOp, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm));
5416:   if (hasJac && hasPrec) {
5417:     MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY);
5418:     MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY);
5419:   }
5420: }
5421:   MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY);
5422:   MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY);
5423:   PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0);
5424:   return 0;
5425: }

5427: 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)
5428: {
5429:   DM_Plex        *mesh          = (DM_Plex *)dm->data;
5430:   const char     *name          = "Hybrid Jacobian";
5431:   DM              dmAux[3]      = {NULL, NULL, NULL};
5432:   DMLabel         ghostLabel    = NULL;
5433:   DM              plex          = NULL;
5434:   DM              plexA         = NULL;
5435:   PetscDS         ds            = NULL;
5436:   PetscDS         dsAux[3]      = {NULL, NULL, NULL};
5437:   Vec             locA[3]       = {NULL, NULL, NULL};
5438:   PetscSection    section       = NULL;
5439:   PetscSection    sectionAux[3] = {NULL, NULL, NULL};
5440:   DMField         coordField    = NULL;
5441:   PetscScalar    *u             = NULL, *u_t, *a[3];
5442:   PetscScalar    *elemMat, *elemMatP;
5443:   PetscSection    globalSection;
5444:   IS              chunkIS;
5445:   const PetscInt *cells;
5446:   PetscInt       *faces;
5447:   PetscInt        cStart, cEnd, numCells;
5448:   PetscInt        Nf, fieldI, fieldJ, totDim, totDimAux[3], numChunks, cellChunkSize, chunk;
5449:   PetscInt        maxDegree  = PETSC_MAX_INT;
5450:   PetscQuadrature affineQuad = NULL, *quads = NULL;
5451:   PetscFEGeom    *affineGeom = NULL, **geoms = NULL;
5452:   PetscBool       hasBdJac, hasBdPrec;

5454:   if ((key[0].label == key[1].label) && (key[0].value == key[1].value) && (key[0].part == key[1].part)) {
5455:     const char *name;
5456:     PetscObjectGetName((PetscObject)key[0].label, &name);
5457:     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);
5458:   }
5459:   PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0);
5460:   ISGetLocalSize(cellIS, &numCells);
5461:   ISGetPointRange(cellIS, &cStart, &cEnd, &cells);
5462:   DMConvert(dm, DMPLEX, &plex);
5463:   DMGetSection(dm, &section);
5464:   DMGetGlobalSection(dm, &globalSection);
5465:   DMGetLabel(dm, "ghost", &ghostLabel);
5466:   DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds);
5467:   PetscDSGetNumFields(ds, &Nf);
5468:   PetscDSGetTotalDimension(ds, &totDim);
5469:   PetscDSHasBdJacobian(ds, &hasBdJac);
5470:   PetscDSHasBdJacobianPreconditioner(ds, &hasBdPrec);
5471:   DMGetAuxiliaryVec(dm, key[2].label, key[2].value, key[2].part, &locA[2]);
5472:   if (locA[2]) {
5473:     const PetscInt cellStart = cells ? cells[cStart] : cStart;

5475:     VecGetDM(locA[2], &dmAux[2]);
5476:     DMConvert(dmAux[2], DMPLEX, &plexA);
5477:     DMGetSection(dmAux[2], &sectionAux[2]);
5478:     DMGetCellDS(dmAux[2], cellStart, &dsAux[2]);
5479:     PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]);
5480:     {
5481:       const PetscInt *cone;
5482:       PetscInt        c;

5484:       DMPlexGetCone(dm, cellStart, &cone);
5485:       for (c = 0; c < 2; ++c) {
5486:         const PetscInt *support;
5487:         PetscInt        ssize, s;

5489:         DMPlexGetSupport(dm, cone[c], &support);
5490:         DMPlexGetSupportSize(dm, cone[c], &ssize);
5492:         if (support[0] == cellStart) s = 1;
5493:         else if (support[1] == cellStart) s = 0;
5494:         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
5495:         DMGetAuxiliaryVec(dm, key[c].label, key[c].value, key[c].part, &locA[c]);
5496:         if (locA[c]) VecGetDM(locA[c], &dmAux[c]);
5497:         else dmAux[c] = dmAux[2];
5498:         DMGetCellDS(dmAux[c], support[s], &dsAux[c]);
5499:         PetscDSGetTotalDimension(dsAux[c], &totDimAux[c]);
5500:       }
5501:     }
5502:   }
5503:   DMGetCoordinateField(dm, &coordField);
5504:   DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree);
5505:   if (maxDegree > 1) {
5506:     PetscInt f;
5507:     PetscCalloc2(Nf, &quads, Nf, &geoms);
5508:     for (f = 0; f < Nf; ++f) {
5509:       PetscFE fe;

5511:       PetscDSGetDiscretization(ds, f, (PetscObject *)&fe);
5512:       if (fe) {
5513:         PetscFEGetQuadrature(fe, &quads[f]);
5514:         PetscObjectReference((PetscObject)quads[f]);
5515:       }
5516:     }
5517:   }
5518:   cellChunkSize = numCells;
5519:   numChunks     = !numCells ? 0 : PetscCeilReal(((PetscReal)numCells) / cellChunkSize);
5520:   PetscCalloc1(1 * cellChunkSize, &faces);
5521:   ISCreateGeneral(PETSC_COMM_SELF, 1 * cellChunkSize, faces, PETSC_USE_POINTER, &chunkIS);
5522:   DMPlexGetCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]);
5523:   DMPlexGetHybridAuxFields(dm, dmAux, dsAux, cellIS, locA, a);
5524:   DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMat);
5525:   DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatP);
5526:   for (chunk = 0; chunk < numChunks; ++chunk) {
5527:     PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;

5529:     if (hasBdJac) PetscMemzero(elemMat, numCells * totDim * totDim * sizeof(PetscScalar));
5530:     if (hasBdPrec) PetscMemzero(elemMatP, numCells * totDim * totDim * sizeof(PetscScalar));
5531:     /* Get faces */
5532:     for (c = cS; c < cE; ++c) {
5533:       const PetscInt  cell = cells ? cells[c] : c;
5534:       const PetscInt *cone;
5535:       DMPlexGetCone(plex, cell, &cone);
5536:       faces[0 * cellChunkSize + (c - cS)] = cone[0];
5537:       /*faces[2*cellChunkSize+(c-cS)] = cone[1];*/
5538:     }
5539:     ISGeneralSetIndices(chunkIS, 1 * cellChunkSize, faces, PETSC_USE_POINTER);
5540:     if (maxDegree <= 1) {
5541:       if (!affineQuad) DMFieldCreateDefaultQuadrature(coordField, chunkIS, &affineQuad);
5542:       if (affineQuad) DMSNESGetFEGeom(coordField, chunkIS, affineQuad, PETSC_TRUE, &affineGeom);
5543:     } else {
5544:       PetscInt f;
5545:       for (f = 0; f < Nf; ++f) {
5546:         if (quads[f]) DMSNESGetFEGeom(coordField, chunkIS, quads[f], PETSC_TRUE, &geoms[f]);
5547:       }
5548:     }

5550:     for (fieldI = 0; fieldI < Nf; ++fieldI) {
5551:       PetscFE         feI;
5552:       PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[fieldI];
5553:       PetscFEGeom    *chunkGeom = NULL, *remGeom = NULL;
5554:       PetscQuadrature quad = affineQuad ? affineQuad : quads[fieldI];
5555:       PetscInt        numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb;
5556:       PetscBool       isCohesiveField;

5558:       PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&feI);
5559:       if (!feI) continue;
5560:       PetscFEGetTileSizes(feI, NULL, &numBlocks, NULL, &numBatches);
5561:       PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL);
5562:       PetscFEGetDimension(feI, &Nb);
5563:       blockSize = Nb;
5564:       batchSize = numBlocks * blockSize;
5565:       PetscFESetTileSizes(feI, blockSize, numBlocks, batchSize, numBatches);
5566:       numChunks = numCells / (numBatches * batchSize);
5567:       Ne        = numChunks * numBatches * batchSize;
5568:       Nr        = numCells % (numBatches * batchSize);
5569:       offset    = numCells - Nr;
5570:       PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom);
5571:       PetscFEGeomGetChunk(geom, offset, numCells, &remGeom);
5572:       PetscDSGetCohesive(ds, fieldI, &isCohesiveField);
5573:       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5574:         PetscFE feJ;

5576:         PetscDSGetDiscretization(ds, fieldJ, (PetscObject *)&feJ);
5577:         if (!feJ) continue;
5578:         key[0].field = fieldI * Nf + fieldJ;
5579:         key[1].field = fieldI * Nf + fieldJ;
5580:         key[2].field = fieldI * Nf + fieldJ;
5581:         if (hasBdJac) {
5582:           PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, X_tShift, elemMat);
5583:           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]);
5584:           PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, X_tShift, elemMat);
5585:           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]);
5586:         }
5587:         if (hasBdPrec) {
5588:           PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN_PRE, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, X_tShift, elemMatP);
5589:           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]);
5590:           PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN_PRE, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, X_tShift, elemMatP);
5591:           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]);
5592:         }
5593:         if (hasBdJac) {
5594:           PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, X_tShift, elemMat);
5595:           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]);
5596:         }
5597:         if (hasBdPrec) {
5598:           PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN_PRE, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, X_tShift, elemMatP);
5599:           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]);
5600:         }
5601:       }
5602:       PetscFEGeomRestoreChunk(geom, offset, numCells, &remGeom);
5603:       PetscFEGeomRestoreChunk(geom, 0, offset, &chunkGeom);
5604:     }
5605:     /* Insert values into matrix */
5606:     for (c = cS; c < cE; ++c) {
5607:       const PetscInt cell = cells ? cells[c] : c;
5608:       const PetscInt cind = c - cS;

5610:       if (hasBdPrec) {
5611:         if (hasBdJac) {
5612:           if (mesh->printFEM > 1) DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim]);
5613:           DMPlexMatSetClosure(plex, section, globalSection, Jac, cell, &elemMat[cind * totDim * totDim], ADD_VALUES);
5614:         }
5615:         if (mesh->printFEM > 1) DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatP[cind * totDim * totDim]);
5616:         DMPlexMatSetClosure(plex, section, globalSection, JacP, cell, &elemMatP[cind * totDim * totDim], ADD_VALUES);
5617:       } else if (hasBdJac) {
5618:         if (mesh->printFEM > 1) DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim]);
5619:         DMPlexMatSetClosure(plex, section, globalSection, JacP, cell, &elemMat[cind * totDim * totDim], ADD_VALUES);
5620:       }
5621:     }
5622:   }
5623:   DMPlexRestoreCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]);
5624:   DMPlexRestoreHybridAuxFields(dmAux, dsAux, cellIS, locA, a);
5625:   DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMat);
5626:   DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatP);
5627:   PetscFree(faces);
5628:   ISDestroy(&chunkIS);
5629:   ISRestorePointRange(cellIS, &cStart, &cEnd, &cells);
5630:   if (maxDegree <= 1) {
5631:     DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom);
5632:     PetscQuadratureDestroy(&affineQuad);
5633:   } else {
5634:     PetscInt f;
5635:     for (f = 0; f < Nf; ++f) {
5636:       if (geoms) DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]);
5637:       if (quads) PetscQuadratureDestroy(&quads[f]);
5638:     }
5639:     PetscFree2(quads, geoms);
5640:   }
5641:   if (dmAux[2]) DMDestroy(&plexA);
5642:   DMDestroy(&plex);
5643:   PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0);
5644:   return 0;
5645: }

5647: /*
5648:   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.

5650:   Input Parameters:
5651: + dm     - The mesh
5652: . key    - The PetscWeakFormKey indcating where integration should happen
5653: . cellIS - The cells to integrate over
5654: . t      - The time
5655: . X_tShift - The multiplier for the Jacobian with repsect to X_t
5656: . X      - Local solution vector
5657: . X_t    - Time-derivative of the local solution vector
5658: . Y      - Local input vector
5659: - user   - the user context

5661:   Output Parameter:
5662: . Z - Local output vector

5664:   Note:
5665:   We form the residual one batch of elements at a time. This allows us to offload work onto an accelerator,
5666:   like a GPU, or vectorize on a multicore machine.
5667: */
5668: 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)
5669: {
5670:   DM_Plex        *mesh  = (DM_Plex *)dm->data;
5671:   const char     *name  = "Jacobian";
5672:   DM              dmAux = NULL, plex, plexAux = NULL;
5673:   DMEnclosureType encAux;
5674:   Vec             A;
5675:   DMField         coordField;
5676:   PetscDS         prob, probAux = NULL;
5677:   PetscQuadrature quad;
5678:   PetscSection    section, globalSection, sectionAux;
5679:   PetscScalar    *elemMat, *elemMatD, *u, *u_t, *a = NULL, *y, *z;
5680:   const PetscInt *cells;
5681:   PetscInt        Nf, fieldI, fieldJ;
5682:   PetscInt        totDim, totDimAux = 0, cStart, cEnd, numCells, c;
5683:   PetscBool       hasDyn;

5685:   PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0);
5686:   DMConvert(dm, DMPLEX, &plex);
5687:   if (!cellIS) {
5688:     PetscInt depth;

5690:     DMPlexGetDepth(plex, &depth);
5691:     DMGetStratumIS(plex, "dim", depth, &cellIS);
5692:     if (!cellIS) DMGetStratumIS(plex, "depth", depth, &cellIS);
5693:   } else {
5694:     PetscObjectReference((PetscObject)cellIS);
5695:   }
5696:   ISGetLocalSize(cellIS, &numCells);
5697:   ISGetPointRange(cellIS, &cStart, &cEnd, &cells);
5698:   DMGetLocalSection(dm, &section);
5699:   DMGetGlobalSection(dm, &globalSection);
5700:   DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob);
5701:   PetscDSGetNumFields(prob, &Nf);
5702:   PetscDSGetTotalDimension(prob, &totDim);
5703:   PetscDSHasDynamicJacobian(prob, &hasDyn);
5704:   hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
5705:   DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &A);
5706:   if (A) {
5707:     VecGetDM(A, &dmAux);
5708:     DMGetEnclosureRelation(dmAux, dm, &encAux);
5709:     DMConvert(dmAux, DMPLEX, &plexAux);
5710:     DMGetLocalSection(plexAux, &sectionAux);
5711:     DMGetDS(dmAux, &probAux);
5712:     PetscDSGetTotalDimension(probAux, &totDimAux);
5713:   }
5714:   VecSet(Z, 0.0);
5715:   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);
5716:   if (dmAux) PetscMalloc1(numCells * totDimAux, &a);
5717:   DMGetCoordinateField(dm, &coordField);
5718:   for (c = cStart; c < cEnd; ++c) {
5719:     const PetscInt cell = cells ? cells[c] : c;
5720:     const PetscInt cind = c - cStart;
5721:     PetscScalar   *x = NULL, *x_t = NULL;
5722:     PetscInt       i;

5724:     DMPlexVecGetClosure(plex, section, X, cell, NULL, &x);
5725:     for (i = 0; i < totDim; ++i) u[cind * totDim + i] = x[i];
5726:     DMPlexVecRestoreClosure(plex, section, X, cell, NULL, &x);
5727:     if (X_t) {
5728:       DMPlexVecGetClosure(plex, section, X_t, cell, NULL, &x_t);
5729:       for (i = 0; i < totDim; ++i) u_t[cind * totDim + i] = x_t[i];
5730:       DMPlexVecRestoreClosure(plex, section, X_t, cell, NULL, &x_t);
5731:     }
5732:     if (dmAux) {
5733:       PetscInt subcell;
5734:       DMGetEnclosurePoint(dmAux, dm, encAux, cell, &subcell);
5735:       DMPlexVecGetClosure(plexAux, sectionAux, A, subcell, NULL, &x);
5736:       for (i = 0; i < totDimAux; ++i) a[cind * totDimAux + i] = x[i];
5737:       DMPlexVecRestoreClosure(plexAux, sectionAux, A, subcell, NULL, &x);
5738:     }
5739:     DMPlexVecGetClosure(plex, section, Y, cell, NULL, &x);
5740:     for (i = 0; i < totDim; ++i) y[cind * totDim + i] = x[i];
5741:     DMPlexVecRestoreClosure(plex, section, Y, cell, NULL, &x);
5742:   }
5743:   PetscArrayzero(elemMat, numCells * totDim * totDim);
5744:   if (hasDyn) PetscArrayzero(elemMatD, numCells * totDim * totDim);
5745:   for (fieldI = 0; fieldI < Nf; ++fieldI) {
5746:     PetscFE  fe;
5747:     PetscInt Nb;
5748:     /* Conforming batches */
5749:     PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
5750:     /* Remainder */
5751:     PetscInt        Nr, offset, Nq;
5752:     PetscQuadrature qGeom = NULL;
5753:     PetscInt        maxDegree;
5754:     PetscFEGeom    *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL;

5756:     PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe);
5757:     PetscFEGetQuadrature(fe, &quad);
5758:     PetscFEGetDimension(fe, &Nb);
5759:     PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches);
5760:     DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree);
5761:     if (maxDegree <= 1) DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom);
5762:     if (!qGeom) {
5763:       PetscFEGetQuadrature(fe, &qGeom);
5764:       PetscObjectReference((PetscObject)qGeom);
5765:     }
5766:     PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL);
5767:     DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM);
5768:     blockSize = Nb;
5769:     batchSize = numBlocks * blockSize;
5770:     PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches);
5771:     numChunks = numCells / (numBatches * batchSize);
5772:     Ne        = numChunks * numBatches * batchSize;
5773:     Nr        = numCells % (numBatches * batchSize);
5774:     offset    = numCells - Nr;
5775:     PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom);
5776:     PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &remGeom);
5777:     for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5778:       key.field = fieldI * Nf + fieldJ;
5779:       PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat);
5780:       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]);
5781:       if (hasDyn) {
5782:         PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatD);
5783:         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]);
5784:       }
5785:     }
5786:     PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &remGeom);
5787:     PetscFEGeomRestoreChunk(cgeomFEM, 0, offset, &chunkGeom);
5788:     DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM);
5789:     PetscQuadratureDestroy(&qGeom);
5790:   }
5791:   if (hasDyn) {
5792:     for (c = 0; c < numCells * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
5793:   }
5794:   for (c = cStart; c < cEnd; ++c) {
5795:     const PetscInt     cell = cells ? cells[c] : c;
5796:     const PetscInt     cind = c - cStart;
5797:     const PetscBLASInt M = totDim, one = 1;
5798:     const PetscScalar  a = 1.0, b = 0.0;

5800:     PetscCallBLAS("BLASgemv", BLASgemv_("N", &M, &M, &a, &elemMat[cind * totDim * totDim], &M, &y[cind * totDim], &one, &b, z, &one));
5801:     if (mesh->printFEM > 1) {
5802:       DMPrintCellMatrix(c, name, totDim, totDim, &elemMat[cind * totDim * totDim]);
5803:       DMPrintCellVector(c, "Y", totDim, &y[cind * totDim]);
5804:       DMPrintCellVector(c, "Z", totDim, z);
5805:     }
5806:     DMPlexVecSetClosure(dm, section, Z, cell, z, ADD_VALUES);
5807:   }
5808:   PetscFree6(u, u_t, elemMat, elemMatD, y, z);
5809:   if (mesh->printFEM) {
5810:     PetscPrintf(PetscObjectComm((PetscObject)Z), "Z:\n");
5811:     VecView(Z, NULL);
5812:   }
5813:   ISRestorePointRange(cellIS, &cStart, &cEnd, &cells);
5814:   PetscFree(a);
5815:   ISDestroy(&cellIS);
5816:   DMDestroy(&plexAux);
5817:   DMDestroy(&plex);
5818:   PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0);
5819:   return 0;
5820: }