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: [](chapter_unstructured), `DM`, `DMPLEX`, `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: [](chapter_unstructured), `DM`, `DMPLEX`, `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:   Level: advanced

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

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

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

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

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

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

234:   Collective on dm

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

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

246:   Level: advanced

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

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

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

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

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

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

311:   Input Parameters:
312: + dm - the `DMPLEX` object
313: - height - the maximum projection height >= 0

315:   Level: advanced

317: .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetMaxProjectionHeight()`, `DMProjectFunctionLocal()`, `DMProjectFunctionLabelLocal()`
318: @*/
319: PetscErrorCode DMPlexSetMaxProjectionHeight(DM dm, PetscInt height)
320: {
321:   DM_Plex *plex = (DM_Plex *)dm->data;

324:   plex->maxProjectionHeight = height;
325:   return 0;
326: }

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

332:   Input Parameters:
333: . dm - the `DMPLEX` object

335:   Output Parameters:
336: . height - the maximum projection height

338:   Level: intermediate

340: .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexSetMaxProjectionHeight()`, `DMProjectFunctionLocal()`, `DMProjectFunctionLabelLocal()`
341: @*/
342: PetscErrorCode DMPlexGetMaxProjectionHeight(DM dm, PetscInt *height)
343: {
344:   DM_Plex *plex = (DM_Plex *)dm->data;

347:   *height = plex->maxProjectionHeight;
348:   return 0;
349: }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

651:   Input Parameters:
652: + dm - The `DM`
653: - lv - A local vector with values in the global basis

655:   Output Parameters:
656: . lv - A local vector with values in the local basis

658:   Level: developer

660:   Note:
661:   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.

663: .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexLocalToGlobalBasis()`, `DMGetLocalSection()`, `DMPlexCreateBasisRotation()`
664: @*/
665: PetscErrorCode DMPlexGlobalToLocalBasis(DM dm, Vec lv)
666: {
669:   DMPlexBasisTransform_Internal(dm, lv, PETSC_FALSE);
670:   return 0;
671: }

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

676:   Input Parameters:
677: + dm - The `DM`
678: - lv - A local vector with values in the local basis

680:   Output Parameters:
681: . lv - A local vector with values in the global basis

683:   Level: developer

685:   Note:
686:   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.

688: .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGlobalToLocalBasis()`, `DMGetLocalSection()`, `DMPlexCreateBasisRotation()`
689: @*/
690: PetscErrorCode DMPlexLocalToGlobalBasis(DM dm, Vec lv)
691: {
694:   DMPlexBasisTransform_Internal(dm, lv, PETSC_TRUE);
695:   return 0;
696: }

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

702:   Input Parameters:
703: + dm    - The `DM`
704: . alpha - The first Euler angle, and in 2D the only one
705: . beta  - The second Euler angle
706: - gamma - The third Euler angle

708:   Level: developer

710:   Note:
711:   Following https://en.wikipedia.org/wiki/Euler_angles, we will specify Euler angles by extrinsic rotations, meaning that
712:   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
713: .vb
714:    The XYZ system rotates about the z axis by alpha. The X axis is now at angle alpha with respect to the x axis.
715:    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.
716:    The XYZ system rotates a third time about the z axis by gamma.
717: .ve

719: .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGlobalToLocalBasis()`, `DMPlexLocalToGlobalBasis()`
720: @*/
721: PetscErrorCode DMPlexCreateBasisRotation(DM dm, PetscReal alpha, PetscReal beta, PetscReal gamma)
722: {
723:   RotCtx  *rc;
724:   PetscInt cdim;

726:   DMGetCoordinateDim(dm, &cdim);
727:   PetscMalloc1(1, &rc);
728:   dm->transformCtx       = rc;
729:   dm->transformSetUp     = DMPlexBasisTransformSetUp_Rotation_Internal;
730:   dm->transformDestroy   = DMPlexBasisTransformDestroy_Rotation_Internal;
731:   dm->transformGetMatrix = DMPlexBasisTransformGetMatrix_Rotation_Internal;
732:   rc->dim                = cdim;
733:   rc->alpha              = alpha;
734:   rc->beta               = beta;
735:   rc->gamma              = gamma;
736:   (*dm->transformSetUp)(dm, dm->transformCtx);
737:   DMConstructBasisTransform_Internal(dm);
738:   return 0;
739: }

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

744:   Input Parameters:
745: + dm     - The `DM`, with a `PetscDS` that matches the problem being constrained
746: . time   - The time
747: . field  - The field to constrain
748: . Nc     - The number of constrained field components, or 0 for all components
749: . comps  - An array of constrained component numbers, or NULL for all components
750: . label  - The `DMLabel` defining constrained points
751: . numids - The number of `DMLabel` ids for constrained points
752: . ids    - An array of ids for constrained points
753: . func   - A pointwise function giving boundary values
754: - ctx    - An optional user context for bcFunc

756:   Output Parameter:
757: . locX   - A local vector to receives the boundary values

759:   Level: developer

761: .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMLabel`, `DMPlexInsertBoundaryValuesEssentialField()`, `DMPlexInsertBoundaryValuesEssentialBdField()`, `DMAddBoundary()`
762: @*/
763: 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)
764: {
765:   PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal x[], PetscInt, PetscScalar *u, void *ctx);
766:   void   **ctxs;
767:   PetscInt numFields;

769:   DMGetNumFields(dm, &numFields);
770:   PetscCalloc2(numFields, &funcs, numFields, &ctxs);
771:   funcs[field] = func;
772:   ctxs[field]  = ctx;
773:   DMProjectFunctionLabelLocal(dm, time, label, numids, ids, Nc, comps, funcs, ctxs, INSERT_BC_VALUES, locX);
774:   PetscFree2(funcs, ctxs);
775:   return 0;
776: }

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

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

794:   Output Parameter:
795: . locX   - A local vector to receives the boundary values

797:   Level: developer

799: .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexInsertBoundaryValuesEssential()`, `DMPlexInsertBoundaryValuesEssentialBdField()`, `DMAddBoundary()`
800: @*/
801: 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)
802: {
803:   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[]);
804:   void   **ctxs;
805:   PetscInt numFields;

807:   DMGetNumFields(dm, &numFields);
808:   PetscCalloc2(numFields, &funcs, numFields, &ctxs);
809:   funcs[field] = func;
810:   ctxs[field]  = ctx;
811:   DMProjectFieldLabelLocal(dm, time, label, numids, ids, Nc, comps, locU, funcs, INSERT_BC_VALUES, locX);
812:   PetscFree2(funcs, ctxs);
813:   return 0;
814: }

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

819:   Collective on dm

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

834:   Output Parameter:
835: . locX   - A local vector to receive the boundary values

837:   Level: developer

839: .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMProjectBdFieldLabelLocal()`, `DMPlexInsertBoundaryValuesEssential()`, `DMPlexInsertBoundaryValuesEssentialField()`, `DMAddBoundary()`
840: @*/
841: 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)
842: {
843:   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[]);
844:   void   **ctxs;
845:   PetscInt numFields;

847:   DMGetNumFields(dm, &numFields);
848:   PetscCalloc2(numFields, &funcs, numFields, &ctxs);
849:   funcs[field] = func;
850:   ctxs[field]  = ctx;
851:   DMProjectBdFieldLabelLocal(dm, time, label, numids, ids, Nc, comps, locU, funcs, INSERT_BC_VALUES, locX);
852:   PetscFree2(funcs, ctxs);
853:   return 0;
854: }

856: /*@C
857:   DMPlexInsertBoundaryValuesRiemann - Insert boundary values into a local vector

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

874:   Output Parameter:
875: . locX   - A local vector to receives the boundary values

877:   Level: developer

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

882: .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexInsertBoundaryValuesEssential()`, `DMPlexInsertBoundaryValuesEssentialField()`, `DMAddBoundary()`
883: @*/
884: 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)
885: {
886:   PetscDS            prob;
887:   PetscSF            sf;
888:   DM                 dmFace, dmCell, dmGrad;
889:   const PetscScalar *facegeom, *cellgeom = NULL, *grad;
890:   const PetscInt    *leaves;
891:   PetscScalar       *x, *fx;
892:   PetscInt           dim, nleaves, loc, fStart, fEnd, pdim, i;
893:   PetscErrorCode     ierru = 0;

895:   DMGetPointSF(dm, &sf);
896:   PetscSFGetGraph(sf, NULL, &nleaves, &leaves, NULL);
897:   nleaves = PetscMax(0, nleaves);
898:   DMGetDimension(dm, &dim);
899:   DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd);
900:   DMGetDS(dm, &prob);
901:   VecGetDM(faceGeometry, &dmFace);
902:   VecGetArrayRead(faceGeometry, &facegeom);
903:   if (cellGeometry) {
904:     VecGetDM(cellGeometry, &dmCell);
905:     VecGetArrayRead(cellGeometry, &cellgeom);
906:   }
907:   if (Grad) {
908:     PetscFV fv;

910:     PetscDSGetDiscretization(prob, field, (PetscObject *)&fv);
911:     VecGetDM(Grad, &dmGrad);
912:     VecGetArrayRead(Grad, &grad);
913:     PetscFVGetNumComponents(fv, &pdim);
914:     DMGetWorkArray(dm, pdim, MPIU_SCALAR, &fx);
915:   }
916:   VecGetArray(locX, &x);
917:   for (i = 0; i < numids; ++i) {
918:     IS              faceIS;
919:     const PetscInt *faces;
920:     PetscInt        numFaces, f;

922:     DMLabelGetStratumIS(label, ids[i], &faceIS);
923:     if (!faceIS) continue; /* No points with that id on this process */
924:     ISGetLocalSize(faceIS, &numFaces);
925:     ISGetIndices(faceIS, &faces);
926:     for (f = 0; f < numFaces; ++f) {
927:       const PetscInt   face = faces[f], *cells;
928:       PetscFVFaceGeom *fg;

930:       if ((face < fStart) || (face >= fEnd)) continue; /* Refinement adds non-faces to labels */
931:       PetscFindInt(face, nleaves, (PetscInt *)leaves, &loc);
932:       if (loc >= 0) continue;
933:       DMPlexPointLocalRead(dmFace, face, facegeom, &fg);
934:       DMPlexGetSupport(dm, face, &cells);
935:       if (Grad) {
936:         PetscFVCellGeom *cg;
937:         PetscScalar     *cx, *cgrad;
938:         PetscScalar     *xG;
939:         PetscReal        dx[3];
940:         PetscInt         d;

942:         DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cg);
943:         DMPlexPointLocalRead(dm, cells[0], x, &cx);
944:         DMPlexPointLocalRead(dmGrad, cells[0], grad, &cgrad);
945:         DMPlexPointLocalFieldRef(dm, cells[1], field, x, &xG);
946:         DMPlex_WaxpyD_Internal(dim, -1, cg->centroid, fg->centroid, dx);
947:         for (d = 0; d < pdim; ++d) fx[d] = cx[d] + DMPlex_DotD_Internal(dim, &cgrad[d * dim], dx);
948:         (*func)(time, fg->centroid, fg->normal, fx, xG, ctx);
949:       } else {
950:         PetscScalar *xI;
951:         PetscScalar *xG;

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

978: static PetscErrorCode zero(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nc, PetscScalar *u, void *ctx)
979: {
980:   PetscInt c;
981:   for (c = 0; c < Nc; ++c) u[c] = 0.0;
982:   return 0;
983: }

985: PetscErrorCode DMPlexInsertBoundaryValues_Plex(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
986: {
987:   PetscObject isZero;
988:   PetscDS     prob;
989:   PetscInt    numBd, b;

991:   DMGetDS(dm, &prob);
992:   PetscDSGetNumBoundary(prob, &numBd);
993:   PetscObjectQuery((PetscObject)locX, "__Vec_bc_zero__", &isZero);
994:   for (b = 0; b < numBd; ++b) {
995:     PetscWeakForm           wf;
996:     DMBoundaryConditionType type;
997:     const char             *name;
998:     DMLabel                 label;
999:     PetscInt                field, Nc;
1000:     const PetscInt         *comps;
1001:     PetscObject             obj;
1002:     PetscClassId            id;
1003:     void (*bvfunc)(void);
1004:     PetscInt        numids;
1005:     const PetscInt *ids;
1006:     void           *ctx;

1008:     PetscDSGetBoundary(prob, b, &wf, &type, &name, &label, &numids, &ids, &field, &Nc, &comps, &bvfunc, NULL, &ctx);
1009:     if (insertEssential != (type & DM_BC_ESSENTIAL)) continue;
1010:     DMGetField(dm, field, NULL, &obj);
1011:     PetscObjectGetClassId(obj, &id);
1012:     if (id == PETSCFE_CLASSID) {
1013:       switch (type) {
1014:         /* for FEM, there is no insertion to be done for non-essential boundary conditions */
1015:       case DM_BC_ESSENTIAL: {
1016:         PetscSimplePointFunc func = (PetscSimplePointFunc)bvfunc;

1018:         if (isZero) func = zero;
1019:         DMPlexLabelAddCells(dm, label);
1020:         DMPlexInsertBoundaryValuesEssential(dm, time, field, Nc, comps, label, numids, ids, func, ctx, locX);
1021:         DMPlexLabelClearCells(dm, label);
1022:       } break;
1023:       case DM_BC_ESSENTIAL_FIELD: {
1024:         PetscPointFunc func = (PetscPointFunc)bvfunc;

1026:         DMPlexLabelAddCells(dm, label);
1027:         DMPlexInsertBoundaryValuesEssentialField(dm, time, locX, field, Nc, comps, label, numids, ids, func, ctx, locX);
1028:         DMPlexLabelClearCells(dm, label);
1029:       } break;
1030:       default:
1031:         break;
1032:       }
1033:     } else if (id == PETSCFV_CLASSID) {
1034:       {
1035:         PetscErrorCode (*func)(PetscReal, const PetscReal *, const PetscReal *, const PetscScalar *, PetscScalar *, void *) = (PetscErrorCode(*)(PetscReal, const PetscReal *, const PetscReal *, const PetscScalar *, PetscScalar *, void *))bvfunc;

1037:         if (!faceGeomFVM) continue;
1038:         DMPlexInsertBoundaryValuesRiemann(dm, time, faceGeomFVM, cellGeomFVM, gradFVM, field, Nc, comps, label, numids, ids, func, ctx, locX);
1039:       }
1040:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1041:   }
1042:   return 0;
1043: }

1045: PetscErrorCode DMPlexInsertTimeDerivativeBoundaryValues_Plex(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
1046: {
1047:   PetscObject isZero;
1048:   PetscDS     prob;
1049:   PetscInt    numBd, b;

1051:   if (!locX) return 0;
1052:   DMGetDS(dm, &prob);
1053:   PetscDSGetNumBoundary(prob, &numBd);
1054:   PetscObjectQuery((PetscObject)locX, "__Vec_bc_zero__", &isZero);
1055:   for (b = 0; b < numBd; ++b) {
1056:     PetscWeakForm           wf;
1057:     DMBoundaryConditionType type;
1058:     const char             *name;
1059:     DMLabel                 label;
1060:     PetscInt                field, Nc;
1061:     const PetscInt         *comps;
1062:     PetscObject             obj;
1063:     PetscClassId            id;
1064:     PetscInt                numids;
1065:     const PetscInt         *ids;
1066:     void (*bvfunc)(void);
1067:     void *ctx;

1069:     PetscDSGetBoundary(prob, b, &wf, &type, &name, &label, &numids, &ids, &field, &Nc, &comps, NULL, &bvfunc, &ctx);
1070:     if (insertEssential != (type & DM_BC_ESSENTIAL)) continue;
1071:     DMGetField(dm, field, NULL, &obj);
1072:     PetscObjectGetClassId(obj, &id);
1073:     if (id == PETSCFE_CLASSID) {
1074:       switch (type) {
1075:         /* for FEM, there is no insertion to be done for non-essential boundary conditions */
1076:       case DM_BC_ESSENTIAL: {
1077:         PetscSimplePointFunc func_t = (PetscSimplePointFunc)bvfunc;

1079:         if (isZero) func_t = zero;
1080:         DMPlexLabelAddCells(dm, label);
1081:         DMPlexInsertBoundaryValuesEssential(dm, time, field, Nc, comps, label, numids, ids, func_t, ctx, locX);
1082:         DMPlexLabelClearCells(dm, label);
1083:       } break;
1084:       case DM_BC_ESSENTIAL_FIELD: {
1085:         PetscPointFunc func_t = (PetscPointFunc)bvfunc;

1087:         DMPlexLabelAddCells(dm, label);
1088:         DMPlexInsertBoundaryValuesEssentialField(dm, time, locX, field, Nc, comps, label, numids, ids, func_t, ctx, locX);
1089:         DMPlexLabelClearCells(dm, label);
1090:       } break;
1091:       default:
1092:         break;
1093:       }
1094:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1095:   }
1096:   return 0;
1097: }

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

1102:   Not Collective

1104:   Input Parameters:
1105: + dm - The `DM`
1106: . insertEssential - Should I insert essential (e.g. Dirichlet) or inessential (e.g. Neumann) boundary conditions
1107: . time - The time
1108: . faceGeomFVM - Face geometry data for FV discretizations
1109: . cellGeomFVM - Cell geometry data for FV discretizations
1110: - gradFVM - Gradient reconstruction data for FV discretizations

1112:   Output Parameters:
1113: . locX - Solution updated with boundary values

1115:   Level: intermediate

1117: .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMProjectFunctionLabelLocal()`, `DMAddBoundary()`
1118: @*/
1119: PetscErrorCode DMPlexInsertBoundaryValues(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
1120: {
1126:   PetscTryMethod(dm, "DMPlexInsertBoundaryValues_C", (DM, PetscBool, Vec, PetscReal, Vec, Vec, Vec), (dm, insertEssential, locX, time, faceGeomFVM, cellGeomFVM, gradFVM));
1127:   return 0;
1128: }

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

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

1141:   Output Parameters:
1142: . locX_t - Solution updated with boundary values

1144:   Level: developer

1146: .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMProjectFunctionLabelLocal()`
1147: @*/
1148: PetscErrorCode DMPlexInsertTimeDerivativeBoundaryValues(DM dm, PetscBool insertEssential, Vec locX_t, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
1149: {
1155:   PetscTryMethod(dm, "DMPlexInsertTimeDerviativeBoundaryValues_C", (DM, PetscBool, Vec, PetscReal, Vec, Vec, Vec), (dm, insertEssential, locX_t, time, faceGeomFVM, cellGeomFVM, gradFVM));
1156:   return 0;
1157: }

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

1163:   DMGetLocalVector(dm, &localX);
1164:   DMPlexInsertBoundaryValues(dm, PETSC_TRUE, localX, time, NULL, NULL, NULL);
1165:   DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX);
1166:   DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX);
1167:   DMPlexComputeL2DiffLocal(dm, time, funcs, ctxs, localX, diff);
1168:   DMRestoreLocalVector(dm, &localX);
1169:   return 0;
1170: }

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

1175:   Collective on dm

1177:   Input Parameters:
1178: + dm     - The `DM`
1179: . time   - The time
1180: . funcs  - The functions to evaluate for each field component
1181: . ctxs   - Optional array of contexts to pass to each function, or NULL.
1182: - localX - The coefficient vector u_h, a local vector

1184:   Output Parameter:
1185: . diff - The diff ||u - u_h||_2

1187:   Level: developer

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

1206:   DMGetDimension(dm, &dim);
1207:   DMGetCoordinateDim(dm, &coordDim);
1208:   fegeom.dimEmbed = coordDim;
1209:   DMGetLocalSection(dm, &section);
1210:   PetscSectionGetNumFields(section, &numFields);
1211:   DMGetBasisTransformDM_Internal(dm, &tdm);
1212:   DMGetBasisTransformVec_Internal(dm, &tv);
1213:   DMHasBasisTransform(dm, &transform);
1214:   for (field = 0; field < numFields; ++field) {
1215:     PetscObject  obj;
1216:     PetscClassId id;
1217:     PetscInt     Nc;

1219:     DMGetField(dm, field, NULL, &obj);
1220:     PetscObjectGetClassId(obj, &id);
1221:     if (id == PETSCFE_CLASSID) {
1222:       PetscFE fe = (PetscFE)obj;

1224:       PetscFEGetQuadrature(fe, &quad);
1225:       PetscFEGetNumComponents(fe, &Nc);
1226:     } else if (id == PETSCFV_CLASSID) {
1227:       PetscFV fv = (PetscFV)obj;

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

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

1247:     for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1248:       PetscObject  obj;
1249:       PetscClassId id;
1250:       void *const  ctx = ctxs ? ctxs[field] : NULL;
1251:       PetscInt     Nb, Nc, q, fc;

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

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

1314: 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)
1315: {
1316:   const PetscInt   debug = ((DM_Plex *)dm->data)->printL2;
1317:   DM               tdm;
1318:   PetscSection     section;
1319:   PetscQuadrature  quad;
1320:   Vec              localX, tv;
1321:   PetscScalar     *funcVal, *interpolant;
1322:   const PetscReal *quadWeights;
1323:   PetscFEGeom      fegeom;
1324:   PetscReal       *coords, *gcoords;
1325:   PetscReal        localDiff = 0.0;
1326:   PetscInt         dim, coordDim, qNc = 0, Nq = 0, numFields, numComponents = 0, cStart, cEnd, c, field, fieldOffset;
1327:   PetscBool        transform;

1329:   DMGetDimension(dm, &dim);
1330:   DMGetCoordinateDim(dm, &coordDim);
1331:   fegeom.dimEmbed = coordDim;
1332:   DMGetLocalSection(dm, &section);
1333:   PetscSectionGetNumFields(section, &numFields);
1334:   DMGetLocalVector(dm, &localX);
1335:   DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX);
1336:   DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX);
1337:   DMGetBasisTransformDM_Internal(dm, &tdm);
1338:   DMGetBasisTransformVec_Internal(dm, &tv);
1339:   DMHasBasisTransform(dm, &transform);
1340:   for (field = 0; field < numFields; ++field) {
1341:     PetscFE  fe;
1342:     PetscInt Nc;

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

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

1362:     for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1363:       PetscFE     fe;
1364:       void *const ctx = ctxs ? ctxs[field] : NULL;
1365:       PetscInt    Nb, Nc, q, fc;

1367:       DMGetField(dm, field, NULL, (PetscObject *)&fe);
1368:       PetscFEGetDimension(fe, &Nb);
1369:       PetscFEGetNumComponents(fe, &Nc);
1370:       if (debug) {
1371:         char title[1024];
1372:         PetscSNPrintf(title, 1023, "Solution for Field %" PetscInt_FMT, field);
1373:         DMPrintCellVector(c, title, Nb, &x[fieldOffset]);
1374:       }
1375:       for (q = 0; q < Nq; ++q) {
1376:         PetscFEGeom    qgeom;

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

1428: PetscErrorCode DMComputeL2FieldDiff_Plex(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, PetscReal *diff)
1429: {
1430:   const PetscInt debug = ((DM_Plex *)dm->data)->printL2;
1431:   DM             tdm;
1432:   DMLabel        depthLabel;
1433:   PetscSection   section;
1434:   Vec            localX, tv;
1435:   PetscReal     *localDiff;
1436:   PetscInt       dim, depth, dE, Nf, f, Nds, s;
1437:   PetscBool      transform;

1439:   DMGetDimension(dm, &dim);
1440:   DMGetCoordinateDim(dm, &dE);
1441:   DMGetLocalSection(dm, &section);
1442:   DMGetLocalVector(dm, &localX);
1443:   DMGetBasisTransformDM_Internal(dm, &tdm);
1444:   DMGetBasisTransformVec_Internal(dm, &tv);
1445:   DMHasBasisTransform(dm, &transform);
1446:   DMGetNumFields(dm, &Nf);
1447:   DMPlexGetDepthLabel(dm, &depthLabel);
1448:   DMLabelGetNumValues(depthLabel, &depth);

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

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

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

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

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

1580: /*@C
1581:   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.

1583:   Collective on dm

1585:   Input Parameters:
1586: + dm    - The `DM`
1587: . time  - The time
1588: . funcs - The functions to evaluate for each field component: NULL means that component does not contribute to error calculation
1589: . ctxs  - Optional array of contexts to pass to each function, or NULL.
1590: - X     - The coefficient vector u_h

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

1595:   Level: developer

1597: .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
1598: @*/
1599: PetscErrorCode DMPlexComputeL2DiffVec(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, Vec D)
1600: {
1601:   PetscSection     section;
1602:   PetscQuadrature  quad;
1603:   Vec              localX;
1604:   PetscFEGeom      fegeom;
1605:   PetscScalar     *funcVal, *interpolant;
1606:   PetscReal       *coords;
1607:   const PetscReal *quadPoints, *quadWeights;
1608:   PetscInt         dim, coordDim, numFields, numComponents = 0, qNc, Nq, cStart, cEnd, c, field, fieldOffset;

1610:   VecSet(D, 0.0);
1611:   DMGetDimension(dm, &dim);
1612:   DMGetCoordinateDim(dm, &coordDim);
1613:   DMGetLocalSection(dm, &section);
1614:   PetscSectionGetNumFields(section, &numFields);
1615:   DMGetLocalVector(dm, &localX);
1616:   DMProjectFunctionLocal(dm, time, funcs, ctxs, INSERT_BC_VALUES, localX);
1617:   DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX);
1618:   DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX);
1619:   for (field = 0; field < numFields; ++field) {
1620:     PetscObject  obj;
1621:     PetscClassId id;
1622:     PetscInt     Nc;

1624:     DMGetField(dm, field, NULL, &obj);
1625:     PetscObjectGetClassId(obj, &id);
1626:     if (id == PETSCFE_CLASSID) {
1627:       PetscFE fe = (PetscFE)obj;

1629:       PetscFEGetQuadrature(fe, &quad);
1630:       PetscFEGetNumComponents(fe, &Nc);
1631:     } else if (id == PETSCFV_CLASSID) {
1632:       PetscFV fv = (PetscFV)obj;

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

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

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

1657:       DMGetField(dm, field, NULL, &obj);
1658:       PetscObjectGetClassId(obj, &id);
1659:       if (id == PETSCFE_CLASSID) {
1660:         PetscFEGetNumComponents((PetscFE)obj, &Nc);
1661:         PetscFEGetDimension((PetscFE)obj, &Nb);
1662:       } else if (id == PETSCFV_CLASSID) {
1663:         PetscFVGetNumComponents((PetscFV)obj, &Nc);
1664:         Nb = 1;
1665:       } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1666:       if (funcs[field]) {
1667:         for (q = 0; q < Nq; ++q) {
1668:           PetscFEGeom qgeom;

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

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

1707:   Collective on dm

1709:   Input Parameters:
1710: + dm - The `DM`
1711: - locX  - The coefficient vector u_h

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

1716:   Level: developer

1718:   Note:
1719:   $ 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

1721: .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
1722: @*/
1723: PetscErrorCode DMPlexComputeClementInterpolant(DM dm, Vec locX, Vec locC)
1724: {
1725:   PetscInt         debug = ((DM_Plex *)dm->data)->printFEM;
1726:   DM               dmc;
1727:   PetscQuadrature  quad;
1728:   PetscScalar     *interpolant, *valsum;
1729:   PetscFEGeom      fegeom;
1730:   PetscReal       *coords;
1731:   const PetscReal *quadPoints, *quadWeights;
1732:   PetscInt         dim, cdim, Nf, f, Nc = 0, Nq, qNc, cStart, cEnd, vStart, vEnd, v;

1734:   PetscCitationsRegister(ClementCitation, &Clementcite);
1735:   VecGetDM(locC, &dmc);
1736:   VecSet(locC, 0.0);
1737:   DMGetDimension(dm, &dim);
1738:   DMGetCoordinateDim(dm, &cdim);
1739:   fegeom.dimEmbed = cdim;
1740:   DMGetNumFields(dm, &Nf);
1742:   for (f = 0; f < Nf; ++f) {
1743:     PetscObject  obj;
1744:     PetscClassId id;
1745:     PetscInt     fNc;

1747:     DMGetField(dm, f, NULL, &obj);
1748:     PetscObjectGetClassId(obj, &id);
1749:     if (id == PETSCFE_CLASSID) {
1750:       PetscFE fe = (PetscFE)obj;

1752:       PetscFEGetQuadrature(fe, &quad);
1753:       PetscFEGetNumComponents(fe, &fNc);
1754:     } else if (id == PETSCFV_CLASSID) {
1755:       PetscFV fv = (PetscFV)obj;

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

1772:     PetscArrayzero(valsum, Nc);
1773:     DMPlexGetTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star);
1774:     for (st = 0; st < starSize * 2; st += 2) {
1775:       const PetscInt cell = star[st];
1776:       PetscScalar   *val  = &valsum[Nc];
1777:       PetscScalar   *x    = NULL;
1778:       PetscReal      vol  = 0.0;
1779:       PetscInt       foff = 0;

1781:       if ((cell < cStart) || (cell >= cEnd)) continue;
1782:       DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ);
1783:       DMPlexVecGetClosure(dm, NULL, locX, cell, NULL, &x);
1784:       for (f = 0; f < Nf; ++f) {
1785:         PetscObject  obj;
1786:         PetscClassId id;
1787:         PetscInt     Nb, fNc, q;

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

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

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

1838:   Collective on dm

1840:   Input Parameters:
1841: + dm - The `DM`
1842: - locX  - The coefficient vector u_h

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

1847:   Level: developer

1849:   Note:
1850:   $\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

1852: .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
1853: @*/
1854: PetscErrorCode DMPlexComputeGradientClementInterpolant(DM dm, Vec locX, Vec locC)
1855: {
1856:   DM_Plex         *mesh  = (DM_Plex *)dm->data;
1857:   PetscInt         debug = mesh->printFEM;
1858:   DM               dmC;
1859:   PetscQuadrature  quad;
1860:   PetscScalar     *interpolant, *gradsum;
1861:   PetscFEGeom      fegeom;
1862:   PetscReal       *coords;
1863:   const PetscReal *quadPoints, *quadWeights;
1864:   PetscInt         dim, coordDim, numFields, numComponents = 0, qNc, Nq, cStart, cEnd, vStart, vEnd, v, field, fieldOffset;

1866:   PetscCitationsRegister(ClementCitation, &Clementcite);
1867:   VecGetDM(locC, &dmC);
1868:   VecSet(locC, 0.0);
1869:   DMGetDimension(dm, &dim);
1870:   DMGetCoordinateDim(dm, &coordDim);
1871:   fegeom.dimEmbed = coordDim;
1872:   DMGetNumFields(dm, &numFields);
1874:   for (field = 0; field < numFields; ++field) {
1875:     PetscObject  obj;
1876:     PetscClassId id;
1877:     PetscInt     Nc;

1879:     DMGetField(dm, field, NULL, &obj);
1880:     PetscObjectGetClassId(obj, &id);
1881:     if (id == PETSCFE_CLASSID) {
1882:       PetscFE fe = (PetscFE)obj;

1884:       PetscFEGetQuadrature(fe, &quad);
1885:       PetscFEGetNumComponents(fe, &Nc);
1886:     } else if (id == PETSCFV_CLASSID) {
1887:       PetscFV fv = (PetscFV)obj;

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

1904:     PetscArrayzero(gradsum, coordDim * numComponents);
1905:     DMPlexGetTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star);
1906:     for (st = 0; st < starSize * 2; st += 2) {
1907:       const PetscInt cell = star[st];
1908:       PetscScalar   *grad = &gradsum[coordDim * numComponents];
1909:       PetscScalar   *x    = NULL;
1910:       PetscReal      vol  = 0.0;

1912:       if ((cell < cStart) || (cell >= cEnd)) continue;
1913:       DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ);
1914:       DMPlexVecGetClosure(dm, NULL, locX, cell, NULL, &x);
1915:       for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1916:         PetscObject  obj;
1917:         PetscClassId id;
1918:         PetscInt     Nb, Nc, q, qc = 0;

1920:         PetscArrayzero(grad, coordDim * numComponents);
1921:         DMGetField(dm, field, NULL, &obj);
1922:         PetscObjectGetClassId(obj, &id);
1923:         if (id == PETSCFE_CLASSID) {
1924:           PetscFEGetNumComponents((PetscFE)obj, &Nc);
1925:           PetscFEGetDimension((PetscFE)obj, &Nb);
1926:         } else if (id == PETSCFV_CLASSID) {
1927:           PetscFVGetNumComponents((PetscFV)obj, &Nc);
1928:           Nb = 1;
1929:         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1930:         for (q = 0; q < Nq; ++q) {
1931:           PetscFEGeom qgeom;

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

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

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

2000:   DMGetDS(dm, &prob);
2001:   DMGetDimension(dm, &dim);
2002:   DMGetLocalSection(dm, &section);
2003:   DMGetNumFields(dm, &Nf);
2004:   /* Determine which discretizations we have */
2005:   for (f = 0; f < Nf; ++f) {
2006:     PetscObject  obj;
2007:     PetscClassId id;

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

2050:     for (f = 0; f < Nf; ++f) {
2051:       PetscObject  obj;
2052:       PetscClassId id;

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

2085:     DMPlexVecGetClosure(dm, section, locX, c, NULL, &x);
2086:     for (i = 0; i < totDim; ++i) u[c * totDim + i] = x[i];
2087:     DMPlexVecRestoreClosure(dm, section, locX, c, NULL, &x);
2088:     if (dmAux) {
2089:       DMPlexVecGetClosure(dmAux, sectionAux, locA, c, NULL, &x);
2090:       for (i = 0; i < totDimAux; ++i) a[c * totDimAux + i] = x[i];
2091:       DMPlexVecRestoreClosure(dmAux, sectionAux, locA, c, NULL, &x);
2092:     }
2093:   }
2094:   /* Do integration for each field */
2095:   for (f = 0; f < Nf; ++f) {
2096:     PetscObject  obj;
2097:     PetscClassId id;
2098:     PetscInt     numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;

2100:     PetscDSGetDiscretization(prob, f, &obj);
2101:     PetscObjectGetClassId(obj, &id);
2102:     if (id == PETSCFE_CLASSID) {
2103:       PetscFE         fe = (PetscFE)obj;
2104:       PetscQuadrature q;
2105:       PetscFEGeom    *chunkGeom = NULL;
2106:       PetscInt        Nq, Nb;

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

2131:       PetscDSGetObjective(prob, f, &obj_func);
2132:       PetscDSGetFieldOffset(prob, f, &foff);
2133:       if (obj_func) {
2134:         for (c = 0; c < numCells; ++c) {
2135:           PetscScalar *u_x;

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

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

2166:   Input Parameters:
2167: + dm - The mesh
2168: . X  - Global input vector
2169: - user - The user context

2171:   Output Parameter:
2172: . integral - Integral for each field

2174:   Level: developer

2176: .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexSNESComputeResidualFEM()`
2177: @*/
2178: PetscErrorCode DMPlexComputeIntegralFEM(DM dm, Vec X, PetscScalar *integral, void *user)
2179: {
2180:   DM_Plex     *mesh = (DM_Plex *)dm->data;
2181:   PetscScalar *cintegral, *lintegral;
2182:   PetscInt     Nf, f, cellHeight, cStart, cEnd, cell;

2187:   PetscLogEventBegin(DMPLEX_IntegralFEM, dm, 0, 0, 0);
2188:   DMGetNumFields(dm, &Nf);
2189:   DMPlexGetVTKCellHeight(dm, &cellHeight);
2190:   DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd);
2191:   /* TODO Introduce a loop over large chunks (right now this is a single chunk) */
2192:   PetscCalloc2(Nf, &lintegral, (cEnd - cStart) * Nf, &cintegral);
2193:   DMPlexComputeIntegral_Internal(dm, X, cStart, cEnd, cintegral, user);
2194:   /* Sum up values */
2195:   for (cell = cStart; cell < cEnd; ++cell) {
2196:     const PetscInt c = cell - cStart;

2198:     if (mesh->printFEM > 1) DMPrintCellVector(cell, "Cell Integral", Nf, &cintegral[c * Nf]);
2199:     for (f = 0; f < Nf; ++f) lintegral[f] += cintegral[c * Nf + f];
2200:   }
2201:   MPIU_Allreduce(lintegral, integral, Nf, MPIU_SCALAR, MPIU_SUM, PetscObjectComm((PetscObject)dm));
2202:   if (mesh->printFEM) {
2203:     PetscPrintf(PetscObjectComm((PetscObject)dm), "Integral:");
2204:     for (f = 0; f < Nf; ++f) PetscPrintf(PetscObjectComm((PetscObject)dm), " %g", (double)PetscRealPart(integral[f]));
2205:     PetscPrintf(PetscObjectComm((PetscObject)dm), "\n");
2206:   }
2207:   PetscFree2(lintegral, cintegral);
2208:   PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0);
2209:   return 0;
2210: }

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

2215:   Input Parameters:
2216: + dm - The mesh
2217: . X  - Global input vector
2218: - user - The user context

2220:   Output Parameter:
2221: . integral - Cellwise integrals for each field

2223:   Level: developer

2225: .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexSNESComputeResidualFEM()`
2226: @*/
2227: PetscErrorCode DMPlexComputeCellwiseIntegralFEM(DM dm, Vec X, Vec F, void *user)
2228: {
2229:   DM_Plex     *mesh = (DM_Plex *)dm->data;
2230:   DM           dmF;
2231:   PetscSection sectionF;
2232:   PetscScalar *cintegral, *af;
2233:   PetscInt     Nf, f, cellHeight, cStart, cEnd, cell;

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

2253:     if (mesh->printFEM > 1) DMPrintCellVector(cell, "Cell Integral", Nf, &cintegral[c * Nf]);
2254:     PetscSectionGetDof(sectionF, cell, &dof);
2255:     PetscSectionGetOffset(sectionF, cell, &off);
2257:     for (f = 0; f < Nf; ++f) af[off + f] = cintegral[c * Nf + f];
2258:   }
2259:   VecRestoreArray(F, &af);
2260:   PetscFree(cintegral);
2261:   PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0);
2262:   return 0;
2263: }

2265: 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)
2266: {
2267:   DM                 plex = NULL, plexA = NULL;
2268:   DMEnclosureType    encAux;
2269:   PetscDS            prob, probAux       = NULL;
2270:   PetscSection       section, sectionAux = NULL;
2271:   Vec                locA = NULL;
2272:   DMField            coordField;
2273:   PetscInt           Nf, totDim, *uOff, *uOff_x;
2274:   PetscInt           NfAux = 0, totDimAux = 0, *aOff = NULL;
2275:   PetscScalar       *u, *a = NULL;
2276:   const PetscScalar *constants;
2277:   PetscInt           numConstants, f;

2279:   DMGetCoordinateField(dm, &coordField);
2280:   DMConvert(dm, DMPLEX, &plex);
2281:   DMGetDS(dm, &prob);
2282:   DMGetLocalSection(dm, &section);
2283:   PetscSectionGetNumFields(section, &Nf);
2284:   /* Determine which discretizations we have */
2285:   for (f = 0; f < Nf; ++f) {
2286:     PetscObject  obj;
2287:     PetscClassId id;

2289:     PetscDSGetDiscretization(prob, f, &obj);
2290:     PetscObjectGetClassId(obj, &id);
2292:   }
2293:   /* Read DS information */
2294:   PetscDSGetTotalDimension(prob, &totDim);
2295:   PetscDSGetComponentOffsets(prob, &uOff);
2296:   PetscDSGetComponentDerivativeOffsets(prob, &uOff_x);
2297:   PetscDSGetConstants(prob, &numConstants, &constants);
2298:   /* Read Auxiliary DS information */
2299:   DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA);
2300:   if (locA) {
2301:     DM dmAux;

2303:     VecGetDM(locA, &dmAux);
2304:     DMGetEnclosureRelation(dmAux, dm, &encAux);
2305:     DMConvert(dmAux, DMPLEX, &plexA);
2306:     DMGetDS(dmAux, &probAux);
2307:     PetscDSGetNumFields(probAux, &NfAux);
2308:     DMGetLocalSection(dmAux, &sectionAux);
2309:     PetscDSGetTotalDimension(probAux, &totDimAux);
2310:     PetscDSGetComponentOffsets(probAux, &aOff);
2311:   }
2312:   /* Integrate over points */
2313:   {
2314:     PetscFEGeom    *fgeom, *chunkGeom = NULL;
2315:     PetscInt        maxDegree;
2316:     PetscQuadrature qGeom = NULL;
2317:     const PetscInt *points;
2318:     PetscInt        numFaces, face, Nq, field;
2319:     PetscInt        numChunks, chunkSize, chunk, Nr, offset;

2321:     ISGetLocalSize(pointIS, &numFaces);
2322:     ISGetIndices(pointIS, &points);
2323:     PetscCalloc2(numFaces * totDim, &u, locA ? numFaces * totDimAux : 0, &a);
2324:     DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree);
2325:     for (field = 0; field < Nf; ++field) {
2326:       PetscFE fe;

2328:       PetscDSGetDiscretization(prob, field, (PetscObject *)&fe);
2329:       if (maxDegree <= 1) DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom);
2330:       if (!qGeom) {
2331:         PetscFEGetFaceQuadrature(fe, &qGeom);
2332:         PetscObjectReference((PetscObject)qGeom);
2333:       }
2334:       PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL);
2335:       DMPlexGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom);
2336:       for (face = 0; face < numFaces; ++face) {
2337:         const PetscInt point = points[face], *support;
2338:         PetscScalar   *x     = NULL;
2339:         PetscInt       i;

2341:         DMPlexGetSupport(dm, point, &support);
2342:         DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x);
2343:         for (i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
2344:         DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x);
2345:         if (locA) {
2346:           PetscInt subp;
2347:           DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp);
2348:           DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x);
2349:           for (i = 0; i < totDimAux; ++i) a[f * totDimAux + i] = x[i];
2350:           DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x);
2351:         }
2352:       }
2353:       /* Get blocking */
2354:       {
2355:         PetscQuadrature q;
2356:         PetscInt        numBatches, batchSize, numBlocks, blockSize;
2357:         PetscInt        Nq, Nb;

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

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

2395:   Input Parameters:
2396: + dm      - The mesh
2397: . X       - Global input vector
2398: . label   - The boundary `DMLabel`
2399: . numVals - The number of label values to use, or `PETSC_DETERMINE` for all values
2400: . vals    - The label values to use, or NULL for all values
2401: . func    - The function to integrate along the boundary
2402: - user    - The user context

2404:   Output Parameter:
2405: . integral - Integral for each field

2407:   Level: developer

2409: .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexComputeIntegralFEM()`, `DMPlexComputeBdResidualFEM()`
2410: @*/
2411: 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)
2412: {
2413:   Vec          locX;
2414:   PetscSection section;
2415:   DMLabel      depthLabel;
2416:   IS           facetIS;
2417:   PetscInt     dim, Nf, f, v;

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

2442:     DMLabelGetStratumIS(label, vals[v], &pointIS);
2443:     if (!pointIS) continue; /* No points with that id on this process */
2444:     {
2445:       IS isectIS;

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

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

2470:   Input Parameters:
2471: + dmc  - The coarse mesh
2472: . dmf  - The fine mesh
2473: . isRefined - Flag indicating regular refinement, rather than the same topology
2474: - user - The user context

2476:   Output Parameter:
2477: . In  - The interpolation matrix

2479:   Level: developer

2481: .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorGeneral()`, `DMPlexComputeJacobianFEM()`
2482: @*/
2483: PetscErrorCode DMPlexComputeInterpolatorNested(DM dmc, DM dmf, PetscBool isRefined, Mat In, void *user)
2484: {
2485:   DM_Plex     *mesh = (DM_Plex *)dmc->data;
2486:   const char  *name = "Interpolator";
2487:   PetscFE     *feRef;
2488:   PetscFV     *fvRef;
2489:   PetscSection fsection, fglobalSection;
2490:   PetscSection csection, cglobalSection;
2491:   PetscScalar *elemMat;
2492:   PetscInt     dim, Nf, f, fieldI, fieldJ, offsetI, offsetJ, cStart, cEnd, c;
2493:   PetscInt     cTotDim = 0, rTotDim = 0;

2495:   PetscLogEventBegin(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0);
2496:   DMGetDimension(dmf, &dim);
2497:   DMGetLocalSection(dmf, &fsection);
2498:   DMGetGlobalSection(dmf, &fglobalSection);
2499:   DMGetLocalSection(dmc, &csection);
2500:   DMGetGlobalSection(dmc, &cglobalSection);
2501:   PetscSectionGetNumFields(fsection, &Nf);
2502:   DMPlexGetSimplexOrBoxCells(dmc, 0, &cStart, &cEnd);
2503:   PetscCalloc2(Nf, &feRef, Nf, &fvRef);
2504:   for (f = 0; f < Nf; ++f) {
2505:     PetscObject  obj, objc;
2506:     PetscClassId id, idc;
2507:     PetscInt     rNb = 0, Nc = 0, cNb = 0;

2509:     DMGetField(dmf, f, NULL, &obj);
2510:     PetscObjectGetClassId(obj, &id);
2511:     if (id == PETSCFE_CLASSID) {
2512:       PetscFE fe = (PetscFE)obj;

2514:       if (isRefined) {
2515:         PetscFERefine(fe, &feRef[f]);
2516:       } else {
2517:         PetscObjectReference((PetscObject)fe);
2518:         feRef[f] = fe;
2519:       }
2520:       PetscFEGetDimension(feRef[f], &rNb);
2521:       PetscFEGetNumComponents(fe, &Nc);
2522:     } else if (id == PETSCFV_CLASSID) {
2523:       PetscFV        fv = (PetscFV)obj;
2524:       PetscDualSpace Q;

2526:       if (isRefined) {
2527:         PetscFVRefine(fv, &fvRef[f]);
2528:       } else {
2529:         PetscObjectReference((PetscObject)fv);
2530:         fvRef[f] = fv;
2531:       }
2532:       PetscFVGetDualSpace(fvRef[f], &Q);
2533:       PetscDualSpaceGetDimension(Q, &rNb);
2534:       PetscFVGetDualSpace(fv, &Q);
2535:       PetscFVGetNumComponents(fv, &Nc);
2536:     }
2537:     DMGetField(dmc, f, NULL, &objc);
2538:     PetscObjectGetClassId(objc, &idc);
2539:     if (idc == PETSCFE_CLASSID) {
2540:       PetscFE fe = (PetscFE)objc;

2542:       PetscFEGetDimension(fe, &cNb);
2543:     } else if (id == PETSCFV_CLASSID) {
2544:       PetscFV        fv = (PetscFV)obj;
2545:       PetscDualSpace Q;

2547:       PetscFVGetDualSpace(fv, &Q);
2548:       PetscDualSpaceGetDimension(Q, &cNb);
2549:     }
2550:     rTotDim += rNb;
2551:     cTotDim += cNb;
2552:   }
2553:   PetscMalloc1(rTotDim * cTotDim, &elemMat);
2554:   PetscArrayzero(elemMat, rTotDim * cTotDim);
2555:   for (fieldI = 0, offsetI = 0; fieldI < Nf; ++fieldI) {
2556:     PetscDualSpace   Qref;
2557:     PetscQuadrature  f;
2558:     const PetscReal *qpoints, *qweights;
2559:     PetscReal       *points;
2560:     PetscInt         npoints = 0, Nc, Np, fpdim, i, k, p, d;

2562:     /* Compose points from all dual basis functionals */
2563:     if (feRef[fieldI]) {
2564:       PetscFEGetDualSpace(feRef[fieldI], &Qref);
2565:       PetscFEGetNumComponents(feRef[fieldI], &Nc);
2566:     } else {
2567:       PetscFVGetDualSpace(fvRef[fieldI], &Qref);
2568:       PetscFVGetNumComponents(fvRef[fieldI], &Nc);
2569:     }
2570:     PetscDualSpaceGetDimension(Qref, &fpdim);
2571:     for (i = 0; i < fpdim; ++i) {
2572:       PetscDualSpaceGetFunctional(Qref, i, &f);
2573:       PetscQuadratureGetData(f, NULL, NULL, &Np, NULL, NULL);
2574:       npoints += Np;
2575:     }
2576:     PetscMalloc1(npoints * dim, &points);
2577:     for (i = 0, k = 0; i < fpdim; ++i) {
2578:       PetscDualSpaceGetFunctional(Qref, i, &f);
2579:       PetscQuadratureGetData(f, NULL, NULL, &Np, &qpoints, NULL);
2580:       for (p = 0; p < Np; ++p, ++k)
2581:         for (d = 0; d < dim; ++d) points[k * dim + d] = qpoints[p * dim + d];
2582:     }

2584:     for (fieldJ = 0, offsetJ = 0; fieldJ < Nf; ++fieldJ) {
2585:       PetscObject  obj;
2586:       PetscClassId id;
2587:       PetscInt     NcJ = 0, cpdim = 0, j, qNc;

2589:       DMGetField(dmc, fieldJ, NULL, &obj);
2590:       PetscObjectGetClassId(obj, &id);
2591:       if (id == PETSCFE_CLASSID) {
2592:         PetscFE         fe = (PetscFE)obj;
2593:         PetscTabulation T  = NULL;

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

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

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

2699: PetscErrorCode DMPlexComputeMassMatrixNested(DM dmc, DM dmf, Mat mass, void *user)
2700: {
2701:   SETERRQ(PetscObjectComm((PetscObject)dmc), PETSC_ERR_SUP, "Laziness");
2702: }

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

2707:   Input Parameters:
2708: + dmf  - The fine mesh
2709: . dmc  - The coarse mesh
2710: - user - The user context

2712:   Output Parameter:
2713: . In  - The interpolation matrix

2715:   Level: developer

2717: .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorNested()`, `DMPlexComputeJacobianFEM()`
2718: @*/
2719: PetscErrorCode DMPlexComputeInterpolatorGeneral(DM dmc, DM dmf, Mat In, void *user)
2720: {
2721:   DM_Plex     *mesh = (DM_Plex *)dmf->data;
2722:   const char  *name = "Interpolator";
2723:   PetscDS      prob;
2724:   Mat          interp;
2725:   PetscSection fsection, globalFSection;
2726:   PetscSection csection, globalCSection;
2727:   PetscInt     locRows, locCols;
2728:   PetscReal   *x, *v0, *J, *invJ, detJ;
2729:   PetscReal   *v0c, *Jc, *invJc, detJc;
2730:   PetscScalar *elemMat;
2731:   PetscInt     dim, Nf, field, totDim, cStart, cEnd, cell, ccell, s;

2733:   PetscLogEventBegin(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0);
2734:   DMGetCoordinateDim(dmc, &dim);
2735:   DMGetDS(dmc, &prob);
2736:   PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL);
2737:   PetscDSGetNumFields(prob, &Nf);
2738:   PetscMalloc3(dim, &v0, dim * dim, &J, dim * dim, &invJ);
2739:   PetscMalloc3(dim, &v0c, dim * dim, &Jc, dim * dim, &invJc);
2740:   DMGetLocalSection(dmf, &fsection);
2741:   DMGetGlobalSection(dmf, &globalFSection);
2742:   DMGetLocalSection(dmc, &csection);
2743:   DMGetGlobalSection(dmc, &globalCSection);
2744:   DMPlexGetSimplexOrBoxCells(dmf, 0, &cStart, &cEnd);
2745:   PetscDSGetTotalDimension(prob, &totDim);
2746:   PetscMalloc1(totDim, &elemMat);

2748:   MatGetLocalSize(In, &locRows, &locCols);
2749:   MatCreate(PetscObjectComm((PetscObject)In), &interp);
2750:   MatSetType(interp, MATPREALLOCATOR);
2751:   MatSetSizes(interp, locRows, locCols, PETSC_DETERMINE, PETSC_DETERMINE);
2752:   MatSetUp(interp);
2753:   for (s = 0; s < 2; ++s) {
2754:     for (field = 0; field < Nf; ++field) {
2755:       PetscObject      obj;
2756:       PetscClassId     id;
2757:       PetscDualSpace   Q = NULL;
2758:       PetscTabulation  T = NULL;
2759:       PetscQuadrature  f;
2760:       const PetscReal *qpoints, *qweights;
2761:       PetscInt         Nc, qNc, Np, fpdim, off, i, d;

2763:       PetscDSGetFieldOffset(prob, field, &off);
2764:       PetscDSGetDiscretization(prob, field, &obj);
2765:       PetscObjectGetClassId(obj, &id);
2766:       if (id == PETSCFE_CLASSID) {
2767:         PetscFE fe = (PetscFE)obj;

2769:         PetscFEGetDualSpace(fe, &Q);
2770:         PetscFEGetNumComponents(fe, &Nc);
2771:         if (s) PetscFECreateTabulation(fe, 1, 1, x, 0, &T);
2772:       } else if (id == PETSCFV_CLASSID) {
2773:         PetscFV fv = (PetscFV)obj;

2775:         PetscFVGetDualSpace(fv, &Q);
2776:         Nc = 1;
2777:       } else SETERRQ(PetscObjectComm((PetscObject)dmc), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
2778:       PetscDualSpaceGetDimension(Q, &fpdim);
2779:       /* For each fine grid cell */
2780:       for (cell = cStart; cell < cEnd; ++cell) {
2781:         PetscInt *findices, *cindices;
2782:         PetscInt  numFIndices, numCIndices;

2784:         DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL);
2785:         DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ);
2787:         for (i = 0; i < fpdim; ++i) {
2788:           Vec                pointVec;
2789:           PetscScalar       *pV;
2790:           PetscSF            coarseCellSF = NULL;
2791:           const PetscSFNode *coarseCells;
2792:           PetscInt           numCoarseCells, cpdim, row = findices[i + off], q, c, j;

2794:           /* Get points from the dual basis functional quadrature */
2795:           PetscDualSpaceGetFunctional(Q, i, &f);
2796:           PetscQuadratureGetData(f, NULL, &qNc, &Np, &qpoints, &qweights);
2798:           VecCreateSeq(PETSC_COMM_SELF, Np * dim, &pointVec);
2799:           VecSetBlockSize(pointVec, dim);
2800:           VecGetArray(pointVec, &pV);
2801:           for (q = 0; q < Np; ++q) {
2802:             const PetscReal xi0[3] = {-1., -1., -1.};

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

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

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

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

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

2878:   Input Parameters:
2879: + dmf  - The fine mesh
2880: . dmc  - The coarse mesh
2881: - user - The user context

2883:   Output Parameter:
2884: . mass  - The mass matrix

2886:   Level: developer

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

2905:   DMGetCoordinateDim(dmc, &dim);
2906:   DMGetDS(dmc, &prob);
2907:   PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL);
2908:   PetscDSGetNumFields(prob, &Nf);
2909:   PetscMalloc3(dim, &v0, dim * dim, &J, dim * dim, &invJ);
2910:   PetscMalloc3(dim, &v0c, dim * dim, &Jc, dim * dim, &invJc);
2911:   DMGetLocalSection(dmf, &fsection);
2912:   DMGetGlobalSection(dmf, &globalFSection);
2913:   DMGetLocalSection(dmc, &csection);
2914:   DMGetGlobalSection(dmc, &globalCSection);
2915:   DMPlexGetHeightStratum(dmf, 0, &cStart, &cEnd);
2916:   PetscDSGetTotalDimension(prob, &totDim);
2917:   PetscMalloc1(totDim, &elemMat);

2919:   MatGetLocalSize(mass, &locRows, NULL);
2920:   PetscLayoutCreate(PetscObjectComm((PetscObject)mass), &rLayout);
2921:   PetscLayoutSetLocalSize(rLayout, locRows);
2922:   PetscLayoutSetBlockSize(rLayout, 1);
2923:   PetscLayoutSetUp(rLayout);
2924:   PetscLayoutGetRange(rLayout, &rStart, &rEnd);
2925:   PetscLayoutDestroy(&rLayout);
2926:   PetscCalloc2(locRows, &dnz, locRows, &onz);
2927:   PetscHSetIJCreate(&ht);
2928:   for (field = 0; field < Nf; ++field) {
2929:     PetscObject      obj;
2930:     PetscClassId     id;
2931:     PetscQuadrature  quad;
2932:     const PetscReal *qpoints;
2933:     PetscInt         Nq, Nc, i, d;

2935:     PetscDSGetDiscretization(prob, field, &obj);
2936:     PetscObjectGetClassId(obj, &id);
2937:     if (id == PETSCFE_CLASSID) PetscFEGetQuadrature((PetscFE)obj, &quad);
2938:     else PetscFVGetQuadrature((PetscFV)obj, &quad);
2939:     PetscQuadratureGetData(quad, NULL, &Nc, &Nq, &qpoints, NULL);
2940:     /* For each fine grid cell */
2941:     for (cell = cStart; cell < cEnd; ++cell) {
2942:       Vec                pointVec;
2943:       PetscScalar       *pV;
2944:       PetscSF            coarseCellSF = NULL;
2945:       const PetscSFNode *coarseCells;
2946:       PetscInt           numCoarseCells, q, c;
2947:       PetscInt          *findices, *cindices;
2948:       PetscInt           numFIndices, numCIndices;

2950:       DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL);
2951:       DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ);
2952:       /* Get points from the quadrature */
2953:       VecCreateSeq(PETSC_COMM_SELF, Nq * dim, &pointVec);
2954:       VecSetBlockSize(pointVec, dim);
2955:       VecGetArray(pointVec, &pV);
2956:       for (q = 0; q < Nq; ++q) {
2957:         const PetscReal xi0[3] = {-1., -1., -1.};

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

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

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

3031:       DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL);
3032:       DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ);
3033:       /* Get points from the quadrature */
3034:       VecCreateSeq(PETSC_COMM_SELF, Nq * dim, &pointVec);
3035:       VecSetBlockSize(pointVec, dim);
3036:       VecGetArray(pointVec, &pV);
3037:       for (q = 0; q < Nq; ++q) {
3038:         const PetscReal xi0[3] = {-1., -1., -1.};

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

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

3061:         if (id == PETSCFE_CLASSID) {
3062:           PetscFE fe = (PetscFE)obj;

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

3109: /*@
3110:   DMPlexComputeInjectorFEM - Compute a mapping from coarse unknowns to fine unknowns

3112:   Input Parameters:
3113: + dmc  - The coarse mesh
3114: - dmf  - The fine mesh
3115: - user - The user context

3117:   Output Parameter:
3118: . sc   - The mapping

3120:   Level: developer

3122: .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorNested()`, `DMPlexComputeJacobianFEM()`
3123: @*/
3124: PetscErrorCode DMPlexComputeInjectorFEM(DM dmc, DM dmf, VecScatter *sc, void *user)
3125: {
3126:   PetscDS      prob;
3127:   PetscFE     *feRef;
3128:   PetscFV     *fvRef;
3129:   Vec          fv, cv;
3130:   IS           fis, cis;
3131:   PetscSection fsection, fglobalSection, csection, cglobalSection;
3132:   PetscInt    *cmap, *cellCIndices, *cellFIndices, *cindices, *findices;
3133:   PetscInt     cTotDim, fTotDim = 0, Nf, f, field, cStart, cEnd, c, dim, d, startC, endC, offsetC, offsetF, m;
3134:   PetscBool   *needAvg;

3136:   PetscLogEventBegin(DMPLEX_InjectorFEM, dmc, dmf, 0, 0);
3137:   DMGetDimension(dmf, &dim);
3138:   DMGetLocalSection(dmf, &fsection);
3139:   DMGetGlobalSection(dmf, &fglobalSection);
3140:   DMGetLocalSection(dmc, &csection);
3141:   DMGetGlobalSection(dmc, &cglobalSection);
3142:   PetscSectionGetNumFields(fsection, &Nf);
3143:   DMPlexGetSimplexOrBoxCells(dmc, 0, &cStart, &cEnd);
3144:   DMGetDS(dmc, &prob);
3145:   PetscCalloc3(Nf, &feRef, Nf, &fvRef, Nf, &needAvg);
3146:   for (f = 0; f < Nf; ++f) {
3147:     PetscObject  obj;
3148:     PetscClassId id;
3149:     PetscInt     fNb = 0, Nc = 0;

3151:     PetscDSGetDiscretization(prob, f, &obj);
3152:     PetscObjectGetClassId(obj, &id);
3153:     if (id == PETSCFE_CLASSID) {
3154:       PetscFE    fe = (PetscFE)obj;
3155:       PetscSpace sp;
3156:       PetscInt   maxDegree;

3158:       PetscFERefine(fe, &feRef[f]);
3159:       PetscFEGetDimension(feRef[f], &fNb);
3160:       PetscFEGetNumComponents(fe, &Nc);
3161:       PetscFEGetBasisSpace(fe, &sp);
3162:       PetscSpaceGetDegree(sp, NULL, &maxDegree);
3163:       if (!maxDegree) needAvg[f] = PETSC_TRUE;
3164:     } else if (id == PETSCFV_CLASSID) {
3165:       PetscFV        fv = (PetscFV)obj;
3166:       PetscDualSpace Q;

3168:       PetscFVRefine(fv, &fvRef[f]);
3169:       PetscFVGetDualSpace(fvRef[f], &Q);
3170:       PetscDualSpaceGetDimension(Q, &fNb);
3171:       PetscFVGetNumComponents(fv, &Nc);
3172:       needAvg[f] = PETSC_TRUE;
3173:     }
3174:     fTotDim += fNb;
3175:   }
3176:   PetscDSGetTotalDimension(prob, &cTotDim);
3177:   PetscMalloc1(cTotDim, &cmap);
3178:   for (field = 0, offsetC = 0, offsetF = 0; field < Nf; ++field) {
3179:     PetscFE        feC;
3180:     PetscFV        fvC;
3181:     PetscDualSpace QF, QC;
3182:     PetscInt       order = -1, NcF, NcC, fpdim, cpdim;

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

3209:       PetscDualSpaceGetFunctional(QC, c, &cfunc);
3210:       PetscQuadratureGetData(cfunc, NULL, &NqcC, &NpC, &cqpoints, &cqweights);
3213:       for (f = 0; f < fpdim; ++f) {
3214:         PetscQuadrature  ffunc;
3215:         const PetscReal *fqpoints, *fqweights;
3216:         PetscReal        sum = 0.0;
3217:         PetscInt         NqcF, NpF;

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

3247:   DMGetGlobalVector(dmf, &fv);
3248:   DMGetGlobalVector(dmc, &cv);
3249:   VecGetOwnershipRange(cv, &startC, &endC);
3250:   PetscSectionGetConstrainedStorageSize(cglobalSection, &m);
3251:   PetscMalloc2(cTotDim, &cellCIndices, fTotDim, &cellFIndices);
3252:   PetscMalloc1(m, &cindices);
3253:   PetscMalloc1(m, &findices);
3254:   for (d = 0; d < m; ++d) cindices[d] = findices[d] = -1;
3255:   for (c = cStart; c < cEnd; ++c) {
3256:     DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, c, cellCIndices, cellFIndices);
3257:     for (d = 0; d < cTotDim; ++d) {
3258:       if ((cellCIndices[d] < startC) || (cellCIndices[d] >= endC)) continue;
3260:       cindices[cellCIndices[d] - startC] = cellCIndices[d];
3261:       findices[cellCIndices[d] - startC] = cellFIndices[cmap[d]];
3262:     }
3263:   }
3264:   PetscFree(cmap);
3265:   PetscFree2(cellCIndices, cellFIndices);

3267:   ISCreateGeneral(PETSC_COMM_SELF, m, cindices, PETSC_OWN_POINTER, &cis);
3268:   ISCreateGeneral(PETSC_COMM_SELF, m, findices, PETSC_OWN_POINTER, &fis);
3269:   VecScatterCreate(cv, cis, fv, fis, sc);
3270:   ISDestroy(&cis);
3271:   ISDestroy(&fis);
3272:   DMRestoreGlobalVector(dmf, &fv);
3273:   DMRestoreGlobalVector(dmc, &cv);
3274:   PetscLogEventEnd(DMPLEX_InjectorFEM, dmc, dmf, 0, 0);
3275:   return 0;
3276: }

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

3281:   Input Parameters:
3282: + dm     - The `DM`
3283: . cellIS - The cells to include
3284: . locX   - A local vector with the solution fields
3285: . locX_t - A local vector with solution field time derivatives, or NULL
3286: - locA   - A local vector with auxiliary fields, or NULL

3288:   Output Parameters:
3289: + u   - The field coefficients
3290: . u_t - The fields derivative coefficients
3291: - a   - The auxiliary field coefficients

3293:   Level: developer

3295: .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
3296: @*/
3297: PetscErrorCode DMPlexGetCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
3298: {
3299:   DM              plex, plexA = NULL;
3300:   DMEnclosureType encAux;
3301:   PetscSection    section, sectionAux;
3302:   PetscDS         prob;
3303:   const PetscInt *cells;
3304:   PetscInt        cStart, cEnd, numCells, totDim, totDimAux, c;

3313:   DMPlexConvertPlex(dm, &plex, PETSC_FALSE);
3314:   ISGetPointRange(cellIS, &cStart, &cEnd, &cells);
3315:   DMGetLocalSection(dm, &section);
3316:   DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob);
3317:   PetscDSGetTotalDimension(prob, &totDim);
3318:   if (locA) {
3319:     DM      dmAux;
3320:     PetscDS probAux;

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

3341:     DMPlexVecGetClosure(plex, section, locX, cell, NULL, &x);
3342:     for (i = 0; i < totDim; ++i) ul[cind * totDim + i] = x[i];
3343:     DMPlexVecRestoreClosure(plex, section, locX, cell, NULL, &x);
3344:     if (locX_t) {
3345:       DMPlexVecGetClosure(plex, section, locX_t, cell, NULL, &x_t);
3346:       for (i = 0; i < totDim; ++i) ul_t[cind * totDim + i] = x_t[i];
3347:       DMPlexVecRestoreClosure(plex, section, locX_t, cell, NULL, &x_t);
3348:     }
3349:     if (locA) {
3350:       PetscInt subcell;
3351:       DMGetEnclosurePoint(plexA, dm, encAux, cell, &subcell);
3352:       DMPlexVecGetClosure(plexA, sectionAux, locA, subcell, NULL, &x);
3353:       for (i = 0; i < totDimAux; ++i) al[cind * totDimAux + i] = x[i];
3354:       DMPlexVecRestoreClosure(plexA, sectionAux, locA, subcell, NULL, &x);
3355:     }
3356:   }
3357:   DMDestroy(&plex);
3358:   if (locA) DMDestroy(&plexA);
3359:   ISRestorePointRange(cellIS, &cStart, &cEnd, &cells);
3360:   return 0;
3361: }

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

3366:   Input Parameters:
3367: + dm     - The `DM`
3368: . cellIS - The cells to include
3369: . locX   - A local vector with the solution fields
3370: . locX_t - A local vector with solution field time derivatives, or NULL
3371: - locA   - A local vector with auxiliary fields, or NULL

3373:   Output Parameters:
3374: + u   - The field coefficients
3375: . u_t - The fields derivative coefficients
3376: - a   - The auxiliary field coefficients

3378:   Level: developer

3380: .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
3381: @*/
3382: PetscErrorCode DMPlexRestoreCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
3383: {
3384:   DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u);
3385:   if (locX_t) DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u_t);
3386:   if (locA) DMRestoreWorkArray(dm, 0, MPIU_SCALAR, a);
3387:   return 0;
3388: }

3390: /*
3391:   Get the auxiliary field vectors for the negative side (s = 0) and positive side (s = 1) of the interfaace
3392: */
3393: static PetscErrorCode DMPlexGetHybridAuxFields(DM dm, DM dmAux[], PetscDS dsAux[], IS cellIS, Vec locA[], PetscScalar *a[])
3394: {
3395:   DM              plexA[2];
3396:   DMEnclosureType encAux[2];
3397:   PetscSection    sectionAux[2];
3398:   const PetscInt *cells;
3399:   PetscInt        cStart, cEnd, numCells, c, s, totDimAux[2];

3402:   if (!locA[0] || !locA[1]) return 0;
3406:   ISGetPointRange(cellIS, &cStart, &cEnd, &cells);
3407:   numCells = cEnd - cStart;
3408:   for (s = 0; s < 2; ++s) {
3412:     DMPlexConvertPlex(dmAux[s], &plexA[s], PETSC_FALSE);
3413:     DMGetEnclosureRelation(dmAux[s], dm, &encAux[s]);
3414:     DMGetLocalSection(dmAux[s], &sectionAux[s]);
3415:     PetscDSGetTotalDimension(dsAux[s], &totDimAux[s]);
3416:     DMGetWorkArray(dmAux[s], numCells * totDimAux[s], MPIU_SCALAR, &a[s]);
3417:   }
3418:   for (c = cStart; c < cEnd; ++c) {
3419:     const PetscInt  cell = cells ? cells[c] : c;
3420:     const PetscInt  cind = c - cStart;
3421:     const PetscInt *cone, *ornt;

3423:     DMPlexGetCone(dm, cell, &cone);
3424:     DMPlexGetConeOrientation(dm, cell, &ornt);
3425:     for (s = 0; s < 2; ++s) {
3426:       const PetscInt *support;
3427:       PetscScalar    *x = NULL, *al = a[s];
3428:       const PetscInt  tdA = totDimAux[s];
3429:       PetscInt        ssize, scell;
3430:       PetscInt        subface, Na, i;

3432:       DMPlexGetSupport(dm, cone[s], &support);
3433:       DMPlexGetSupportSize(dm, cone[s], &ssize);
3435:       if (support[0] == cell) scell = support[1];
3436:       else if (support[1] == cell) scell = support[0];
3437:       else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[s], cell);

3439:       DMGetEnclosurePoint(plexA[s], dm, encAux[s], scell, &subface);
3440:       DMPlexVecGetClosure(plexA[s], sectionAux[s], locA[s], subface, &Na, &x);
3441:       for (i = 0; i < Na; ++i) al[cind * tdA + i] = x[i];
3442:       DMPlexVecRestoreClosure(plexA[s], sectionAux[s], locA[s], subface, &Na, &x);
3443:     }
3444:   }
3445:   for (s = 0; s < 2; ++s) DMDestroy(&plexA[s]);
3446:   ISRestorePointRange(cellIS, &cStart, &cEnd, &cells);
3447:   return 0;
3448: }

3450: static PetscErrorCode DMPlexRestoreHybridAuxFields(DM dmAux[], PetscDS dsAux[], IS cellIS, Vec locA[], PetscScalar *a[])
3451: {
3452:   if (!locA[0] || !locA[1]) return 0;
3453:   DMRestoreWorkArray(dmAux[0], 0, MPIU_SCALAR, &a[0]);
3454:   DMRestoreWorkArray(dmAux[1], 0, MPIU_SCALAR, &a[1]);
3455:   return 0;
3456: }

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

3461:   Input Parameters:
3462: + dm     - The `DM`
3463: . fStart - The first face to include
3464: . fEnd   - The first face to exclude
3465: . locX   - A local vector with the solution fields
3466: . locX_t - A local vector with solution field time derivatives, or NULL
3467: . faceGeometry - A local vector with face geometry
3468: . cellGeometry - A local vector with cell geometry
3469: - locaGrad - A local vector with field gradients, or NULL

3471:   Output Parameters:
3472: + Nface - The number of faces with field values
3473: . uL - The field values at the left side of the face
3474: - uR - The field values at the right side of the face

3476:   Level: developer

3478: .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellFields()`
3479: @*/
3480: 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)
3481: {
3482:   DM                 dmFace, dmCell, dmGrad = NULL;
3483:   PetscSection       section;
3484:   PetscDS            prob;
3485:   DMLabel            ghostLabel;
3486:   const PetscScalar *facegeom, *cellgeom, *x, *lgrad;
3487:   PetscBool         *isFE;
3488:   PetscInt           dim, Nf, f, Nc, numFaces = fEnd - fStart, iface, face;

3498:   DMGetDimension(dm, &dim);
3499:   DMGetDS(dm, &prob);
3500:   DMGetLocalSection(dm, &section);
3501:   PetscDSGetNumFields(prob, &Nf);
3502:   PetscDSGetTotalComponents(prob, &Nc);
3503:   PetscMalloc1(Nf, &isFE);
3504:   for (f = 0; f < Nf; ++f) {
3505:     PetscObject  obj;
3506:     PetscClassId id;

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

3539:     DMLabelGetValue(ghostLabel, face, &ghost);
3540:     DMPlexGetSupportSize(dm, face, &nsupp);
3541:     DMPlexGetTreeChildren(dm, face, &nchild, NULL);
3542:     if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
3543:     DMPlexPointLocalRead(dmFace, face, facegeom, &fg);
3544:     DMPlexGetSupport(dm, face, &cells);
3545:     DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL);
3546:     DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR);
3547:     for (f = 0; f < Nf; ++f) {
3548:       PetscInt off;

3550:       PetscDSGetComponentOffset(prob, f, &off);
3551:       if (isFE[f]) {
3552:         const PetscInt *cone;
3553:         PetscInt        comp, coneSizeL, coneSizeR, faceLocL, faceLocR, ldof, rdof, d;

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

3587:         PetscDSGetDiscretization(prob, f, (PetscObject *)&fv);
3588:         PetscFVGetNumComponents(fv, &numComp);
3589:         DMPlexPointLocalFieldRead(dm, cells[0], f, x, &xL);
3590:         DMPlexPointLocalFieldRead(dm, cells[1], f, x, &xR);
3591:         if (dmGrad) {
3592:           PetscReal dxL[3], dxR[3];

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

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

3624:   Input Parameters:
3625: + dm     - The `DM`
3626: . fStart - The first face to include
3627: . fEnd   - The first face to exclude
3628: . locX   - A local vector with the solution fields
3629: . locX_t - A local vector with solution field time derivatives, or NULL
3630: . faceGeometry - A local vector with face geometry
3631: . cellGeometry - A local vector with cell geometry
3632: - locaGrad - A local vector with field gradients, or NULL

3634:   Output Parameters:
3635: + Nface - The number of faces with field values
3636: . uL - The field values at the left side of the face
3637: - uR - The field values at the right side of the face

3639:   Level: developer

3641: .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
3642: @*/
3643: 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)
3644: {
3645:   DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uL);
3646:   DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uR);
3647:   return 0;
3648: }

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

3653:   Input Parameters:
3654: + dm     - The `DM`
3655: . fStart - The first face to include
3656: . fEnd   - The first face to exclude
3657: . faceGeometry - A local vector with face geometry
3658: - cellGeometry - A local vector with cell geometry

3660:   Output Parameters:
3661: + Nface - The number of faces with field values
3662: . fgeom - The extract the face centroid and normal
3663: - vol   - The cell volume

3665:   Level: developer

3667: .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellFields()`
3668: @*/
3669: PetscErrorCode DMPlexGetFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom **fgeom, PetscReal **vol)
3670: {
3671:   DM                 dmFace, dmCell;
3672:   DMLabel            ghostLabel;
3673:   const PetscScalar *facegeom, *cellgeom;
3674:   PetscInt           dim, numFaces = fEnd - fStart, iface, face;

3681:   DMGetDimension(dm, &dim);
3682:   DMGetLabel(dm, "ghost", &ghostLabel);
3683:   VecGetDM(faceGeometry, &dmFace);
3684:   VecGetArrayRead(faceGeometry, &facegeom);
3685:   VecGetDM(cellGeometry, &dmCell);
3686:   VecGetArrayRead(cellGeometry, &cellgeom);
3687:   PetscMalloc1(numFaces, fgeom);
3688:   DMGetWorkArray(dm, numFaces * 2, MPIU_SCALAR, vol);
3689:   for (face = fStart, iface = 0; face < fEnd; ++face) {
3690:     const PetscInt  *cells;
3691:     PetscFVFaceGeom *fg;
3692:     PetscFVCellGeom *cgL, *cgR;
3693:     PetscFVFaceGeom *fgeoml = *fgeom;
3694:     PetscReal       *voll   = *vol;
3695:     PetscInt         ghost, d, nchild, nsupp;

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

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

3722:   Input Parameters:
3723: + dm     - The `DM`
3724: . fStart - The first face to include
3725: . fEnd   - The first face to exclude
3726: . faceGeometry - A local vector with face geometry
3727: - cellGeometry - A local vector with cell geometry

3729:   Output Parameters:
3730: + Nface - The number of faces with field values
3731: . fgeom - The extract the face centroid and normal
3732: - vol   - The cell volume

3734:   Level: developer

3736: .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
3737: @*/
3738: PetscErrorCode DMPlexRestoreFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom **fgeom, PetscReal **vol)
3739: {
3740:   PetscFree(*fgeom);
3741:   DMRestoreWorkArray(dm, 0, MPIU_REAL, vol);
3742:   return 0;
3743: }

3745: PetscErrorCode DMSNESGetFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
3746: {
3747:   char           composeStr[33] = {0};
3748:   PetscObjectId  id;
3749:   PetscContainer container;

3751:   PetscObjectGetId((PetscObject)quad, &id);
3752:   PetscSNPrintf(composeStr, 32, "DMSNESGetFEGeom_%" PetscInt64_FMT "\n", id);
3753:   PetscObjectQuery((PetscObject)pointIS, composeStr, (PetscObject *)&container);
3754:   if (container) {
3755:     PetscContainerGetPointer(container, (void **)geom);
3756:   } else {
3757:     DMFieldCreateFEGeom(coordField, pointIS, quad, faceData, geom);
3758:     PetscContainerCreate(PETSC_COMM_SELF, &container);
3759:     PetscContainerSetPointer(container, (void *)*geom);
3760:     PetscContainerSetUserDestroy(container, PetscContainerUserDestroy_PetscFEGeom);
3761:     PetscObjectCompose((PetscObject)pointIS, composeStr, (PetscObject)container);
3762:     PetscContainerDestroy(&container);
3763:   }
3764:   return 0;
3765: }

3767: PetscErrorCode DMSNESRestoreFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
3768: {
3769:   *geom = NULL;
3770:   return 0;
3771: }

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

3795:   PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0);
3796:   /* FEM+FVM */
3797:   /* 1: Get sizes from dm and dmAux */
3798:   DMGetLabel(dm, "ghost", &ghostLabel);
3799:   DMGetDS(dm, &prob);
3800:   PetscDSGetNumFields(prob, &Nf);
3801:   PetscDSGetTotalDimension(prob, &totDim);
3802:   DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA);
3803:   if (locA) {
3804:     VecGetDM(locA, &dmAux);
3805:     DMGetDS(dmAux, &probAux);
3806:     PetscDSGetTotalDimension(probAux, &totDimAux);
3807:   }
3808:   /* 2: Get geometric data */
3809:   for (f = 0; f < Nf; ++f) {
3810:     PetscObject  obj;
3811:     PetscClassId id;
3812:     PetscBool    fimp;

3814:     PetscDSGetImplicit(prob, f, &fimp);
3815:     if (isImplicit != fimp) continue;
3816:     PetscDSGetDiscretization(prob, f, &obj);
3817:     PetscObjectGetClassId(obj, &id);
3818:     if (id == PETSCFE_CLASSID) useFEM = PETSC_TRUE;
3820:   }
3821:   if (useFEM) {
3822:     DMGetCoordinateField(dm, &coordField);
3823:     DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree);
3824:     if (maxDegree <= 1) {
3825:       DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad);
3826:       if (affineQuad) DMSNESGetFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom);
3827:     } else {
3828:       PetscCalloc2(Nf, &quads, Nf, &geoms);
3829:       for (f = 0; f < Nf; ++f) {
3830:         PetscObject  obj;
3831:         PetscClassId id;
3832:         PetscBool    fimp;

3834:         PetscDSGetImplicit(prob, f, &fimp);
3835:         if (isImplicit != fimp) continue;
3836:         PetscDSGetDiscretization(prob, f, &obj);
3837:         PetscObjectGetClassId(obj, &id);
3838:         if (id == PETSCFE_CLASSID) {
3839:           PetscFE fe = (PetscFE)obj;

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

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

3881:       key.field = f;
3882:       PetscDSGetImplicit(prob, f, &fimp);
3883:       if (isImplicit != fimp) continue;
3884:       PetscDSGetDiscretization(prob, f, &obj);
3885:       PetscObjectGetClassId(obj, &id);
3886:       if (id == PETSCFE_CLASSID) {
3887:         PetscFE         fe        = (PetscFE)obj;
3888:         PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[f];
3889:         PetscFEGeom    *chunkGeom = NULL;
3890:         PetscQuadrature quad      = affineQuad ? affineQuad : quads[f];
3891:         PetscInt        Nq, Nb;

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

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

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

3930:           DMLabelGetValue(ghostLabel, cell, &ghostVal);
3931:           if (ghostVal > 0) continue;
3932:         }
3933:         DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind * totDim], ADD_ALL_VALUES);
3934:       }
3935:     }
3936:     /* Handle time derivative */
3937:     if (locX_t) {
3938:       PetscScalar *x_t, *fa;

3940:       VecGetArray(locF, &fa);
3941:       VecGetArray(locX_t, &x_t);
3942:       for (f = 0; f < Nf; ++f) {
3943:         PetscFV      fv;
3944:         PetscObject  obj;
3945:         PetscClassId id;
3946:         PetscInt     pdim, d;

3948:         PetscDSGetDiscretization(prob, f, &obj);
3949:         PetscObjectGetClassId(obj, &id);
3950:         if (id != PETSCFV_CLASSID) continue;
3951:         fv = (PetscFV)obj;
3952:         PetscFVGetNumComponents(fv, &pdim);
3953:         for (c = cS; c < cE; ++c) {
3954:           const PetscInt cell = cells ? cells[c] : c;
3955:           PetscScalar   *u_t, *r;

3957:           if (ghostLabel) {
3958:             PetscInt ghostVal;

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

3995: /*
3996:   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

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

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

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

4091:     PetscDSGetDiscretization(prob, 0, (PetscObject *)&fe);
4092:     PetscFEGetQuadrature(fe, &qGeom);
4093:     PetscObjectReference((PetscObject)qGeom);
4094:   }
4095:   DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM);
4096:   /* Compute volume integrals */
4097:   if (assembleJac) MatZeroEntries(J);
4098:   MatZeroEntries(JP);
4099:   key.label = NULL;
4100:   key.value = 0;
4101:   key.part  = 0;
4102:   for (chunk = 0; chunk < numChunks; ++chunk, offCell += chunkSize) {
4103:     const PetscInt Ncell = PetscMin(chunkSize, numCells - offCell);
4104:     PetscInt       c;

4106:     /* Extract values */
4107:     for (c = 0; c < Ncell; ++c) {
4108:       const PetscInt cell = cells ? cells[c + offCell] : c + offCell;
4109:       PetscScalar   *x = NULL, *x_t = NULL;
4110:       PetscInt       i;

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

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

4188: /******** FEM Assembly Function ********/

4190: static PetscErrorCode DMConvertPlex_Internal(DM dm, DM *plex, PetscBool copy)
4191: {
4192:   PetscBool isPlex;

4194:   PetscObjectTypeCompare((PetscObject)dm, DMPLEX, &isPlex);
4195:   if (isPlex) {
4196:     *plex = dm;
4197:     PetscObjectReference((PetscObject)dm);
4198:   } else {
4199:     PetscObjectQuery((PetscObject)dm, "dm_plex", (PetscObject *)plex);
4200:     if (!*plex) {
4201:       DMConvert(dm, DMPLEX, plex);
4202:       PetscObjectCompose((PetscObject)dm, "dm_plex", (PetscObject)*plex);
4203:       if (copy) DMCopyAuxiliaryVec(dm, *plex);
4204:     } else {
4205:       PetscObjectReference((PetscObject)*plex);
4206:     }
4207:   }
4208:   return 0;
4209: }

4211: /*@
4212:   DMPlexGetGeometryFVM - Return precomputed geometric data

4214:   Collective on dm

4216:   Input Parameter:
4217: . dm - The `DM`

4219:   Output Parameters:
4220: + facegeom - The values precomputed from face geometry
4221: . cellgeom - The values precomputed from cell geometry
4222: - minRadius - The minimum radius over the mesh of an inscribed sphere in a cell

4224:   Level: developer

4226: .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMTSSetRHSFunctionLocal()`
4227: @*/
4228: PetscErrorCode DMPlexGetGeometryFVM(DM dm, Vec *facegeom, Vec *cellgeom, PetscReal *minRadius)
4229: {
4230:   DM plex;

4233:   DMConvertPlex_Internal(dm, &plex, PETSC_TRUE);
4234:   DMPlexGetDataFVM(plex, NULL, cellgeom, facegeom, NULL);
4235:   if (minRadius) DMPlexGetMinRadius(plex, minRadius);
4236:   DMDestroy(&plex);
4237:   return 0;
4238: }

4240: /*@
4241:   DMPlexGetGradientDM - Return gradient data layout

4243:   Collective on dm

4245:   Input Parameters:
4246: + dm - The `DM`
4247: - fv - The PetscFV

4249:   Output Parameter:
4250: . dmGrad - The layout for gradient values

4252:   Level: developer

4254: .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetGeometryFVM()`
4255: @*/
4256: PetscErrorCode DMPlexGetGradientDM(DM dm, PetscFV fv, DM *dmGrad)
4257: {
4258:   DM        plex;
4259:   PetscBool computeGradients;

4264:   PetscFVGetComputeGradients(fv, &computeGradients);
4265:   if (!computeGradients) {
4266:     *dmGrad = NULL;
4267:     return 0;
4268:   }
4269:   DMConvertPlex_Internal(dm, &plex, PETSC_TRUE);
4270:   DMPlexGetDataFVM(plex, fv, NULL, NULL, dmGrad);
4271:   DMDestroy(&plex);
4272:   return 0;
4273: }

4275: static PetscErrorCode DMPlexComputeBdResidual_Single_Internal(DM dm, PetscReal t, PetscWeakForm wf, PetscFormKey key, Vec locX, Vec locX_t, Vec locF, DMField coordField, IS facetIS)
4276: {
4277:   DM_Plex        *mesh = (DM_Plex *)dm->data;
4278:   DM              plex = NULL, plexA = NULL;
4279:   DMEnclosureType encAux;
4280:   PetscDS         prob, probAux       = NULL;
4281:   PetscSection    section, sectionAux = NULL;
4282:   Vec             locA = NULL;
4283:   PetscScalar    *u = NULL, *u_t = NULL, *a = NULL, *elemVec = NULL;
4284:   PetscInt        totDim, totDimAux = 0;

4286:   DMConvert(dm, DMPLEX, &plex);
4287:   DMGetLocalSection(dm, &section);
4288:   DMGetDS(dm, &prob);
4289:   PetscDSGetTotalDimension(prob, &totDim);
4290:   DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &locA);
4291:   if (locA) {
4292:     DM dmAux;

4294:     VecGetDM(locA, &dmAux);
4295:     DMGetEnclosureRelation(dmAux, dm, &encAux);
4296:     DMConvert(dmAux, DMPLEX, &plexA);
4297:     DMGetDS(plexA, &probAux);
4298:     PetscDSGetTotalDimension(probAux, &totDimAux);
4299:     DMGetLocalSection(plexA, &sectionAux);
4300:   }
4301:   {
4302:     PetscFEGeom    *fgeom;
4303:     PetscInt        maxDegree;
4304:     PetscQuadrature qGeom = NULL;
4305:     IS              pointIS;
4306:     const PetscInt *points;
4307:     PetscInt        numFaces, face, Nq;

4309:     DMLabelGetStratumIS(key.label, key.value, &pointIS);
4310:     if (!pointIS) goto end; /* No points with that id on this process */
4311:     {
4312:       IS isectIS;

4314:       /* TODO: Special cases of ISIntersect where it is quick to check a priori if one is a superset of the other */
4315:       ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS);
4316:       ISDestroy(&pointIS);
4317:       pointIS = isectIS;
4318:     }
4319:     ISGetLocalSize(pointIS, &numFaces);
4320:     ISGetIndices(pointIS, &points);
4321:     PetscMalloc4(numFaces * totDim, &u, locX_t ? numFaces * totDim : 0, &u_t, numFaces * totDim, &elemVec, locA ? numFaces * totDimAux : 0, &a);
4322:     DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree);
4323:     if (maxDegree <= 1) DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom);
4324:     if (!qGeom) {
4325:       PetscFE fe;

4327:       PetscDSGetDiscretization(prob, key.field, (PetscObject *)&fe);
4328:       PetscFEGetFaceQuadrature(fe, &qGeom);
4329:       PetscObjectReference((PetscObject)qGeom);
4330:     }
4331:     PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL);
4332:     DMSNESGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom);
4333:     for (face = 0; face < numFaces; ++face) {
4334:       const PetscInt point = points[face], *support;
4335:       PetscScalar   *x     = NULL;
4336:       PetscInt       i;

4338:       DMPlexGetSupport(dm, point, &support);
4339:       DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x);
4340:       for (i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
4341:       DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x);
4342:       if (locX_t) {
4343:         DMPlexVecGetClosure(plex, section, locX_t, support[0], NULL, &x);
4344:         for (i = 0; i < totDim; ++i) u_t[face * totDim + i] = x[i];
4345:         DMPlexVecRestoreClosure(plex, section, locX_t, support[0], NULL, &x);
4346:       }
4347:       if (locA) {
4348:         PetscInt subp;

4350:         DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp);
4351:         DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x);
4352:         for (i = 0; i < totDimAux; ++i) a[face * totDimAux + i] = x[i];
4353:         DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x);
4354:       }
4355:     }
4356:     PetscArrayzero(elemVec, numFaces * totDim);
4357:     {
4358:       PetscFE      fe;
4359:       PetscInt     Nb;
4360:       PetscFEGeom *chunkGeom = NULL;
4361:       /* Conforming batches */
4362:       PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
4363:       /* Remainder */
4364:       PetscInt Nr, offset;

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

4387:       if (mesh->printFEM > 1) DMPrintCellVector(point, "BdResidual", totDim, &elemVec[face * totDim]);
4388:       DMPlexGetSupport(plex, point, &support);
4389:       DMPlexVecSetClosure(plex, NULL, locF, support[0], &elemVec[face * totDim], ADD_ALL_VALUES);
4390:     }
4391:     DMSNESRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom);
4392:     PetscQuadratureDestroy(&qGeom);
4393:     ISRestoreIndices(pointIS, &points);
4394:     ISDestroy(&pointIS);
4395:     PetscFree4(u, u_t, elemVec, a);
4396:   }
4397: end:
4398:   DMDestroy(&plex);
4399:   DMDestroy(&plexA);
4400:   return 0;
4401: }

4403: PetscErrorCode DMPlexComputeBdResidualSingle(DM dm, PetscReal t, PetscWeakForm wf, PetscFormKey key, Vec locX, Vec locX_t, Vec locF)
4404: {
4405:   DMField  coordField;
4406:   DMLabel  depthLabel;
4407:   IS       facetIS;
4408:   PetscInt dim;

4410:   DMGetDimension(dm, &dim);
4411:   DMPlexGetDepthLabel(dm, &depthLabel);
4412:   DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS);
4413:   DMGetCoordinateField(dm, &coordField);
4414:   DMPlexComputeBdResidual_Single_Internal(dm, t, wf, key, locX, locX_t, locF, coordField, facetIS);
4415:   ISDestroy(&facetIS);
4416:   return 0;
4417: }

4419: PetscErrorCode DMPlexComputeBdResidual_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
4420: {
4421:   PetscDS  prob;
4422:   PetscInt numBd, bd;
4423:   DMField  coordField = NULL;
4424:   IS       facetIS    = NULL;
4425:   DMLabel  depthLabel;
4426:   PetscInt dim;

4428:   DMGetDS(dm, &prob);
4429:   DMPlexGetDepthLabel(dm, &depthLabel);
4430:   DMGetDimension(dm, &dim);
4431:   DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS);
4432:   PetscDSGetNumBoundary(prob, &numBd);
4433:   for (bd = 0; bd < numBd; ++bd) {
4434:     PetscWeakForm           wf;
4435:     DMBoundaryConditionType type;
4436:     DMLabel                 label;
4437:     const PetscInt         *values;
4438:     PetscInt                field, numValues, v;
4439:     PetscObject             obj;
4440:     PetscClassId            id;
4441:     PetscFormKey            key;

4443:     PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &field, NULL, NULL, NULL, NULL, NULL);
4444:     if (type & DM_BC_ESSENTIAL) continue;
4445:     PetscDSGetDiscretization(prob, field, &obj);
4446:     PetscObjectGetClassId(obj, &id);
4447:     if (id != PETSCFE_CLASSID) continue;
4448:     if (!facetIS) {
4449:       DMLabel  depthLabel;
4450:       PetscInt dim;

4452:       DMPlexGetDepthLabel(dm, &depthLabel);
4453:       DMGetDimension(dm, &dim);
4454:       DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS);
4455:     }
4456:     DMGetCoordinateField(dm, &coordField);
4457:     for (v = 0; v < numValues; ++v) {
4458:       key.label = label;
4459:       key.value = values[v];
4460:       key.field = field;
4461:       key.part  = 0;
4462:       DMPlexComputeBdResidual_Single_Internal(dm, t, wf, key, locX, locX_t, locF, coordField, facetIS);
4463:     }
4464:   }
4465:   ISDestroy(&facetIS);
4466:   return 0;
4467: }

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

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

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

4546:         PetscDSGetImplicit(ds, f, &fimp);
4547:         if (isImplicit != fimp) continue;
4548:         PetscDSGetDiscretization(ds, f, &obj);
4549:         PetscObjectGetClassId(obj, &id);
4550:         if (id == PETSCFE_CLASSID) {
4551:           PetscFE fe = (PetscFE)obj;

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

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

4616:       key.field = f;
4617:       PetscDSGetImplicit(ds, f, &fimp);
4618:       if (isImplicit != fimp) continue;
4619:       PetscDSGetDiscretization(ds, f, &obj);
4620:       PetscObjectGetClassId(obj, &id);
4621:       if (id == PETSCFE_CLASSID) {
4622:         PetscFE         fe        = (PetscFE)obj;
4623:         PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[f];
4624:         PetscFEGeom    *chunkGeom = NULL;
4625:         PetscQuadrature quad      = affineQuad ? affineQuad : quads[f];
4626:         PetscInt        Nq, Nb;

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

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

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

4665:           DMLabelGetValue(ghostLabel, cell, &ghostVal);
4666:           if (ghostVal > 0) continue;
4667:         }
4668:         DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind * totDim], ADD_ALL_VALUES);
4669:       }
4670:     }
4671:     if (useFVM) {
4672:       PetscScalar *fa;
4673:       PetscInt     iface;

4675:       VecGetArray(locF, &fa);
4676:       for (f = 0; f < Nf; ++f) {
4677:         PetscFV      fv;
4678:         PetscObject  obj;
4679:         PetscClassId id;
4680:         PetscInt     foff, pdim;

4682:         PetscDSGetDiscretization(ds, f, &obj);
4683:         PetscDSGetFieldOffset(ds, f, &foff);
4684:         PetscObjectGetClassId(obj, &id);
4685:         if (id != PETSCFV_CLASSID) continue;
4686:         fv = (PetscFV)obj;
4687:         PetscFVGetNumComponents(fv, &pdim);
4688:         /* Accumulate fluxes to cells */
4689:         for (face = fS, iface = 0; face < fE; ++face) {
4690:           const PetscInt *scells;
4691:           PetscScalar    *fL = NULL, *fR = NULL;
4692:           PetscInt        ghost, d, nsupp, nchild;

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

4716:       VecGetArray(locF, &fa);
4717:       VecGetArray(locX_t, &x_t);
4718:       for (f = 0; f < Nf; ++f) {
4719:         PetscFV      fv;
4720:         PetscObject  obj;
4721:         PetscClassId id;
4722:         PetscInt     pdim, d;

4724:         PetscDSGetDiscretization(ds, f, &obj);
4725:         PetscObjectGetClassId(obj, &id);
4726:         if (id != PETSCFV_CLASSID) continue;
4727:         fv = (PetscFV)obj;
4728:         PetscFVGetNumComponents(fv, &pdim);
4729:         for (c = cS; c < cE; ++c) {
4730:           const PetscInt cell = cells ? cells[c] : c;
4731:           PetscScalar   *u_t, *r;

4733:           if (ghostLabel) {
4734:             PetscInt ghostVal;

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

4762:   if (useFEM) {
4763:     DMPlexComputeBdResidual_Internal(dm, locX, locX_t, t, locF, user);

4765:     if (maxDegree <= 1) {
4766:       DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom);
4767:       PetscQuadratureDestroy(&affineQuad);
4768:     } else {
4769:       for (f = 0; f < Nf; ++f) {
4770:         DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]);
4771:         PetscQuadratureDestroy(&quads[f]);
4772:       }
4773:       PetscFree2(quads, geoms);
4774:     }
4775:   }

4777:   /* FEM */
4778:   /* 1: Get sizes from dm and dmAux */
4779:   /* 2: Get geometric data */
4780:   /* 3: Handle boundary values */
4781:   /* 4: Loop over domain */
4782:   /*   Extract coefficients */
4783:   /* Loop over fields */
4784:   /*   Set tiling for FE*/
4785:   /*   Integrate FE residual to get elemVec */
4786:   /*     Loop over subdomain */
4787:   /*       Loop over quad points */
4788:   /*         Transform coords to real space */
4789:   /*         Evaluate field and aux fields at point */
4790:   /*         Evaluate residual at point */
4791:   /*         Transform residual to real space */
4792:   /*       Add residual to elemVec */
4793:   /* Loop over domain */
4794:   /*   Add elemVec to locX */

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

4817:     VecDuplicate(locF, &locFbc);
4818:     VecCopy(locF, locFbc);
4819:     PetscSectionGetChart(section, &pStart, &pEnd);
4820:     PetscSectionGetMaxDof(section, &maxDof);
4821:     PetscCalloc1(maxDof, &zeroes);
4822:     for (p = pStart; p < pEnd; p++) VecSetValuesSection(locFbc, section, p, zeroes, INSERT_BC_VALUES);
4823:     PetscFree(zeroes);
4824:     DMPrintLocalVec(dm, name, mesh->printTol, locFbc);
4825:     VecDestroy(&locFbc);
4826:   }
4827:   PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0);
4828:   return 0;
4829: }

4831: /*
4832:   1) Allow multiple kernels for BdResidual for hybrid DS

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

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

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

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

4884:     VecGetDM(locA[2], &dmAux[2]);
4885:     DMGetCellDS(dmAux[2], cellStart, &dsAux[2]);
4886:     PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]);
4887:     {
4888:       const PetscInt *cone;
4889:       PetscInt        c;

4891:       DMPlexGetCone(dm, cellStart, &cone);
4892:       for (c = 0; c < 2; ++c) {
4893:         const PetscInt *support;
4894:         PetscInt        ssize, s;

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

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

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

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

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

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

5030: 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)
5031: {
5032:   DM_Plex        *mesh = (DM_Plex *)dm->data;
5033:   DM              plex = NULL, plexA = NULL, tdm;
5034:   DMEnclosureType encAux;
5035:   PetscDS         prob, probAux       = NULL;
5036:   PetscSection    section, sectionAux = NULL;
5037:   PetscSection    globalSection;
5038:   Vec             locA = NULL, tv;
5039:   PetscScalar    *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL;
5040:   PetscInt        v;
5041:   PetscInt        Nf, totDim, totDimAux = 0;
5042:   PetscBool       transform;

5044:   DMConvert(dm, DMPLEX, &plex);
5045:   DMHasBasisTransform(dm, &transform);
5046:   DMGetBasisTransformDM_Internal(dm, &tdm);
5047:   DMGetBasisTransformVec_Internal(dm, &tv);
5048:   DMGetLocalSection(dm, &section);
5049:   DMGetDS(dm, &prob);
5050:   PetscDSGetNumFields(prob, &Nf);
5051:   PetscDSGetTotalDimension(prob, &totDim);
5052:   DMGetAuxiliaryVec(dm, label, values[0], 0, &locA);
5053:   if (locA) {
5054:     DM dmAux;

5056:     VecGetDM(locA, &dmAux);
5057:     DMGetEnclosureRelation(dmAux, dm, &encAux);
5058:     DMConvert(dmAux, DMPLEX, &plexA);
5059:     DMGetDS(plexA, &probAux);
5060:     PetscDSGetTotalDimension(probAux, &totDimAux);
5061:     DMGetLocalSection(plexA, &sectionAux);
5062:   }

5064:   DMGetGlobalSection(dm, &globalSection);
5065:   for (v = 0; v < numValues; ++v) {
5066:     PetscFEGeom    *fgeom;
5067:     PetscInt        maxDegree;
5068:     PetscQuadrature qGeom = NULL;
5069:     IS              pointIS;
5070:     const PetscInt *points;
5071:     PetscFormKey    key;
5072:     PetscInt        numFaces, face, Nq;

5074:     key.label = label;
5075:     key.value = values[v];
5076:     key.part  = 0;
5077:     DMLabelGetStratumIS(label, values[v], &pointIS);
5078:     if (!pointIS) continue; /* No points with that id on this process */
5079:     {
5080:       IS isectIS;

5082:       /* TODO: Special cases of ISIntersect where it is quick to check a prior if one is a superset of the other */
5083:       ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS);
5084:       ISDestroy(&pointIS);
5085:       pointIS = isectIS;
5086:     }
5087:     ISGetLocalSize(pointIS, &numFaces);
5088:     ISGetIndices(pointIS, &points);
5089:     PetscMalloc4(numFaces * totDim, &u, locX_t ? numFaces * totDim : 0, &u_t, numFaces * totDim * totDim, &elemMat, locA ? numFaces * totDimAux : 0, &a);
5090:     DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree);
5091:     if (maxDegree <= 1) DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom);
5092:     if (!qGeom) {
5093:       PetscFE fe;

5095:       PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe);
5096:       PetscFEGetFaceQuadrature(fe, &qGeom);
5097:       PetscObjectReference((PetscObject)qGeom);
5098:     }
5099:     PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL);
5100:     DMSNESGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom);
5101:     for (face = 0; face < numFaces; ++face) {
5102:       const PetscInt point = points[face], *support;
5103:       PetscScalar   *x     = NULL;
5104:       PetscInt       i;

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

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

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

5175: 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)
5176: {
5177:   DMField  coordField;
5178:   DMLabel  depthLabel;
5179:   IS       facetIS;
5180:   PetscInt dim;

5182:   DMGetDimension(dm, &dim);
5183:   DMPlexGetDepthLabel(dm, &depthLabel);
5184:   DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS);
5185:   DMGetCoordinateField(dm, &coordField);
5186:   DMPlexComputeBdJacobian_Single_Internal(dm, t, wf, label, numValues, values, field, locX, locX_t, X_tShift, Jac, JacP, coordField, facetIS);
5187:   ISDestroy(&facetIS);
5188:   return 0;
5189: }

5191: PetscErrorCode DMPlexComputeBdJacobian_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, PetscReal X_tShift, Mat Jac, Mat JacP, void *user)
5192: {
5193:   PetscDS  prob;
5194:   PetscInt dim, numBd, bd;
5195:   DMLabel  depthLabel;
5196:   DMField  coordField = NULL;
5197:   IS       facetIS;

5199:   DMGetDS(dm, &prob);
5200:   DMPlexGetDepthLabel(dm, &depthLabel);
5201:   DMGetDimension(dm, &dim);
5202:   DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS);
5203:   PetscDSGetNumBoundary(prob, &numBd);
5204:   DMGetCoordinateField(dm, &coordField);
5205:   for (bd = 0; bd < numBd; ++bd) {
5206:     PetscWeakForm           wf;
5207:     DMBoundaryConditionType type;
5208:     DMLabel                 label;
5209:     const PetscInt         *values;
5210:     PetscInt                fieldI, numValues;
5211:     PetscObject             obj;
5212:     PetscClassId            id;

5214:     PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &fieldI, NULL, NULL, NULL, NULL, NULL);
5215:     if (type & DM_BC_ESSENTIAL) continue;
5216:     PetscDSGetDiscretization(prob, fieldI, &obj);
5217:     PetscObjectGetClassId(obj, &id);
5218:     if (id != PETSCFE_CLASSID) continue;
5219:     DMPlexComputeBdJacobian_Single_Internal(dm, t, wf, label, numValues, values, fieldI, locX, locX_t, X_tShift, Jac, JacP, coordField, facetIS);
5220:   }
5221:   ISDestroy(&facetIS);
5222:   return 0;
5223: }

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

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

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

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

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

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

5423:   MPI_Allreduce(&assOp, &gassOp, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm));
5424:   if (hasJac && hasPrec) {
5425:     MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY);
5426:     MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY);
5427:   }
5428: }
5429:   MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY);
5430:   MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY);
5431:   PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0);
5432:   return 0;
5433: }

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

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

5483:     VecGetDM(locA[2], &dmAux[2]);
5484:     DMConvert(dmAux[2], DMPLEX, &plexA);
5485:     DMGetSection(dmAux[2], &sectionAux[2]);
5486:     DMGetCellDS(dmAux[2], cellStart, &dsAux[2]);
5487:     PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]);
5488:     {
5489:       const PetscInt *cone;
5490:       PetscInt        c;

5492:       DMPlexGetCone(dm, cellStart, &cone);
5493:       for (c = 0; c < 2; ++c) {
5494:         const PetscInt *support;
5495:         PetscInt        ssize, s;

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

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

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

5558:     for (fieldI = 0; fieldI < Nf; ++fieldI) {
5559:       PetscFE         feI;
5560:       PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[fieldI];
5561:       PetscFEGeom    *chunkGeom = NULL, *remGeom = NULL;
5562:       PetscQuadrature quad = affineQuad ? affineQuad : quads[fieldI];
5563:       PetscInt        numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb;
5564:       PetscBool       isCohesiveField;

5566:       PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&feI);
5567:       if (!feI) continue;
5568:       PetscFEGetTileSizes(feI, NULL, &numBlocks, NULL, &numBatches);
5569:       PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL);
5570:       PetscFEGetDimension(feI, &Nb);
5571:       blockSize = Nb;
5572:       batchSize = numBlocks * blockSize;
5573:       PetscFESetTileSizes(feI, blockSize, numBlocks, batchSize, numBatches);
5574:       numChunks = numCells / (numBatches * batchSize);
5575:       Ne        = numChunks * numBatches * batchSize;
5576:       Nr        = numCells % (numBatches * batchSize);
5577:       offset    = numCells - Nr;
5578:       PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom);
5579:       PetscFEGeomGetChunk(geom, offset, numCells, &remGeom);
5580:       PetscDSGetCohesive(ds, fieldI, &isCohesiveField);
5581:       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5582:         PetscFE feJ;

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

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

5655: /*
5656:   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.

5658:   Input Parameters:
5659: + dm     - The mesh
5660: . key    - The PetscWeakFormKey indcating where integration should happen
5661: . cellIS - The cells to integrate over
5662: . t      - The time
5663: . X_tShift - The multiplier for the Jacobian with repsect to X_t
5664: . X      - Local solution vector
5665: . X_t    - Time-derivative of the local solution vector
5666: . Y      - Local input vector
5667: - user   - the user context

5669:   Output Parameter:
5670: . Z - Local output vector

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

5693:   PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0);
5694:   DMConvert(dm, DMPLEX, &plex);
5695:   if (!cellIS) {
5696:     PetscInt depth;

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

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

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

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