xref: /petsc/src/dm/impls/plex/plexfem.c (revision a233fdd855952c806ef1c41ee07dc5cd945c1b71) !
1 #include <petsc/private/dmpleximpl.h> /*I      "petscdmplex.h"   I*/
2 #include <petscsf.h>
3 
4 #include <petscblaslapack.h>
5 #include <petsc/private/hashsetij.h>
6 #include <petsc/private/petscfeimpl.h>
7 #include <petsc/private/petscfvimpl.h>
8 
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";
18 
19 static PetscErrorCode DMPlexConvertPlex(DM dm, DM *plex, PetscBool copy)
20 {
21   PetscBool isPlex;
22 
23   PetscFunctionBegin;
24   PetscCall(PetscObjectTypeCompare((PetscObject)dm, DMPLEX, &isPlex));
25   if (isPlex) {
26     *plex = dm;
27     PetscCall(PetscObjectReference((PetscObject)dm));
28   } else {
29     PetscCall(PetscObjectQuery((PetscObject)dm, "dm_plex", (PetscObject *)plex));
30     if (!*plex) {
31       PetscCall(DMConvert(dm, DMPLEX, plex));
32       PetscCall(PetscObjectCompose((PetscObject)dm, "dm_plex", (PetscObject)*plex));
33       if (copy) {
34         DMSubDomainHookLink link;
35 
36         PetscCall(DMCopyAuxiliaryVec(dm, *plex));
37         /* Run the subdomain hook (this will copy the DMSNES/DMTS) */
38         for (link = dm->subdomainhook; link; link = link->next) {
39           if (link->ddhook) PetscCall((*link->ddhook)(dm, *plex, link->ctx));
40         }
41       }
42     } else {
43       PetscCall(PetscObjectReference((PetscObject)*plex));
44     }
45   }
46   PetscFunctionReturn(PETSC_SUCCESS);
47 }
48 
49 static PetscErrorCode PetscContainerUserDestroy_PetscFEGeom(void *ctx)
50 {
51   PetscFEGeom *geom = (PetscFEGeom *)ctx;
52 
53   PetscFunctionBegin;
54   PetscCall(PetscFEGeomDestroy(&geom));
55   PetscFunctionReturn(PETSC_SUCCESS);
56 }
57 
58 static PetscErrorCode DMPlexGetFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
59 {
60   char           composeStr[33] = {0};
61   PetscObjectId  id;
62   PetscContainer container;
63 
64   PetscFunctionBegin;
65   PetscCall(PetscObjectGetId((PetscObject)quad, &id));
66   PetscCall(PetscSNPrintf(composeStr, 32, "DMPlexGetFEGeom_%" PetscInt64_FMT "\n", id));
67   PetscCall(PetscObjectQuery((PetscObject)pointIS, composeStr, (PetscObject *)&container));
68   if (container) {
69     PetscCall(PetscContainerGetPointer(container, (void **)geom));
70   } else {
71     PetscCall(DMFieldCreateFEGeom(coordField, pointIS, quad, faceData, geom));
72     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &container));
73     PetscCall(PetscContainerSetPointer(container, (void *)*geom));
74     PetscCall(PetscContainerSetUserDestroy(container, PetscContainerUserDestroy_PetscFEGeom));
75     PetscCall(PetscObjectCompose((PetscObject)pointIS, composeStr, (PetscObject)container));
76     PetscCall(PetscContainerDestroy(&container));
77   }
78   PetscFunctionReturn(PETSC_SUCCESS);
79 }
80 
81 static PetscErrorCode DMPlexRestoreFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
82 {
83   PetscFunctionBegin;
84   *geom = NULL;
85   PetscFunctionReturn(PETSC_SUCCESS);
86 }
87 
88 /*@
89   DMPlexGetScale - Get the scale for the specified fundamental unit
90 
91   Not Collective
92 
93   Input Parameters:
94 + dm   - the `DM`
95 - unit - The SI unit
96 
97   Output Parameter:
98 . scale - The value used to scale all quantities with this unit
99 
100   Level: advanced
101 
102 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetScale()`, `PetscUnit`
103 @*/
104 PetscErrorCode DMPlexGetScale(DM dm, PetscUnit unit, PetscReal *scale)
105 {
106   DM_Plex *mesh = (DM_Plex *)dm->data;
107 
108   PetscFunctionBegin;
109   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
110   PetscAssertPointer(scale, 3);
111   *scale = mesh->scale[unit];
112   PetscFunctionReturn(PETSC_SUCCESS);
113 }
114 
115 /*@
116   DMPlexSetScale - Set the scale for the specified fundamental unit
117 
118   Not Collective
119 
120   Input Parameters:
121 + dm    - the `DM`
122 . unit  - The SI unit
123 - scale - The value used to scale all quantities with this unit
124 
125   Level: advanced
126 
127 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetScale()`, `PetscUnit`
128 @*/
129 PetscErrorCode DMPlexSetScale(DM dm, PetscUnit unit, PetscReal scale)
130 {
131   DM_Plex *mesh = (DM_Plex *)dm->data;
132 
133   PetscFunctionBegin;
134   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
135   mesh->scale[unit] = scale;
136   PetscFunctionReturn(PETSC_SUCCESS);
137 }
138 
139 static PetscErrorCode DMPlexProjectRigidBody_Private(PetscInt dim, PetscReal t, const PetscReal X[], PetscInt Nc, PetscScalar *mode, void *ctx)
140 {
141   const PetscInt eps[3][3][3] = {
142     {{0, 0, 0},  {0, 0, 1},  {0, -1, 0}},
143     {{0, 0, -1}, {0, 0, 0},  {1, 0, 0} },
144     {{0, 1, 0},  {-1, 0, 0}, {0, 0, 0} }
145   };
146   PetscInt *ctxInt = (PetscInt *)ctx;
147   PetscInt  dim2   = ctxInt[0];
148   PetscInt  d      = ctxInt[1];
149   PetscInt  i, j, k = dim > 2 ? d - dim : d;
150 
151   PetscFunctionBegin;
152   PetscCheck(dim == dim2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Input dimension %" PetscInt_FMT " does not match context dimension %" PetscInt_FMT, dim, dim2);
153   for (i = 0; i < dim; i++) mode[i] = 0.;
154   if (d < dim) {
155     mode[d] = 1.; /* Translation along axis d */
156   } else {
157     for (i = 0; i < dim; i++) {
158       for (j = 0; j < dim; j++) { mode[j] += eps[i][j][k] * X[i]; /* Rotation about axis d */ }
159     }
160   }
161   PetscFunctionReturn(PETSC_SUCCESS);
162 }
163 
164 /*@
165   DMPlexCreateRigidBody - For the default global section, create rigid body modes by function space interpolation
166 
167   Collective
168 
169   Input Parameters:
170 + dm    - the `DM`
171 - field - The field number for the rigid body space, or 0 for the default
172 
173   Output Parameter:
174 . sp - the null space
175 
176   Level: advanced
177 
178   Note:
179   This is necessary to provide a suitable coarse space for algebraic multigrid
180 
181 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `MatNullSpaceCreate()`, `PCGAMG`
182 @*/
183 PetscErrorCode DMPlexCreateRigidBody(DM dm, PetscInt field, MatNullSpace *sp)
184 {
185   PetscErrorCode (**func)(PetscInt, PetscReal, const PetscReal *, PetscInt, PetscScalar *, void *);
186   MPI_Comm     comm;
187   Vec          mode[6];
188   PetscSection section, globalSection;
189   PetscInt     dim, dimEmbed, Nf, n, m, mmin, d, i, j;
190   void       **ctxs;
191 
192   PetscFunctionBegin;
193   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
194   PetscCall(DMGetDimension(dm, &dim));
195   PetscCall(DMGetCoordinateDim(dm, &dimEmbed));
196   PetscCall(DMGetNumFields(dm, &Nf));
197   PetscCheck(!Nf || !(field < 0 || field >= Nf), comm, PETSC_ERR_ARG_OUTOFRANGE, "Field %" PetscInt_FMT " is not in [0, %" PetscInt_FMT ")", field, Nf);
198   if (dim == 1 && Nf < 2) {
199     PetscCall(MatNullSpaceCreate(comm, PETSC_TRUE, 0, NULL, sp));
200     PetscFunctionReturn(PETSC_SUCCESS);
201   }
202   PetscCall(DMGetLocalSection(dm, &section));
203   PetscCall(DMGetGlobalSection(dm, &globalSection));
204   PetscCall(PetscSectionGetConstrainedStorageSize(globalSection, &n));
205   PetscCall(PetscCalloc2(Nf, &func, Nf, &ctxs));
206   m = (dim * (dim + 1)) / 2;
207   PetscCall(VecCreate(comm, &mode[0]));
208   PetscCall(VecSetType(mode[0], dm->vectype));
209   PetscCall(VecSetSizes(mode[0], n, PETSC_DETERMINE));
210   PetscCall(VecSetUp(mode[0]));
211   PetscCall(VecGetSize(mode[0], &n));
212   mmin        = PetscMin(m, n);
213   func[field] = DMPlexProjectRigidBody_Private;
214   for (i = 1; i < m; ++i) PetscCall(VecDuplicate(mode[0], &mode[i]));
215   for (d = 0; d < m; d++) {
216     PetscInt ctx[2];
217 
218     ctxs[field] = (void *)(&ctx[0]);
219     ctx[0]      = dimEmbed;
220     ctx[1]      = d;
221     PetscCall(DMProjectFunction(dm, 0.0, func, ctxs, INSERT_VALUES, mode[d]));
222   }
223   /* Orthonormalize system */
224   for (i = 0; i < mmin; ++i) {
225     PetscScalar dots[6];
226 
227     PetscCall(VecNormalize(mode[i], NULL));
228     PetscCall(VecMDot(mode[i], mmin - i - 1, mode + i + 1, dots + i + 1));
229     for (j = i + 1; j < mmin; ++j) {
230       dots[j] *= -1.0;
231       PetscCall(VecAXPY(mode[j], dots[j], mode[i]));
232     }
233   }
234   PetscCall(MatNullSpaceCreate(comm, PETSC_FALSE, mmin, mode, sp));
235   for (i = 0; i < m; ++i) PetscCall(VecDestroy(&mode[i]));
236   PetscCall(PetscFree2(func, ctxs));
237   PetscFunctionReturn(PETSC_SUCCESS);
238 }
239 
240 /*@
241   DMPlexCreateRigidBodies - For the default global section, create rigid body modes by function space interpolation
242 
243   Collective
244 
245   Input Parameters:
246 + dm    - the `DM`
247 . nb    - The number of bodies
248 . label - The `DMLabel` marking each domain
249 . nids  - The number of ids per body
250 - ids   - An array of the label ids in sequence for each domain
251 
252   Output Parameter:
253 . sp - the null space
254 
255   Level: advanced
256 
257   Note:
258   This is necessary to provide a suitable coarse space for algebraic multigrid
259 
260 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `MatNullSpaceCreate()`
261 @*/
262 PetscErrorCode DMPlexCreateRigidBodies(DM dm, PetscInt nb, DMLabel label, const PetscInt nids[], const PetscInt ids[], MatNullSpace *sp)
263 {
264   MPI_Comm     comm;
265   PetscSection section, globalSection;
266   Vec         *mode;
267   PetscScalar *dots;
268   PetscInt     dim, dimEmbed, n, m, b, d, i, j, off;
269 
270   PetscFunctionBegin;
271   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
272   PetscCall(DMGetDimension(dm, &dim));
273   PetscCall(DMGetCoordinateDim(dm, &dimEmbed));
274   PetscCall(DMGetLocalSection(dm, &section));
275   PetscCall(DMGetGlobalSection(dm, &globalSection));
276   PetscCall(PetscSectionGetConstrainedStorageSize(globalSection, &n));
277   m = nb * (dim * (dim + 1)) / 2;
278   PetscCall(PetscMalloc2(m, &mode, m, &dots));
279   PetscCall(VecCreate(comm, &mode[0]));
280   PetscCall(VecSetSizes(mode[0], n, PETSC_DETERMINE));
281   PetscCall(VecSetUp(mode[0]));
282   for (i = 1; i < m; ++i) PetscCall(VecDuplicate(mode[0], &mode[i]));
283   for (b = 0, off = 0; b < nb; ++b) {
284     for (d = 0; d < m / nb; ++d) {
285       PetscInt ctx[2];
286       PetscErrorCode (*func)(PetscInt, PetscReal, const PetscReal *, PetscInt, PetscScalar *, void *) = DMPlexProjectRigidBody_Private;
287       void *voidctx                                                                                   = (void *)(&ctx[0]);
288 
289       ctx[0] = dimEmbed;
290       ctx[1] = d;
291       PetscCall(DMProjectFunctionLabel(dm, 0.0, label, nids[b], &ids[off], 0, NULL, &func, &voidctx, INSERT_VALUES, mode[d]));
292       off += nids[b];
293     }
294   }
295   /* Orthonormalize system */
296   for (i = 0; i < m; ++i) {
297     PetscScalar dots[6];
298 
299     PetscCall(VecNormalize(mode[i], NULL));
300     PetscCall(VecMDot(mode[i], m - i - 1, mode + i + 1, dots + i + 1));
301     for (j = i + 1; j < m; ++j) {
302       dots[j] *= -1.0;
303       PetscCall(VecAXPY(mode[j], dots[j], mode[i]));
304     }
305   }
306   PetscCall(MatNullSpaceCreate(comm, PETSC_FALSE, m, mode, sp));
307   for (i = 0; i < m; ++i) PetscCall(VecDestroy(&mode[i]));
308   PetscCall(PetscFree2(mode, dots));
309   PetscFunctionReturn(PETSC_SUCCESS);
310 }
311 
312 /*@
313   DMPlexSetMaxProjectionHeight - In DMPlexProjectXXXLocal() functions, the projected values of a basis function's dofs
314   are computed by associating the basis function with one of the mesh points in its transitively-closed support, and
315   evaluating the dual space basis of that point.
316 
317   Input Parameters:
318 + dm     - the `DMPLEX` object
319 - height - the maximum projection height >= 0
320 
321   Level: advanced
322 
323   Notes:
324   A basis function is associated with the point in its transitively-closed support whose mesh
325   height is highest (w.r.t. DAG height), but not greater than the maximum projection height,
326   which is set with this function.  By default, the maximum projection height is zero, which
327   means that only mesh cells are used to project basis functions.  A height of one, for
328   example, evaluates a cell-interior basis functions using its cells dual space basis, but all
329   other basis functions with the dual space basis of a face.
330 
331 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMaxProjectionHeight()`, `DMProjectFunctionLocal()`, `DMProjectFunctionLabelLocal()`
332 @*/
333 PetscErrorCode DMPlexSetMaxProjectionHeight(DM dm, PetscInt height)
334 {
335   DM_Plex *plex = (DM_Plex *)dm->data;
336 
337   PetscFunctionBegin;
338   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
339   plex->maxProjectionHeight = height;
340   PetscFunctionReturn(PETSC_SUCCESS);
341 }
342 
343 /*@
344   DMPlexGetMaxProjectionHeight - Get the maximum height (w.r.t. DAG) of mesh points used to evaluate dual bases in
345   DMPlexProjectXXXLocal() functions.
346 
347   Input Parameter:
348 . dm - the `DMPLEX` object
349 
350   Output Parameter:
351 . height - the maximum projection height
352 
353   Level: intermediate
354 
355 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetMaxProjectionHeight()`, `DMProjectFunctionLocal()`, `DMProjectFunctionLabelLocal()`
356 @*/
357 PetscErrorCode DMPlexGetMaxProjectionHeight(DM dm, PetscInt *height)
358 {
359   DM_Plex *plex = (DM_Plex *)dm->data;
360 
361   PetscFunctionBegin;
362   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
363   *height = plex->maxProjectionHeight;
364   PetscFunctionReturn(PETSC_SUCCESS);
365 }
366 
367 typedef struct {
368   PetscReal    alpha; /* The first Euler angle, and in 2D the only one */
369   PetscReal    beta;  /* The second Euler angle */
370   PetscReal    gamma; /* The third Euler angle */
371   PetscInt     dim;   /* The dimension of R */
372   PetscScalar *R;     /* The rotation matrix, transforming a vector in the local basis to the global basis */
373   PetscScalar *RT;    /* The transposed rotation matrix, transforming a vector in the global basis to the local basis */
374 } RotCtx;
375 
376 /*
377   Note: Following https://en.wikipedia.org/wiki/Euler_angles, we will specify Euler angles by extrinsic rotations, meaning that
378   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:
379   $ The XYZ system rotates about the z axis by alpha. The X axis is now at angle alpha with respect to the x axis.
380   $ 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.
381   $ The XYZ system rotates a third time about the z axis by gamma.
382 */
383 static PetscErrorCode DMPlexBasisTransformSetUp_Rotation_Internal(DM dm, void *ctx)
384 {
385   RotCtx   *rc  = (RotCtx *)ctx;
386   PetscInt  dim = rc->dim;
387   PetscReal c1, s1, c2, s2, c3, s3;
388 
389   PetscFunctionBegin;
390   PetscCall(PetscMalloc2(PetscSqr(dim), &rc->R, PetscSqr(dim), &rc->RT));
391   switch (dim) {
392   case 2:
393     c1       = PetscCosReal(rc->alpha);
394     s1       = PetscSinReal(rc->alpha);
395     rc->R[0] = c1;
396     rc->R[1] = s1;
397     rc->R[2] = -s1;
398     rc->R[3] = c1;
399     PetscCall(PetscArraycpy(rc->RT, rc->R, PetscSqr(dim)));
400     DMPlex_Transpose2D_Internal(rc->RT);
401     break;
402   case 3:
403     c1       = PetscCosReal(rc->alpha);
404     s1       = PetscSinReal(rc->alpha);
405     c2       = PetscCosReal(rc->beta);
406     s2       = PetscSinReal(rc->beta);
407     c3       = PetscCosReal(rc->gamma);
408     s3       = PetscSinReal(rc->gamma);
409     rc->R[0] = c1 * c3 - c2 * s1 * s3;
410     rc->R[1] = c3 * s1 + c1 * c2 * s3;
411     rc->R[2] = s2 * s3;
412     rc->R[3] = -c1 * s3 - c2 * c3 * s1;
413     rc->R[4] = c1 * c2 * c3 - s1 * s3;
414     rc->R[5] = c3 * s2;
415     rc->R[6] = s1 * s2;
416     rc->R[7] = -c1 * s2;
417     rc->R[8] = c2;
418     PetscCall(PetscArraycpy(rc->RT, rc->R, PetscSqr(dim)));
419     DMPlex_Transpose3D_Internal(rc->RT);
420     break;
421   default:
422     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Dimension %" PetscInt_FMT " not supported", dim);
423   }
424   PetscFunctionReturn(PETSC_SUCCESS);
425 }
426 
427 static PetscErrorCode DMPlexBasisTransformDestroy_Rotation_Internal(DM dm, void *ctx)
428 {
429   RotCtx *rc = (RotCtx *)ctx;
430 
431   PetscFunctionBegin;
432   PetscCall(PetscFree2(rc->R, rc->RT));
433   PetscCall(PetscFree(rc));
434   PetscFunctionReturn(PETSC_SUCCESS);
435 }
436 
437 static PetscErrorCode DMPlexBasisTransformGetMatrix_Rotation_Internal(DM dm, const PetscReal x[], PetscBool l2g, const PetscScalar **A, void *ctx)
438 {
439   RotCtx *rc = (RotCtx *)ctx;
440 
441   PetscFunctionBeginHot;
442   PetscAssertPointer(ctx, 5);
443   if (l2g) {
444     *A = rc->R;
445   } else {
446     *A = rc->RT;
447   }
448   PetscFunctionReturn(PETSC_SUCCESS);
449 }
450 
451 PetscErrorCode DMPlexBasisTransformApplyReal_Internal(DM dm, const PetscReal x[], PetscBool l2g, PetscInt dim, const PetscReal *y, PetscReal *z, void *ctx)
452 {
453   PetscFunctionBegin;
454 #if defined(PETSC_USE_COMPLEX)
455   switch (dim) {
456   case 2: {
457     PetscScalar yt[2] = {y[0], y[1]}, zt[2] = {0.0, 0.0};
458 
459     PetscCall(DMPlexBasisTransformApply_Internal(dm, x, l2g, dim, yt, zt, ctx));
460     z[0] = PetscRealPart(zt[0]);
461     z[1] = PetscRealPart(zt[1]);
462   } break;
463   case 3: {
464     PetscScalar yt[3] = {y[0], y[1], y[2]}, zt[3] = {0.0, 0.0, 0.0};
465 
466     PetscCall(DMPlexBasisTransformApply_Internal(dm, x, l2g, dim, yt, zt, ctx));
467     z[0] = PetscRealPart(zt[0]);
468     z[1] = PetscRealPart(zt[1]);
469     z[2] = PetscRealPart(zt[2]);
470   } break;
471   }
472 #else
473   PetscCall(DMPlexBasisTransformApply_Internal(dm, x, l2g, dim, y, z, ctx));
474 #endif
475   PetscFunctionReturn(PETSC_SUCCESS);
476 }
477 
478 PetscErrorCode DMPlexBasisTransformApply_Internal(DM dm, const PetscReal x[], PetscBool l2g, PetscInt dim, const PetscScalar *y, PetscScalar *z, void *ctx)
479 {
480   const PetscScalar *A;
481 
482   PetscFunctionBeginHot;
483   PetscCall((*dm->transformGetMatrix)(dm, x, l2g, &A, ctx));
484   switch (dim) {
485   case 2:
486     DMPlex_Mult2D_Internal(A, 1, y, z);
487     break;
488   case 3:
489     DMPlex_Mult3D_Internal(A, 1, y, z);
490     break;
491   }
492   PetscFunctionReturn(PETSC_SUCCESS);
493 }
494 
495 static PetscErrorCode DMPlexBasisTransformField_Internal(DM dm, DM tdm, Vec tv, PetscInt p, PetscInt f, PetscBool l2g, PetscScalar *a)
496 {
497   PetscSection       ts;
498   const PetscScalar *ta, *tva;
499   PetscInt           dof;
500 
501   PetscFunctionBeginHot;
502   PetscCall(DMGetLocalSection(tdm, &ts));
503   PetscCall(PetscSectionGetFieldDof(ts, p, f, &dof));
504   PetscCall(VecGetArrayRead(tv, &ta));
505   PetscCall(DMPlexPointLocalFieldRead(tdm, p, f, ta, &tva));
506   if (l2g) {
507     switch (dof) {
508     case 4:
509       DMPlex_Mult2D_Internal(tva, 1, a, a);
510       break;
511     case 9:
512       DMPlex_Mult3D_Internal(tva, 1, a, a);
513       break;
514     }
515   } else {
516     switch (dof) {
517     case 4:
518       DMPlex_MultTranspose2D_Internal(tva, 1, a, a);
519       break;
520     case 9:
521       DMPlex_MultTranspose3D_Internal(tva, 1, a, a);
522       break;
523     }
524   }
525   PetscCall(VecRestoreArrayRead(tv, &ta));
526   PetscFunctionReturn(PETSC_SUCCESS);
527 }
528 
529 static PetscErrorCode DMPlexBasisTransformFieldTensor_Internal(DM dm, DM tdm, Vec tv, PetscInt pf, PetscInt f, PetscInt pg, PetscInt g, PetscBool l2g, PetscInt lda, PetscScalar *a)
530 {
531   PetscSection       s, ts;
532   const PetscScalar *ta, *tvaf, *tvag;
533   PetscInt           fdof, gdof, fpdof, gpdof;
534 
535   PetscFunctionBeginHot;
536   PetscCall(DMGetLocalSection(dm, &s));
537   PetscCall(DMGetLocalSection(tdm, &ts));
538   PetscCall(PetscSectionGetFieldDof(s, pf, f, &fpdof));
539   PetscCall(PetscSectionGetFieldDof(s, pg, g, &gpdof));
540   PetscCall(PetscSectionGetFieldDof(ts, pf, f, &fdof));
541   PetscCall(PetscSectionGetFieldDof(ts, pg, g, &gdof));
542   PetscCall(VecGetArrayRead(tv, &ta));
543   PetscCall(DMPlexPointLocalFieldRead(tdm, pf, f, ta, &tvaf));
544   PetscCall(DMPlexPointLocalFieldRead(tdm, pg, g, ta, &tvag));
545   if (l2g) {
546     switch (fdof) {
547     case 4:
548       DMPlex_MatMult2D_Internal(tvaf, gpdof, lda, a, a);
549       break;
550     case 9:
551       DMPlex_MatMult3D_Internal(tvaf, gpdof, lda, a, a);
552       break;
553     }
554     switch (gdof) {
555     case 4:
556       DMPlex_MatMultTransposeLeft2D_Internal(tvag, fpdof, lda, a, a);
557       break;
558     case 9:
559       DMPlex_MatMultTransposeLeft3D_Internal(tvag, fpdof, lda, a, a);
560       break;
561     }
562   } else {
563     switch (fdof) {
564     case 4:
565       DMPlex_MatMultTranspose2D_Internal(tvaf, gpdof, lda, a, a);
566       break;
567     case 9:
568       DMPlex_MatMultTranspose3D_Internal(tvaf, gpdof, lda, a, a);
569       break;
570     }
571     switch (gdof) {
572     case 4:
573       DMPlex_MatMultLeft2D_Internal(tvag, fpdof, lda, a, a);
574       break;
575     case 9:
576       DMPlex_MatMultLeft3D_Internal(tvag, fpdof, lda, a, a);
577       break;
578     }
579   }
580   PetscCall(VecRestoreArrayRead(tv, &ta));
581   PetscFunctionReturn(PETSC_SUCCESS);
582 }
583 
584 PetscErrorCode DMPlexBasisTransformPoint_Internal(DM dm, DM tdm, Vec tv, PetscInt p, PetscBool fieldActive[], PetscBool l2g, PetscScalar *a)
585 {
586   PetscSection    s;
587   PetscSection    clSection;
588   IS              clPoints;
589   const PetscInt *clp;
590   PetscInt       *points = NULL;
591   PetscInt        Nf, f, Np, cp, dof, d = 0;
592 
593   PetscFunctionBegin;
594   PetscCall(DMGetLocalSection(dm, &s));
595   PetscCall(PetscSectionGetNumFields(s, &Nf));
596   PetscCall(DMPlexGetCompressedClosure(dm, s, p, 0, &Np, &points, &clSection, &clPoints, &clp));
597   for (f = 0; f < Nf; ++f) {
598     for (cp = 0; cp < Np * 2; cp += 2) {
599       PetscCall(PetscSectionGetFieldDof(s, points[cp], f, &dof));
600       if (!dof) continue;
601       if (fieldActive[f]) PetscCall(DMPlexBasisTransformField_Internal(dm, tdm, tv, points[cp], f, l2g, &a[d]));
602       d += dof;
603     }
604   }
605   PetscCall(DMPlexRestoreCompressedClosure(dm, s, p, &Np, &points, &clSection, &clPoints, &clp));
606   PetscFunctionReturn(PETSC_SUCCESS);
607 }
608 
609 PetscErrorCode DMPlexBasisTransformPointTensor_Internal(DM dm, DM tdm, Vec tv, PetscInt p, PetscBool l2g, PetscInt lda, PetscScalar *a)
610 {
611   PetscSection    s;
612   PetscSection    clSection;
613   IS              clPoints;
614   const PetscInt *clp;
615   PetscInt       *points = NULL;
616   PetscInt        Nf, f, g, Np, cpf, cpg, fdof, gdof, r, c = 0;
617 
618   PetscFunctionBegin;
619   PetscCall(DMGetLocalSection(dm, &s));
620   PetscCall(PetscSectionGetNumFields(s, &Nf));
621   PetscCall(DMPlexGetCompressedClosure(dm, s, p, 0, &Np, &points, &clSection, &clPoints, &clp));
622   for (f = 0, r = 0; f < Nf; ++f) {
623     for (cpf = 0; cpf < Np * 2; cpf += 2) {
624       PetscCall(PetscSectionGetFieldDof(s, points[cpf], f, &fdof));
625       for (g = 0, c = 0; g < Nf; ++g) {
626         for (cpg = 0; cpg < Np * 2; cpg += 2) {
627           PetscCall(PetscSectionGetFieldDof(s, points[cpg], g, &gdof));
628           PetscCall(DMPlexBasisTransformFieldTensor_Internal(dm, tdm, tv, points[cpf], f, points[cpg], g, l2g, lda, &a[r * lda + c]));
629           c += gdof;
630         }
631       }
632       PetscCheck(c == lda, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid number of columns %" PetscInt_FMT " should be %" PetscInt_FMT, c, lda);
633       r += fdof;
634     }
635   }
636   PetscCheck(r == lda, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid number of rows %" PetscInt_FMT " should be %" PetscInt_FMT, c, lda);
637   PetscCall(DMPlexRestoreCompressedClosure(dm, s, p, &Np, &points, &clSection, &clPoints, &clp));
638   PetscFunctionReturn(PETSC_SUCCESS);
639 }
640 
641 static PetscErrorCode DMPlexBasisTransform_Internal(DM dm, Vec lv, PetscBool l2g)
642 {
643   DM                 tdm;
644   Vec                tv;
645   PetscSection       ts, s;
646   const PetscScalar *ta;
647   PetscScalar       *a, *va;
648   PetscInt           pStart, pEnd, p, Nf, f;
649 
650   PetscFunctionBegin;
651   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
652   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
653   PetscCall(DMGetLocalSection(tdm, &ts));
654   PetscCall(DMGetLocalSection(dm, &s));
655   PetscCall(PetscSectionGetChart(s, &pStart, &pEnd));
656   PetscCall(PetscSectionGetNumFields(s, &Nf));
657   PetscCall(VecGetArray(lv, &a));
658   PetscCall(VecGetArrayRead(tv, &ta));
659   for (p = pStart; p < pEnd; ++p) {
660     for (f = 0; f < Nf; ++f) {
661       PetscCall(DMPlexPointLocalFieldRef(dm, p, f, a, &va));
662       PetscCall(DMPlexBasisTransformField_Internal(dm, tdm, tv, p, f, l2g, va));
663     }
664   }
665   PetscCall(VecRestoreArray(lv, &a));
666   PetscCall(VecRestoreArrayRead(tv, &ta));
667   PetscFunctionReturn(PETSC_SUCCESS);
668 }
669 
670 /*@
671   DMPlexGlobalToLocalBasis - Transform the values in the given local vector from the global basis to the local basis
672 
673   Input Parameters:
674 + dm - The `DM`
675 - lv - A local vector with values in the global basis
676 
677   Output Parameter:
678 . lv - A local vector with values in the local basis
679 
680   Level: developer
681 
682   Note:
683   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.
684 
685 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexLocalToGlobalBasis()`, `DMGetLocalSection()`, `DMPlexCreateBasisRotation()`
686 @*/
687 PetscErrorCode DMPlexGlobalToLocalBasis(DM dm, Vec lv)
688 {
689   PetscFunctionBegin;
690   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
691   PetscValidHeaderSpecific(lv, VEC_CLASSID, 2);
692   PetscCall(DMPlexBasisTransform_Internal(dm, lv, PETSC_FALSE));
693   PetscFunctionReturn(PETSC_SUCCESS);
694 }
695 
696 /*@
697   DMPlexLocalToGlobalBasis - Transform the values in the given local vector from the local basis to the global basis
698 
699   Input Parameters:
700 + dm - The `DM`
701 - lv - A local vector with values in the local basis
702 
703   Output Parameter:
704 . lv - A local vector with values in the global basis
705 
706   Level: developer
707 
708   Note:
709   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.
710 
711 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGlobalToLocalBasis()`, `DMGetLocalSection()`, `DMPlexCreateBasisRotation()`
712 @*/
713 PetscErrorCode DMPlexLocalToGlobalBasis(DM dm, Vec lv)
714 {
715   PetscFunctionBegin;
716   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
717   PetscValidHeaderSpecific(lv, VEC_CLASSID, 2);
718   PetscCall(DMPlexBasisTransform_Internal(dm, lv, PETSC_TRUE));
719   PetscFunctionReturn(PETSC_SUCCESS);
720 }
721 
722 /*@
723   DMPlexCreateBasisRotation - Create an internal transformation from the global basis, used to specify boundary conditions
724   and global solutions, to a local basis, appropriate for discretization integrals and assembly.
725 
726   Input Parameters:
727 + dm    - The `DM`
728 . alpha - The first Euler angle, and in 2D the only one
729 . beta  - The second Euler angle
730 - gamma - The third Euler angle
731 
732   Level: developer
733 
734   Note:
735   Following https://en.wikipedia.org/wiki/Euler_angles, we will specify Euler angles by extrinsic rotations, meaning that
736   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
737 .vb
738    The XYZ system rotates about the z axis by alpha. The X axis is now at angle alpha with respect to the x axis.
739    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.
740    The XYZ system rotates a third time about the z axis by gamma.
741 .ve
742 
743 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGlobalToLocalBasis()`, `DMPlexLocalToGlobalBasis()`
744 @*/
745 PetscErrorCode DMPlexCreateBasisRotation(DM dm, PetscReal alpha, PetscReal beta, PetscReal gamma)
746 {
747   RotCtx  *rc;
748   PetscInt cdim;
749 
750   PetscFunctionBegin;
751   PetscCall(DMGetCoordinateDim(dm, &cdim));
752   PetscCall(PetscMalloc1(1, &rc));
753   dm->transformCtx       = rc;
754   dm->transformSetUp     = DMPlexBasisTransformSetUp_Rotation_Internal;
755   dm->transformDestroy   = DMPlexBasisTransformDestroy_Rotation_Internal;
756   dm->transformGetMatrix = DMPlexBasisTransformGetMatrix_Rotation_Internal;
757   rc->dim                = cdim;
758   rc->alpha              = alpha;
759   rc->beta               = beta;
760   rc->gamma              = gamma;
761   PetscCall((*dm->transformSetUp)(dm, dm->transformCtx));
762   PetscCall(DMConstructBasisTransform_Internal(dm));
763   PetscFunctionReturn(PETSC_SUCCESS);
764 }
765 
766 /*@C
767   DMPlexInsertBoundaryValuesEssential - Insert boundary values into a local vector using a function of the coordinates
768 
769   Input Parameters:
770 + dm     - The `DM`, with a `PetscDS` that matches the problem being constrained
771 . time   - The time
772 . field  - The field to constrain
773 . Nc     - The number of constrained field components, or 0 for all components
774 . comps  - An array of constrained component numbers, or `NULL` for all components
775 . label  - The `DMLabel` defining constrained points
776 . numids - The number of `DMLabel` ids for constrained points
777 . ids    - An array of ids for constrained points
778 . func   - A pointwise function giving boundary values
779 - ctx    - An optional user context for bcFunc
780 
781   Output Parameter:
782 . locX - A local vector to receives the boundary values
783 
784   Level: developer
785 
786 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLabel`, `DMPlexInsertBoundaryValuesEssentialField()`, `DMPlexInsertBoundaryValuesEssentialBdField()`, `DMAddBoundary()`
787 @*/
788 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)
789 {
790   PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal x[], PetscInt, PetscScalar *u, void *ctx);
791   void   **ctxs;
792   PetscInt numFields;
793 
794   PetscFunctionBegin;
795   PetscCall(DMGetNumFields(dm, &numFields));
796   PetscCall(PetscCalloc2(numFields, &funcs, numFields, &ctxs));
797   funcs[field] = func;
798   ctxs[field]  = ctx;
799   PetscCall(DMProjectFunctionLabelLocal(dm, time, label, numids, ids, Nc, comps, funcs, ctxs, INSERT_BC_VALUES, locX));
800   PetscCall(PetscFree2(funcs, ctxs));
801   PetscFunctionReturn(PETSC_SUCCESS);
802 }
803 
804 /*@C
805   DMPlexInsertBoundaryValuesEssentialField - Insert boundary values into a local vector using a function of the coordinates and field data
806 
807   Input Parameters:
808 + dm     - The `DM`, with a `PetscDS` that matches the problem being constrained
809 . time   - The time
810 . locU   - A local vector with the input solution values
811 . field  - The field to constrain
812 . Nc     - The number of constrained field components, or 0 for all components
813 . comps  - An array of constrained component numbers, or `NULL` for all components
814 . label  - The `DMLabel` defining constrained points
815 . numids - The number of `DMLabel` ids for constrained points
816 . ids    - An array of ids for constrained points
817 . func   - A pointwise function giving boundary values
818 - ctx    - An optional user context for bcFunc
819 
820   Output Parameter:
821 . locX - A local vector to receives the boundary values
822 
823   Level: developer
824 
825 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexInsertBoundaryValuesEssential()`, `DMPlexInsertBoundaryValuesEssentialBdField()`, `DMAddBoundary()`
826 @*/
827 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)
828 {
829   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[]);
830   void   **ctxs;
831   PetscInt numFields;
832 
833   PetscFunctionBegin;
834   PetscCall(DMGetNumFields(dm, &numFields));
835   PetscCall(PetscCalloc2(numFields, &funcs, numFields, &ctxs));
836   funcs[field] = func;
837   ctxs[field]  = ctx;
838   PetscCall(DMProjectFieldLabelLocal(dm, time, label, numids, ids, Nc, comps, locU, funcs, INSERT_BC_VALUES, locX));
839   PetscCall(PetscFree2(funcs, ctxs));
840   PetscFunctionReturn(PETSC_SUCCESS);
841 }
842 
843 /*@C
844   DMPlexInsertBoundaryValuesEssentialBdField - Insert boundary values into a local vector using a function of the coordinates and boundary field data
845 
846   Collective
847 
848   Input Parameters:
849 + dm     - The `DM`, with a `PetscDS` that matches the problem being constrained
850 . time   - The time
851 . locU   - A local vector with the input solution values
852 . field  - The field to constrain
853 . Nc     - The number of constrained field components, or 0 for all components
854 . comps  - An array of constrained component numbers, or `NULL` for all components
855 . label  - The `DMLabel` defining constrained points
856 . numids - The number of `DMLabel` ids for constrained points
857 . ids    - An array of ids for constrained points
858 . func   - A pointwise function giving boundary values, the calling sequence is given in `DMProjectBdFieldLabelLocal()`
859 - ctx    - An optional user context for `func`
860 
861   Output Parameter:
862 . locX - A local vector to receive the boundary values
863 
864   Level: developer
865 
866 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectBdFieldLabelLocal()`, `DMPlexInsertBoundaryValuesEssential()`, `DMPlexInsertBoundaryValuesEssentialField()`, `DMAddBoundary()`
867 @*/
868 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)
869 {
870   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[]);
871   void   **ctxs;
872   PetscInt numFields;
873 
874   PetscFunctionBegin;
875   PetscCall(DMGetNumFields(dm, &numFields));
876   PetscCall(PetscCalloc2(numFields, &funcs, numFields, &ctxs));
877   funcs[field] = func;
878   ctxs[field]  = ctx;
879   PetscCall(DMProjectBdFieldLabelLocal(dm, time, label, numids, ids, Nc, comps, locU, funcs, INSERT_BC_VALUES, locX));
880   PetscCall(PetscFree2(funcs, ctxs));
881   PetscFunctionReturn(PETSC_SUCCESS);
882 }
883 
884 /*@C
885   DMPlexInsertBoundaryValuesRiemann - Insert boundary values into a local vector
886 
887   Input Parameters:
888 + dm           - The `DM`, with a `PetscDS` that matches the problem being constrained
889 . time         - The time
890 . faceGeometry - A vector with the FVM face geometry information
891 . cellGeometry - A vector with the FVM cell geometry information
892 . Grad         - A vector with the FVM cell gradient information
893 . field        - The field to constrain
894 . Nc           - The number of constrained field components, or 0 for all components
895 . comps        - An array of constrained component numbers, or `NULL` for all components
896 . label        - The `DMLabel` defining constrained points
897 . numids       - The number of `DMLabel` ids for constrained points
898 . ids          - An array of ids for constrained points
899 . func         - A pointwise function giving boundary values
900 - ctx          - An optional user context for bcFunc
901 
902   Output Parameter:
903 . locX - A local vector to receives the boundary values
904 
905   Level: developer
906 
907   Note:
908   This implementation currently ignores the numcomps/comps argument from `DMAddBoundary()`
909 
910 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexInsertBoundaryValuesEssential()`, `DMPlexInsertBoundaryValuesEssentialField()`, `DMAddBoundary()`
911 @*/
912 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)
913 {
914   PetscDS            prob;
915   PetscSF            sf;
916   DM                 dmFace, dmCell, dmGrad;
917   const PetscScalar *facegeom, *cellgeom = NULL, *grad;
918   const PetscInt    *leaves;
919   PetscScalar       *x, *fx;
920   PetscInt           dim, nleaves, loc, fStart, fEnd, pdim, i;
921   PetscErrorCode     ierru = PETSC_SUCCESS;
922 
923   PetscFunctionBegin;
924   PetscCall(DMGetPointSF(dm, &sf));
925   PetscCall(PetscSFGetGraph(sf, NULL, &nleaves, &leaves, NULL));
926   nleaves = PetscMax(0, nleaves);
927   PetscCall(DMGetDimension(dm, &dim));
928   PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
929   PetscCall(DMGetDS(dm, &prob));
930   PetscCall(VecGetDM(faceGeometry, &dmFace));
931   PetscCall(VecGetArrayRead(faceGeometry, &facegeom));
932   if (cellGeometry) {
933     PetscCall(VecGetDM(cellGeometry, &dmCell));
934     PetscCall(VecGetArrayRead(cellGeometry, &cellgeom));
935   }
936   if (Grad) {
937     PetscFV fv;
938 
939     PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fv));
940     PetscCall(VecGetDM(Grad, &dmGrad));
941     PetscCall(VecGetArrayRead(Grad, &grad));
942     PetscCall(PetscFVGetNumComponents(fv, &pdim));
943     PetscCall(DMGetWorkArray(dm, pdim, MPIU_SCALAR, &fx));
944   }
945   PetscCall(VecGetArray(locX, &x));
946   for (i = 0; i < numids; ++i) {
947     IS              faceIS;
948     const PetscInt *faces;
949     PetscInt        numFaces, f;
950 
951     PetscCall(DMLabelGetStratumIS(label, ids[i], &faceIS));
952     if (!faceIS) continue; /* No points with that id on this process */
953     PetscCall(ISGetLocalSize(faceIS, &numFaces));
954     PetscCall(ISGetIndices(faceIS, &faces));
955     for (f = 0; f < numFaces; ++f) {
956       const PetscInt   face = faces[f], *cells;
957       PetscFVFaceGeom *fg;
958 
959       if ((face < fStart) || (face >= fEnd)) continue; /* Refinement adds non-faces to labels */
960       PetscCall(PetscFindInt(face, nleaves, (PetscInt *)leaves, &loc));
961       if (loc >= 0) continue;
962       PetscCall(DMPlexPointLocalRead(dmFace, face, facegeom, &fg));
963       PetscCall(DMPlexGetSupport(dm, face, &cells));
964       if (Grad) {
965         PetscFVCellGeom *cg;
966         PetscScalar     *cx, *cgrad;
967         PetscScalar     *xG;
968         PetscReal        dx[3];
969         PetscInt         d;
970 
971         PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cg));
972         PetscCall(DMPlexPointLocalRead(dm, cells[0], x, &cx));
973         PetscCall(DMPlexPointLocalRead(dmGrad, cells[0], grad, &cgrad));
974         PetscCall(DMPlexPointLocalFieldRef(dm, cells[1], field, x, &xG));
975         DMPlex_WaxpyD_Internal(dim, -1, cg->centroid, fg->centroid, dx);
976         for (d = 0; d < pdim; ++d) fx[d] = cx[d] + DMPlex_DotD_Internal(dim, &cgrad[d * dim], dx);
977         PetscCall((*func)(time, fg->centroid, fg->normal, fx, xG, ctx));
978       } else {
979         PetscScalar *xI;
980         PetscScalar *xG;
981 
982         PetscCall(DMPlexPointLocalRead(dm, cells[0], x, &xI));
983         PetscCall(DMPlexPointLocalFieldRef(dm, cells[1], field, x, &xG));
984         ierru = (*func)(time, fg->centroid, fg->normal, xI, xG, ctx);
985         if (ierru) {
986           PetscCall(ISRestoreIndices(faceIS, &faces));
987           PetscCall(ISDestroy(&faceIS));
988           goto cleanup;
989         }
990       }
991     }
992     PetscCall(ISRestoreIndices(faceIS, &faces));
993     PetscCall(ISDestroy(&faceIS));
994   }
995 cleanup:
996   PetscCall(VecRestoreArray(locX, &x));
997   if (Grad) {
998     PetscCall(DMRestoreWorkArray(dm, pdim, MPIU_SCALAR, &fx));
999     PetscCall(VecRestoreArrayRead(Grad, &grad));
1000   }
1001   if (cellGeometry) PetscCall(VecRestoreArrayRead(cellGeometry, &cellgeom));
1002   PetscCall(VecRestoreArrayRead(faceGeometry, &facegeom));
1003   PetscCall(ierru);
1004   PetscFunctionReturn(PETSC_SUCCESS);
1005 }
1006 
1007 static PetscErrorCode zero(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nc, PetscScalar *u, void *ctx)
1008 {
1009   PetscInt c;
1010   for (c = 0; c < Nc; ++c) u[c] = 0.0;
1011   return PETSC_SUCCESS;
1012 }
1013 
1014 PetscErrorCode DMPlexInsertBoundaryValues_Plex(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
1015 {
1016   PetscObject isZero;
1017   PetscDS     prob;
1018   PetscInt    numBd, b;
1019 
1020   PetscFunctionBegin;
1021   PetscCall(DMGetDS(dm, &prob));
1022   PetscCall(PetscDSGetNumBoundary(prob, &numBd));
1023   PetscCall(PetscObjectQuery((PetscObject)locX, "__Vec_bc_zero__", &isZero));
1024   for (b = 0; b < numBd; ++b) {
1025     PetscWeakForm           wf;
1026     DMBoundaryConditionType type;
1027     const char             *name;
1028     DMLabel                 label;
1029     PetscInt                field, Nc;
1030     const PetscInt         *comps;
1031     PetscObject             obj;
1032     PetscClassId            id;
1033     void (*bvfunc)(void);
1034     PetscInt        numids;
1035     const PetscInt *ids;
1036     void           *ctx;
1037 
1038     PetscCall(PetscDSGetBoundary(prob, b, &wf, &type, &name, &label, &numids, &ids, &field, &Nc, &comps, &bvfunc, NULL, &ctx));
1039     if (insertEssential != (type & DM_BC_ESSENTIAL)) continue;
1040     PetscCall(DMGetField(dm, field, NULL, &obj));
1041     PetscCall(PetscObjectGetClassId(obj, &id));
1042     if (id == PETSCFE_CLASSID) {
1043       switch (type) {
1044         /* for FEM, there is no insertion to be done for non-essential boundary conditions */
1045       case DM_BC_ESSENTIAL: {
1046         PetscSimplePointFunc func = (PetscSimplePointFunc)bvfunc;
1047 
1048         if (isZero) func = zero;
1049         PetscCall(DMPlexLabelAddCells(dm, label));
1050         PetscCall(DMPlexInsertBoundaryValuesEssential(dm, time, field, Nc, comps, label, numids, ids, func, ctx, locX));
1051         PetscCall(DMPlexLabelClearCells(dm, label));
1052       } break;
1053       case DM_BC_ESSENTIAL_FIELD: {
1054         PetscPointFunc func = (PetscPointFunc)bvfunc;
1055 
1056         PetscCall(DMPlexLabelAddCells(dm, label));
1057         PetscCall(DMPlexInsertBoundaryValuesEssentialField(dm, time, locX, field, Nc, comps, label, numids, ids, func, ctx, locX));
1058         PetscCall(DMPlexLabelClearCells(dm, label));
1059       } break;
1060       default:
1061         break;
1062       }
1063     } else if (id == PETSCFV_CLASSID) {
1064       {
1065         PetscErrorCode (*func)(PetscReal, const PetscReal *, const PetscReal *, const PetscScalar *, PetscScalar *, void *) = (PetscErrorCode(*)(PetscReal, const PetscReal *, const PetscReal *, const PetscScalar *, PetscScalar *, void *))bvfunc;
1066 
1067         if (!faceGeomFVM) continue;
1068         PetscCall(DMPlexInsertBoundaryValuesRiemann(dm, time, faceGeomFVM, cellGeomFVM, gradFVM, field, Nc, comps, label, numids, ids, func, ctx, locX));
1069       }
1070     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1071   }
1072   PetscFunctionReturn(PETSC_SUCCESS);
1073 }
1074 
1075 PetscErrorCode DMPlexInsertTimeDerivativeBoundaryValues_Plex(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
1076 {
1077   PetscObject isZero;
1078   PetscDS     prob;
1079   PetscInt    numBd, b;
1080 
1081   PetscFunctionBegin;
1082   if (!locX) PetscFunctionReturn(PETSC_SUCCESS);
1083   PetscCall(DMGetDS(dm, &prob));
1084   PetscCall(PetscDSGetNumBoundary(prob, &numBd));
1085   PetscCall(PetscObjectQuery((PetscObject)locX, "__Vec_bc_zero__", &isZero));
1086   for (b = 0; b < numBd; ++b) {
1087     PetscWeakForm           wf;
1088     DMBoundaryConditionType type;
1089     const char             *name;
1090     DMLabel                 label;
1091     PetscInt                field, Nc;
1092     const PetscInt         *comps;
1093     PetscObject             obj;
1094     PetscClassId            id;
1095     PetscInt                numids;
1096     const PetscInt         *ids;
1097     void (*bvfunc)(void);
1098     void *ctx;
1099 
1100     PetscCall(PetscDSGetBoundary(prob, b, &wf, &type, &name, &label, &numids, &ids, &field, &Nc, &comps, NULL, &bvfunc, &ctx));
1101     if (insertEssential != (type & DM_BC_ESSENTIAL)) continue;
1102     PetscCall(DMGetField(dm, field, NULL, &obj));
1103     PetscCall(PetscObjectGetClassId(obj, &id));
1104     if (id == PETSCFE_CLASSID) {
1105       switch (type) {
1106         /* for FEM, there is no insertion to be done for non-essential boundary conditions */
1107       case DM_BC_ESSENTIAL: {
1108         PetscSimplePointFunc func_t = (PetscSimplePointFunc)bvfunc;
1109 
1110         if (isZero) func_t = zero;
1111         PetscCall(DMPlexLabelAddCells(dm, label));
1112         PetscCall(DMPlexInsertBoundaryValuesEssential(dm, time, field, Nc, comps, label, numids, ids, func_t, ctx, locX));
1113         PetscCall(DMPlexLabelClearCells(dm, label));
1114       } break;
1115       case DM_BC_ESSENTIAL_FIELD: {
1116         PetscPointFunc func_t = (PetscPointFunc)bvfunc;
1117 
1118         PetscCall(DMPlexLabelAddCells(dm, label));
1119         PetscCall(DMPlexInsertBoundaryValuesEssentialField(dm, time, locX, field, Nc, comps, label, numids, ids, func_t, ctx, locX));
1120         PetscCall(DMPlexLabelClearCells(dm, label));
1121       } break;
1122       default:
1123         break;
1124       }
1125     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1126   }
1127   PetscFunctionReturn(PETSC_SUCCESS);
1128 }
1129 
1130 /*@
1131   DMPlexInsertBoundaryValues - Puts coefficients which represent boundary values into the local solution vector
1132 
1133   Not Collective
1134 
1135   Input Parameters:
1136 + dm              - The `DM`
1137 . insertEssential - Should I insert essential (e.g. Dirichlet) or inessential (e.g. Neumann) boundary conditions
1138 . time            - The time
1139 . faceGeomFVM     - Face geometry data for FV discretizations
1140 . cellGeomFVM     - Cell geometry data for FV discretizations
1141 - gradFVM         - Gradient reconstruction data for FV discretizations
1142 
1143   Output Parameter:
1144 . locX - Solution updated with boundary values
1145 
1146   Level: intermediate
1147 
1148 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunctionLabelLocal()`, `DMAddBoundary()`
1149 @*/
1150 PetscErrorCode DMPlexInsertBoundaryValues(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
1151 {
1152   PetscFunctionBegin;
1153   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1154   PetscValidHeaderSpecific(locX, VEC_CLASSID, 3);
1155   if (faceGeomFVM) PetscValidHeaderSpecific(faceGeomFVM, VEC_CLASSID, 5);
1156   if (cellGeomFVM) PetscValidHeaderSpecific(cellGeomFVM, VEC_CLASSID, 6);
1157   if (gradFVM) PetscValidHeaderSpecific(gradFVM, VEC_CLASSID, 7);
1158   PetscTryMethod(dm, "DMPlexInsertBoundaryValues_C", (DM, PetscBool, Vec, PetscReal, Vec, Vec, Vec), (dm, insertEssential, locX, time, faceGeomFVM, cellGeomFVM, gradFVM));
1159   PetscFunctionReturn(PETSC_SUCCESS);
1160 }
1161 
1162 /*@
1163   DMPlexInsertTimeDerivativeBoundaryValues - Puts coefficients which represent boundary values of the time derivative into the local solution vector
1164 
1165   Input Parameters:
1166 + dm              - The `DM`
1167 . insertEssential - Should I insert essential (e.g. Dirichlet) or inessential (e.g. Neumann) boundary conditions
1168 . time            - The time
1169 . faceGeomFVM     - Face geometry data for FV discretizations
1170 . cellGeomFVM     - Cell geometry data for FV discretizations
1171 - gradFVM         - Gradient reconstruction data for FV discretizations
1172 
1173   Output Parameter:
1174 . locX_t - Solution updated with boundary values
1175 
1176   Level: developer
1177 
1178 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunctionLabelLocal()`
1179 @*/
1180 PetscErrorCode DMPlexInsertTimeDerivativeBoundaryValues(DM dm, PetscBool insertEssential, Vec locX_t, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
1181 {
1182   PetscFunctionBegin;
1183   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1184   if (locX_t) PetscValidHeaderSpecific(locX_t, VEC_CLASSID, 3);
1185   if (faceGeomFVM) PetscValidHeaderSpecific(faceGeomFVM, VEC_CLASSID, 5);
1186   if (cellGeomFVM) PetscValidHeaderSpecific(cellGeomFVM, VEC_CLASSID, 6);
1187   if (gradFVM) PetscValidHeaderSpecific(gradFVM, VEC_CLASSID, 7);
1188   PetscTryMethod(dm, "DMPlexInsertTimeDerviativeBoundaryValues_C", (DM, PetscBool, Vec, PetscReal, Vec, Vec, Vec), (dm, insertEssential, locX_t, time, faceGeomFVM, cellGeomFVM, gradFVM));
1189   PetscFunctionReturn(PETSC_SUCCESS);
1190 }
1191 
1192 PetscErrorCode DMComputeL2Diff_Plex(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, PetscReal *diff)
1193 {
1194   Vec localX;
1195 
1196   PetscFunctionBegin;
1197   PetscCall(DMGetLocalVector(dm, &localX));
1198   PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, localX, time, NULL, NULL, NULL));
1199   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
1200   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
1201   PetscCall(DMPlexComputeL2DiffLocal(dm, time, funcs, ctxs, localX, diff));
1202   PetscCall(DMRestoreLocalVector(dm, &localX));
1203   PetscFunctionReturn(PETSC_SUCCESS);
1204 }
1205 
1206 /*@C
1207   DMPlexComputeL2DiffLocal - This function computes the L_2 difference between a function u and an FEM interpolant solution u_h.
1208 
1209   Collective
1210 
1211   Input Parameters:
1212 + dm     - The `DM`
1213 . time   - The time
1214 . funcs  - The functions to evaluate for each field component
1215 . ctxs   - Optional array of contexts to pass to each function, or `NULL`.
1216 - localX - The coefficient vector u_h, a local vector
1217 
1218   Output Parameter:
1219 . diff - The diff ||u - u_h||_2
1220 
1221   Level: developer
1222 
1223 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
1224 @*/
1225 PetscErrorCode DMPlexComputeL2DiffLocal(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec localX, PetscReal *diff)
1226 {
1227   const PetscInt   debug = ((DM_Plex *)dm->data)->printL2;
1228   DM               tdm;
1229   Vec              tv;
1230   PetscSection     section;
1231   PetscQuadrature  quad;
1232   PetscFEGeom      fegeom;
1233   PetscScalar     *funcVal, *interpolant;
1234   PetscReal       *coords, *gcoords;
1235   PetscReal        localDiff = 0.0;
1236   const PetscReal *quadWeights;
1237   PetscInt         dim, coordDim, numFields, numComponents = 0, qNc, Nq, cellHeight, cStart, cEnd, c, field, fieldOffset;
1238   PetscBool        transform;
1239 
1240   PetscFunctionBegin;
1241   PetscCall(DMGetDimension(dm, &dim));
1242   PetscCall(DMGetCoordinateDim(dm, &coordDim));
1243   fegeom.dimEmbed = coordDim;
1244   PetscCall(DMGetLocalSection(dm, &section));
1245   PetscCall(PetscSectionGetNumFields(section, &numFields));
1246   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
1247   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
1248   PetscCall(DMHasBasisTransform(dm, &transform));
1249   PetscCheck(numFields, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fields is zero!");
1250   for (field = 0; field < numFields; ++field) {
1251     PetscObject  obj;
1252     PetscClassId id;
1253     PetscInt     Nc;
1254 
1255     PetscCall(DMGetField(dm, field, NULL, &obj));
1256     PetscCall(PetscObjectGetClassId(obj, &id));
1257     if (id == PETSCFE_CLASSID) {
1258       PetscFE fe = (PetscFE)obj;
1259 
1260       PetscCall(PetscFEGetQuadrature(fe, &quad));
1261       PetscCall(PetscFEGetNumComponents(fe, &Nc));
1262     } else if (id == PETSCFV_CLASSID) {
1263       PetscFV fv = (PetscFV)obj;
1264 
1265       PetscCall(PetscFVGetQuadrature(fv, &quad));
1266       PetscCall(PetscFVGetNumComponents(fv, &Nc));
1267     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1268     numComponents += Nc;
1269   }
1270   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, NULL, &quadWeights));
1271   PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
1272   PetscCall(PetscMalloc6(numComponents, &funcVal, numComponents, &interpolant, coordDim * (Nq + 1), &coords, Nq, &fegeom.detJ, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ));
1273   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1274   PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
1275   for (c = cStart; c < cEnd; ++c) {
1276     PetscScalar *x        = NULL;
1277     PetscReal    elemDiff = 0.0;
1278     PetscInt     qc       = 0;
1279 
1280     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1281     PetscCall(DMPlexVecGetClosure(dm, NULL, localX, c, NULL, &x));
1282 
1283     for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1284       PetscObject  obj;
1285       PetscClassId id;
1286       void *const  ctx = ctxs ? ctxs[field] : NULL;
1287       PetscInt     Nb, Nc, q, fc;
1288 
1289       PetscCall(DMGetField(dm, field, NULL, &obj));
1290       PetscCall(PetscObjectGetClassId(obj, &id));
1291       if (id == PETSCFE_CLASSID) {
1292         PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
1293         PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
1294       } else if (id == PETSCFV_CLASSID) {
1295         PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
1296         Nb = 1;
1297       } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1298       if (debug) {
1299         char title[1024];
1300         PetscCall(PetscSNPrintf(title, 1023, "Solution for Field %" PetscInt_FMT, field));
1301         PetscCall(DMPrintCellVector(c, title, Nb, &x[fieldOffset]));
1302       }
1303       for (q = 0; q < Nq; ++q) {
1304         PetscFEGeom    qgeom;
1305         PetscErrorCode ierr;
1306 
1307         qgeom.dimEmbed = fegeom.dimEmbed;
1308         qgeom.J        = &fegeom.J[q * coordDim * coordDim];
1309         qgeom.invJ     = &fegeom.invJ[q * coordDim * coordDim];
1310         qgeom.detJ     = &fegeom.detJ[q];
1311         PetscCheck(fegeom.detJ[q] > 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for element %" PetscInt_FMT ", point %" PetscInt_FMT, (double)fegeom.detJ[q], c, q);
1312         if (transform) {
1313           gcoords = &coords[coordDim * Nq];
1314           PetscCall(DMPlexBasisTransformApplyReal_Internal(dm, &coords[coordDim * q], PETSC_TRUE, coordDim, &coords[coordDim * q], gcoords, dm->transformCtx));
1315         } else {
1316           gcoords = &coords[coordDim * q];
1317         }
1318         PetscCall(PetscArrayzero(funcVal, Nc));
1319         ierr = (*funcs[field])(coordDim, time, gcoords, Nc, funcVal, ctx);
1320         if (ierr) {
1321           PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1322           PetscCall(DMRestoreLocalVector(dm, &localX));
1323           PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1324         }
1325         if (transform) PetscCall(DMPlexBasisTransformApply_Internal(dm, &coords[coordDim * q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx));
1326         if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[fieldOffset], &qgeom, q, interpolant));
1327         else if (id == PETSCFV_CLASSID) PetscCall(PetscFVInterpolate_Static((PetscFV)obj, &x[fieldOffset], q, interpolant));
1328         else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1329         for (fc = 0; fc < Nc; ++fc) {
1330           const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
1331           if (debug)
1332             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.),
1333                                   (double)(PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q]), (double)PetscRealPart(interpolant[fc]), (double)PetscRealPart(funcVal[fc])));
1334           elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
1335         }
1336       }
1337       fieldOffset += Nb;
1338       qc += Nc;
1339     }
1340     PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1341     if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  elem %" PetscInt_FMT " diff %g\n", c, (double)elemDiff));
1342     localDiff += elemDiff;
1343   }
1344   PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1345   PetscCall(MPIU_Allreduce(&localDiff, diff, 1, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
1346   *diff = PetscSqrtReal(*diff);
1347   PetscFunctionReturn(PETSC_SUCCESS);
1348 }
1349 
1350 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)
1351 {
1352   const PetscInt   debug = ((DM_Plex *)dm->data)->printL2;
1353   DM               tdm;
1354   PetscSection     section;
1355   PetscQuadrature  quad;
1356   Vec              localX, tv;
1357   PetscScalar     *funcVal, *interpolant;
1358   const PetscReal *quadWeights;
1359   PetscFEGeom      fegeom;
1360   PetscReal       *coords, *gcoords;
1361   PetscReal        localDiff = 0.0;
1362   PetscInt         dim, coordDim, qNc = 0, Nq = 0, numFields, numComponents = 0, cStart, cEnd, c, field, fieldOffset;
1363   PetscBool        transform;
1364 
1365   PetscFunctionBegin;
1366   PetscCall(DMGetDimension(dm, &dim));
1367   PetscCall(DMGetCoordinateDim(dm, &coordDim));
1368   fegeom.dimEmbed = coordDim;
1369   PetscCall(DMGetLocalSection(dm, &section));
1370   PetscCall(PetscSectionGetNumFields(section, &numFields));
1371   PetscCall(DMGetLocalVector(dm, &localX));
1372   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
1373   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
1374   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
1375   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
1376   PetscCall(DMHasBasisTransform(dm, &transform));
1377   for (field = 0; field < numFields; ++field) {
1378     PetscFE  fe;
1379     PetscInt Nc;
1380 
1381     PetscCall(DMGetField(dm, field, NULL, (PetscObject *)&fe));
1382     PetscCall(PetscFEGetQuadrature(fe, &quad));
1383     PetscCall(PetscFEGetNumComponents(fe, &Nc));
1384     numComponents += Nc;
1385   }
1386   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, NULL, &quadWeights));
1387   PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
1388   /* PetscCall(DMProjectFunctionLocal(dm, fe, funcs, INSERT_BC_VALUES, localX)); */
1389   PetscCall(PetscMalloc6(numComponents, &funcVal, coordDim * (Nq + 1), &coords, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ, numComponents * coordDim, &interpolant, Nq, &fegeom.detJ));
1390   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
1391   for (c = cStart; c < cEnd; ++c) {
1392     PetscScalar *x        = NULL;
1393     PetscReal    elemDiff = 0.0;
1394     PetscInt     qc       = 0;
1395 
1396     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1397     PetscCall(DMPlexVecGetClosure(dm, NULL, localX, c, NULL, &x));
1398 
1399     for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1400       PetscFE     fe;
1401       void *const ctx = ctxs ? ctxs[field] : NULL;
1402       PetscInt    Nb, Nc, q, fc;
1403 
1404       PetscCall(DMGetField(dm, field, NULL, (PetscObject *)&fe));
1405       PetscCall(PetscFEGetDimension(fe, &Nb));
1406       PetscCall(PetscFEGetNumComponents(fe, &Nc));
1407       if (debug) {
1408         char title[1024];
1409         PetscCall(PetscSNPrintf(title, 1023, "Solution for Field %" PetscInt_FMT, field));
1410         PetscCall(DMPrintCellVector(c, title, Nb, &x[fieldOffset]));
1411       }
1412       for (q = 0; q < Nq; ++q) {
1413         PetscFEGeom    qgeom;
1414         PetscErrorCode ierr;
1415 
1416         qgeom.dimEmbed = fegeom.dimEmbed;
1417         qgeom.J        = &fegeom.J[q * coordDim * coordDim];
1418         qgeom.invJ     = &fegeom.invJ[q * coordDim * coordDim];
1419         qgeom.detJ     = &fegeom.detJ[q];
1420         PetscCheck(fegeom.detJ[q] > 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for element %" PetscInt_FMT ", quadrature points %" PetscInt_FMT, (double)fegeom.detJ[q], c, q);
1421         if (transform) {
1422           gcoords = &coords[coordDim * Nq];
1423           PetscCall(DMPlexBasisTransformApplyReal_Internal(dm, &coords[coordDim * q], PETSC_TRUE, coordDim, &coords[coordDim * q], gcoords, dm->transformCtx));
1424         } else {
1425           gcoords = &coords[coordDim * q];
1426         }
1427         PetscCall(PetscArrayzero(funcVal, Nc));
1428         ierr = (*funcs[field])(coordDim, time, gcoords, n, Nc, funcVal, ctx);
1429         if (ierr) {
1430           PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1431           PetscCall(DMRestoreLocalVector(dm, &localX));
1432           PetscCall(PetscFree6(funcVal, coords, fegeom.J, fegeom.invJ, interpolant, fegeom.detJ));
1433         }
1434         if (transform) PetscCall(DMPlexBasisTransformApply_Internal(dm, &coords[coordDim * q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx));
1435         PetscCall(PetscFEInterpolateGradient_Static(fe, 1, &x[fieldOffset], &qgeom, q, interpolant));
1436         /* Overwrite with the dot product if the normal is given */
1437         if (n) {
1438           for (fc = 0; fc < Nc; ++fc) {
1439             PetscScalar sum = 0.0;
1440             PetscInt    d;
1441             for (d = 0; d < dim; ++d) sum += interpolant[fc * dim + d] * n[d];
1442             interpolant[fc] = sum;
1443           }
1444         }
1445         for (fc = 0; fc < Nc; ++fc) {
1446           const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
1447           if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "    elem %" PetscInt_FMT " fieldDer %" PetscInt_FMT ",%" PetscInt_FMT " diff %g\n", c, field, fc, (double)(PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q])));
1448           elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
1449         }
1450       }
1451       fieldOffset += Nb;
1452       qc += Nc;
1453     }
1454     PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1455     if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  elem %" PetscInt_FMT " diff %g\n", c, (double)elemDiff));
1456     localDiff += elemDiff;
1457   }
1458   PetscCall(PetscFree6(funcVal, coords, fegeom.J, fegeom.invJ, interpolant, fegeom.detJ));
1459   PetscCall(DMRestoreLocalVector(dm, &localX));
1460   PetscCall(MPIU_Allreduce(&localDiff, diff, 1, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
1461   *diff = PetscSqrtReal(*diff);
1462   PetscFunctionReturn(PETSC_SUCCESS);
1463 }
1464 
1465 PetscErrorCode DMComputeL2FieldDiff_Plex(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, PetscReal *diff)
1466 {
1467   const PetscInt debug = ((DM_Plex *)dm->data)->printL2;
1468   DM             tdm;
1469   DMLabel        depthLabel;
1470   PetscSection   section;
1471   Vec            localX, tv;
1472   PetscReal     *localDiff;
1473   PetscInt       dim, depth, dE, Nf, f, Nds, s;
1474   PetscBool      transform;
1475 
1476   PetscFunctionBegin;
1477   PetscCall(DMGetDimension(dm, &dim));
1478   PetscCall(DMGetCoordinateDim(dm, &dE));
1479   PetscCall(DMGetLocalSection(dm, &section));
1480   PetscCall(DMGetLocalVector(dm, &localX));
1481   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
1482   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
1483   PetscCall(DMHasBasisTransform(dm, &transform));
1484   PetscCall(DMGetNumFields(dm, &Nf));
1485   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
1486   PetscCall(DMLabelGetNumValues(depthLabel, &depth));
1487 
1488   PetscCall(VecSet(localX, 0.0));
1489   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
1490   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
1491   PetscCall(DMProjectFunctionLocal(dm, time, funcs, ctxs, INSERT_BC_VALUES, localX));
1492   PetscCall(DMGetNumDS(dm, &Nds));
1493   PetscCall(PetscCalloc1(Nf, &localDiff));
1494   for (s = 0; s < Nds; ++s) {
1495     PetscDS          ds;
1496     DMLabel          label;
1497     IS               fieldIS, pointIS;
1498     const PetscInt  *fields, *points = NULL;
1499     PetscQuadrature  quad;
1500     const PetscReal *quadPoints, *quadWeights;
1501     PetscFEGeom      fegeom;
1502     PetscReal       *coords, *gcoords;
1503     PetscScalar     *funcVal, *interpolant;
1504     PetscBool        isCohesive;
1505     PetscInt         qNc, Nq, totNc, cStart = 0, cEnd, c, dsNf;
1506 
1507     PetscCall(DMGetRegionNumDS(dm, s, &label, &fieldIS, &ds, NULL));
1508     PetscCall(ISGetIndices(fieldIS, &fields));
1509     PetscCall(PetscDSIsCohesive(ds, &isCohesive));
1510     PetscCall(PetscDSGetNumFields(ds, &dsNf));
1511     PetscCall(PetscDSGetTotalComponents(ds, &totNc));
1512     PetscCall(PetscDSGetQuadrature(ds, &quad));
1513     PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
1514     PetscCheck(!(qNc != 1) || !(qNc != totNc), PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, totNc);
1515     PetscCall(PetscCalloc6(totNc, &funcVal, totNc, &interpolant, dE * (Nq + 1), &coords, Nq, &fegeom.detJ, dE * dE * Nq, &fegeom.J, dE * dE * Nq, &fegeom.invJ));
1516     if (!label) {
1517       PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
1518     } else {
1519       PetscCall(DMLabelGetStratumIS(label, 1, &pointIS));
1520       PetscCall(ISGetLocalSize(pointIS, &cEnd));
1521       PetscCall(ISGetIndices(pointIS, &points));
1522     }
1523     for (c = cStart; c < cEnd; ++c) {
1524       const PetscInt  cell = points ? points[c] : c;
1525       PetscScalar    *x    = NULL;
1526       const PetscInt *cone;
1527       PetscInt        qc = 0, fOff = 0, dep;
1528 
1529       PetscCall(DMLabelGetValue(depthLabel, cell, &dep));
1530       if (dep != depth - 1) continue;
1531       if (isCohesive) {
1532         PetscCall(DMPlexGetCone(dm, cell, &cone));
1533         PetscCall(DMPlexComputeCellGeometryFEM(dm, cone[0], quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1534       } else {
1535         PetscCall(DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1536       }
1537       PetscCall(DMPlexVecGetClosure(dm, NULL, localX, cell, NULL, &x));
1538       for (f = 0; f < dsNf; ++f) {
1539         PetscObject  obj;
1540         PetscClassId id;
1541         void *const  ctx = ctxs ? ctxs[fields[f]] : NULL;
1542         PetscInt     Nb, Nc, q, fc;
1543         PetscReal    elemDiff = 0.0;
1544         PetscBool    cohesive;
1545 
1546         PetscCall(PetscDSGetCohesive(ds, f, &cohesive));
1547         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
1548         PetscCall(PetscObjectGetClassId(obj, &id));
1549         if (id == PETSCFE_CLASSID) {
1550           PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
1551           PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
1552         } else if (id == PETSCFV_CLASSID) {
1553           PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
1554           Nb = 1;
1555         } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, fields[f]);
1556         if (isCohesive && !cohesive) {
1557           fOff += Nb * 2;
1558           qc += Nc;
1559           continue;
1560         }
1561         if (debug) {
1562           char title[1024];
1563           PetscCall(PetscSNPrintf(title, 1023, "Solution for Field %" PetscInt_FMT, fields[f]));
1564           PetscCall(DMPrintCellVector(cell, title, Nb, &x[fOff]));
1565         }
1566         for (q = 0; q < Nq; ++q) {
1567           PetscFEGeom    qgeom;
1568           PetscErrorCode ierr;
1569 
1570           qgeom.dimEmbed = fegeom.dimEmbed;
1571           qgeom.J        = &fegeom.J[q * dE * dE];
1572           qgeom.invJ     = &fegeom.invJ[q * dE * dE];
1573           qgeom.detJ     = &fegeom.detJ[q];
1574           PetscCheck(fegeom.detJ[q] > 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for cell %" PetscInt_FMT ", quadrature point %" PetscInt_FMT, (double)fegeom.detJ[q], cell, q);
1575           if (transform) {
1576             gcoords = &coords[dE * Nq];
1577             PetscCall(DMPlexBasisTransformApplyReal_Internal(dm, &coords[dE * q], PETSC_TRUE, dE, &coords[dE * q], gcoords, dm->transformCtx));
1578           } else {
1579             gcoords = &coords[dE * q];
1580           }
1581           for (fc = 0; fc < Nc; ++fc) funcVal[fc] = 0.;
1582           ierr = (*funcs[fields[f]])(dE, time, gcoords, Nc, funcVal, ctx);
1583           if (ierr) {
1584             PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, cell, NULL, &x));
1585             PetscCall(DMRestoreLocalVector(dm, &localX));
1586             PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1587           }
1588           if (transform) PetscCall(DMPlexBasisTransformApply_Internal(dm, &coords[dE * q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx));
1589           /* Call once for each face, except for lagrange field */
1590           if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[fOff], &qgeom, q, interpolant));
1591           else if (id == PETSCFV_CLASSID) PetscCall(PetscFVInterpolate_Static((PetscFV)obj, &x[fOff], q, interpolant));
1592           else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, fields[f]);
1593           for (fc = 0; fc < Nc; ++fc) {
1594             const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
1595             if (debug)
1596               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.),
1597                                     (double)(PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q])));
1598             elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
1599           }
1600         }
1601         fOff += Nb;
1602         qc += Nc;
1603         localDiff[fields[f]] += elemDiff;
1604         if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  cell %" PetscInt_FMT " field %" PetscInt_FMT " cum diff %g\n", cell, fields[f], (double)localDiff[fields[f]]));
1605       }
1606       PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, cell, NULL, &x));
1607     }
1608     if (label) {
1609       PetscCall(ISRestoreIndices(pointIS, &points));
1610       PetscCall(ISDestroy(&pointIS));
1611     }
1612     PetscCall(ISRestoreIndices(fieldIS, &fields));
1613     PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1614   }
1615   PetscCall(DMRestoreLocalVector(dm, &localX));
1616   PetscCall(MPIU_Allreduce(localDiff, diff, Nf, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
1617   PetscCall(PetscFree(localDiff));
1618   for (f = 0; f < Nf; ++f) diff[f] = PetscSqrtReal(diff[f]);
1619   PetscFunctionReturn(PETSC_SUCCESS);
1620 }
1621 
1622 /*@C
1623   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.
1624 
1625   Collective
1626 
1627   Input Parameters:
1628 + dm    - The `DM`
1629 . time  - The time
1630 . funcs - The functions to evaluate for each field component: `NULL` means that component does not contribute to error calculation
1631 . ctxs  - Optional array of contexts to pass to each function, or `NULL`.
1632 - X     - The coefficient vector u_h
1633 
1634   Output Parameter:
1635 . D - A `Vec` which holds the difference ||u - u_h||_2 for each cell
1636 
1637   Level: developer
1638 
1639 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
1640 @*/
1641 PetscErrorCode DMPlexComputeL2DiffVec(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, Vec D)
1642 {
1643   PetscSection     section;
1644   PetscQuadrature  quad;
1645   Vec              localX;
1646   PetscFEGeom      fegeom;
1647   PetscScalar     *funcVal, *interpolant;
1648   PetscReal       *coords;
1649   const PetscReal *quadPoints, *quadWeights;
1650   PetscInt         dim, coordDim, numFields, numComponents = 0, qNc, Nq, cStart, cEnd, c, field, fieldOffset;
1651 
1652   PetscFunctionBegin;
1653   PetscCall(VecSet(D, 0.0));
1654   PetscCall(DMGetDimension(dm, &dim));
1655   PetscCall(DMGetCoordinateDim(dm, &coordDim));
1656   PetscCall(DMGetLocalSection(dm, &section));
1657   PetscCall(PetscSectionGetNumFields(section, &numFields));
1658   PetscCall(DMGetLocalVector(dm, &localX));
1659   PetscCall(DMProjectFunctionLocal(dm, time, funcs, ctxs, INSERT_BC_VALUES, localX));
1660   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
1661   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
1662   for (field = 0; field < numFields; ++field) {
1663     PetscObject  obj;
1664     PetscClassId id;
1665     PetscInt     Nc;
1666 
1667     PetscCall(DMGetField(dm, field, NULL, &obj));
1668     PetscCall(PetscObjectGetClassId(obj, &id));
1669     if (id == PETSCFE_CLASSID) {
1670       PetscFE fe = (PetscFE)obj;
1671 
1672       PetscCall(PetscFEGetQuadrature(fe, &quad));
1673       PetscCall(PetscFEGetNumComponents(fe, &Nc));
1674     } else if (id == PETSCFV_CLASSID) {
1675       PetscFV fv = (PetscFV)obj;
1676 
1677       PetscCall(PetscFVGetQuadrature(fv, &quad));
1678       PetscCall(PetscFVGetNumComponents(fv, &Nc));
1679     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1680     numComponents += Nc;
1681   }
1682   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
1683   PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
1684   PetscCall(PetscMalloc6(numComponents, &funcVal, numComponents, &interpolant, coordDim * Nq, &coords, Nq, &fegeom.detJ, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ));
1685   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
1686   for (c = cStart; c < cEnd; ++c) {
1687     PetscScalar *x        = NULL;
1688     PetscScalar  elemDiff = 0.0;
1689     PetscInt     qc       = 0;
1690 
1691     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1692     PetscCall(DMPlexVecGetClosure(dm, NULL, localX, c, NULL, &x));
1693 
1694     for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1695       PetscObject  obj;
1696       PetscClassId id;
1697       void *const  ctx = ctxs ? ctxs[field] : NULL;
1698       PetscInt     Nb, Nc, q, fc;
1699 
1700       PetscCall(DMGetField(dm, field, NULL, &obj));
1701       PetscCall(PetscObjectGetClassId(obj, &id));
1702       if (id == PETSCFE_CLASSID) {
1703         PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
1704         PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
1705       } else if (id == PETSCFV_CLASSID) {
1706         PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
1707         Nb = 1;
1708       } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1709       if (funcs[field]) {
1710         for (q = 0; q < Nq; ++q) {
1711           PetscFEGeom qgeom;
1712 
1713           qgeom.dimEmbed = fegeom.dimEmbed;
1714           qgeom.J        = &fegeom.J[q * coordDim * coordDim];
1715           qgeom.invJ     = &fegeom.invJ[q * coordDim * coordDim];
1716           qgeom.detJ     = &fegeom.detJ[q];
1717           PetscCheck(fegeom.detJ[q] > 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for element %" PetscInt_FMT ", quadrature points %" PetscInt_FMT, (double)fegeom.detJ[q], c, q);
1718           PetscCall((*funcs[field])(coordDim, time, &coords[q * coordDim], Nc, funcVal, ctx));
1719 #if defined(needs_fix_with_return_code_argument)
1720           if (ierr) {
1721             PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1722             PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1723             PetscCall(DMRestoreLocalVector(dm, &localX));
1724           }
1725 #endif
1726           if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[fieldOffset], &qgeom, q, interpolant));
1727           else if (id == PETSCFV_CLASSID) PetscCall(PetscFVInterpolate_Static((PetscFV)obj, &x[fieldOffset], q, interpolant));
1728           else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1729           for (fc = 0; fc < Nc; ++fc) {
1730             const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
1731             elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
1732           }
1733         }
1734       }
1735       fieldOffset += Nb;
1736       qc += Nc;
1737     }
1738     PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1739     PetscCall(VecSetValue(D, c - cStart, elemDiff, INSERT_VALUES));
1740   }
1741   PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1742   PetscCall(DMRestoreLocalVector(dm, &localX));
1743   PetscCall(VecSqrtAbs(D));
1744   PetscFunctionReturn(PETSC_SUCCESS);
1745 }
1746 
1747 /*@
1748   DMPlexComputeClementInterpolant - This function computes the L2 projection of the cellwise values of a function u onto P1
1749 
1750   Collective
1751 
1752   Input Parameters:
1753 + dm   - The `DM`
1754 - locX - The coefficient vector u_h
1755 
1756   Output Parameter:
1757 . locC - A `Vec` which holds the Clement interpolant of the function
1758 
1759   Level: developer
1760 
1761   Note:
1762   $ 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
1763 
1764 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
1765 @*/
1766 PetscErrorCode DMPlexComputeClementInterpolant(DM dm, Vec locX, Vec locC)
1767 {
1768   PetscInt         debug = ((DM_Plex *)dm->data)->printFEM;
1769   DM               dmc;
1770   PetscQuadrature  quad;
1771   PetscScalar     *interpolant, *valsum;
1772   PetscFEGeom      fegeom;
1773   PetscReal       *coords;
1774   const PetscReal *quadPoints, *quadWeights;
1775   PetscInt         dim, cdim, Nf, f, Nc = 0, Nq, qNc, cStart, cEnd, vStart, vEnd, v;
1776 
1777   PetscFunctionBegin;
1778   PetscCall(PetscCitationsRegister(ClementCitation, &Clementcite));
1779   PetscCall(VecGetDM(locC, &dmc));
1780   PetscCall(VecSet(locC, 0.0));
1781   PetscCall(DMGetDimension(dm, &dim));
1782   PetscCall(DMGetCoordinateDim(dm, &cdim));
1783   fegeom.dimEmbed = cdim;
1784   PetscCall(DMGetNumFields(dm, &Nf));
1785   PetscCheck(Nf > 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fields is zero!");
1786   for (f = 0; f < Nf; ++f) {
1787     PetscObject  obj;
1788     PetscClassId id;
1789     PetscInt     fNc;
1790 
1791     PetscCall(DMGetField(dm, f, NULL, &obj));
1792     PetscCall(PetscObjectGetClassId(obj, &id));
1793     if (id == PETSCFE_CLASSID) {
1794       PetscFE fe = (PetscFE)obj;
1795 
1796       PetscCall(PetscFEGetQuadrature(fe, &quad));
1797       PetscCall(PetscFEGetNumComponents(fe, &fNc));
1798     } else if (id == PETSCFV_CLASSID) {
1799       PetscFV fv = (PetscFV)obj;
1800 
1801       PetscCall(PetscFVGetQuadrature(fv, &quad));
1802       PetscCall(PetscFVGetNumComponents(fv, &fNc));
1803     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
1804     Nc += fNc;
1805   }
1806   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
1807   PetscCheck(qNc == 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " > 1", qNc);
1808   PetscCall(PetscMalloc6(Nc * 2, &valsum, Nc, &interpolant, cdim * Nq, &coords, Nq, &fegeom.detJ, cdim * cdim * Nq, &fegeom.J, cdim * cdim * Nq, &fegeom.invJ));
1809   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1810   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
1811   for (v = vStart; v < vEnd; ++v) {
1812     PetscScalar volsum = 0.0;
1813     PetscInt   *star   = NULL;
1814     PetscInt    starSize, st, fc;
1815 
1816     PetscCall(PetscArrayzero(valsum, Nc));
1817     PetscCall(DMPlexGetTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
1818     for (st = 0; st < starSize * 2; st += 2) {
1819       const PetscInt cell = star[st];
1820       PetscScalar   *val  = &valsum[Nc];
1821       PetscScalar   *x    = NULL;
1822       PetscReal      vol  = 0.0;
1823       PetscInt       foff = 0;
1824 
1825       if ((cell < cStart) || (cell >= cEnd)) continue;
1826       PetscCall(DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1827       PetscCall(DMPlexVecGetClosure(dm, NULL, locX, cell, NULL, &x));
1828       for (f = 0; f < Nf; ++f) {
1829         PetscObject  obj;
1830         PetscClassId id;
1831         PetscInt     Nb, fNc, q;
1832 
1833         PetscCall(PetscArrayzero(val, Nc));
1834         PetscCall(DMGetField(dm, f, NULL, &obj));
1835         PetscCall(PetscObjectGetClassId(obj, &id));
1836         if (id == PETSCFE_CLASSID) {
1837           PetscCall(PetscFEGetNumComponents((PetscFE)obj, &fNc));
1838           PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
1839         } else if (id == PETSCFV_CLASSID) {
1840           PetscCall(PetscFVGetNumComponents((PetscFV)obj, &fNc));
1841           Nb = 1;
1842         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
1843         for (q = 0; q < Nq; ++q) {
1844           const PetscReal wt = quadWeights[q] * fegeom.detJ[q];
1845           PetscFEGeom     qgeom;
1846 
1847           qgeom.dimEmbed = fegeom.dimEmbed;
1848           qgeom.J        = &fegeom.J[q * cdim * cdim];
1849           qgeom.invJ     = &fegeom.invJ[q * cdim * cdim];
1850           qgeom.detJ     = &fegeom.detJ[q];
1851           PetscCheck(fegeom.detJ[q] > 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for element %" PetscInt_FMT ", quadrature points %" PetscInt_FMT, (double)fegeom.detJ[q], cell, q);
1852           if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[foff], &qgeom, q, interpolant));
1853           else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
1854           for (fc = 0; fc < fNc; ++fc) val[foff + fc] += interpolant[fc] * wt;
1855           vol += wt;
1856         }
1857         foff += Nb;
1858       }
1859       PetscCall(DMPlexVecRestoreClosure(dm, NULL, locX, cell, NULL, &x));
1860       for (fc = 0; fc < Nc; ++fc) valsum[fc] += val[fc];
1861       volsum += vol;
1862       if (debug) {
1863         PetscCall(PetscPrintf(PETSC_COMM_SELF, "Vertex %" PetscInt_FMT " Cell %" PetscInt_FMT " value: [", v, cell));
1864         for (fc = 0; fc < Nc; ++fc) {
1865           if (fc) PetscCall(PetscPrintf(PETSC_COMM_SELF, ", "));
1866           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%g", (double)PetscRealPart(val[fc])));
1867         }
1868         PetscCall(PetscPrintf(PETSC_COMM_SELF, "]\n"));
1869       }
1870     }
1871     for (fc = 0; fc < Nc; ++fc) valsum[fc] /= volsum;
1872     PetscCall(DMPlexRestoreTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
1873     PetscCall(DMPlexVecSetClosure(dmc, NULL, locC, v, valsum, INSERT_VALUES));
1874   }
1875   PetscCall(PetscFree6(valsum, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1876   PetscFunctionReturn(PETSC_SUCCESS);
1877 }
1878 
1879 /*@
1880   DMPlexComputeGradientClementInterpolant - This function computes the L2 projection of the cellwise gradient of a function u onto P1
1881 
1882   Collective
1883 
1884   Input Parameters:
1885 + dm   - The `DM`
1886 - locX - The coefficient vector u_h
1887 
1888   Output Parameter:
1889 . locC - A `Vec` which holds the Clement interpolant of the gradient
1890 
1891   Level: developer
1892 
1893   Note:
1894   $\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
1895 
1896 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
1897 @*/
1898 PetscErrorCode DMPlexComputeGradientClementInterpolant(DM dm, Vec locX, Vec locC)
1899 {
1900   DM_Plex         *mesh  = (DM_Plex *)dm->data;
1901   PetscInt         debug = mesh->printFEM;
1902   DM               dmC;
1903   PetscQuadrature  quad;
1904   PetscScalar     *interpolant, *gradsum;
1905   PetscFEGeom      fegeom;
1906   PetscReal       *coords;
1907   const PetscReal *quadPoints, *quadWeights;
1908   PetscInt         dim, coordDim, numFields, numComponents = 0, qNc, Nq, cStart, cEnd, vStart, vEnd, v, field, fieldOffset;
1909 
1910   PetscFunctionBegin;
1911   PetscCall(PetscCitationsRegister(ClementCitation, &Clementcite));
1912   PetscCall(VecGetDM(locC, &dmC));
1913   PetscCall(VecSet(locC, 0.0));
1914   PetscCall(DMGetDimension(dm, &dim));
1915   PetscCall(DMGetCoordinateDim(dm, &coordDim));
1916   fegeom.dimEmbed = coordDim;
1917   PetscCall(DMGetNumFields(dm, &numFields));
1918   PetscCheck(numFields, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fields is zero!");
1919   for (field = 0; field < numFields; ++field) {
1920     PetscObject  obj;
1921     PetscClassId id;
1922     PetscInt     Nc;
1923 
1924     PetscCall(DMGetField(dm, field, NULL, &obj));
1925     PetscCall(PetscObjectGetClassId(obj, &id));
1926     if (id == PETSCFE_CLASSID) {
1927       PetscFE fe = (PetscFE)obj;
1928 
1929       PetscCall(PetscFEGetQuadrature(fe, &quad));
1930       PetscCall(PetscFEGetNumComponents(fe, &Nc));
1931     } else if (id == PETSCFV_CLASSID) {
1932       PetscFV fv = (PetscFV)obj;
1933 
1934       PetscCall(PetscFVGetQuadrature(fv, &quad));
1935       PetscCall(PetscFVGetNumComponents(fv, &Nc));
1936     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1937     numComponents += Nc;
1938   }
1939   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
1940   PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
1941   PetscCall(PetscMalloc6(coordDim * numComponents * 2, &gradsum, coordDim * numComponents, &interpolant, coordDim * Nq, &coords, Nq, &fegeom.detJ, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ));
1942   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1943   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
1944   for (v = vStart; v < vEnd; ++v) {
1945     PetscScalar volsum = 0.0;
1946     PetscInt   *star   = NULL;
1947     PetscInt    starSize, st, d, fc;
1948 
1949     PetscCall(PetscArrayzero(gradsum, coordDim * numComponents));
1950     PetscCall(DMPlexGetTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
1951     for (st = 0; st < starSize * 2; st += 2) {
1952       const PetscInt cell = star[st];
1953       PetscScalar   *grad = &gradsum[coordDim * numComponents];
1954       PetscScalar   *x    = NULL;
1955       PetscReal      vol  = 0.0;
1956 
1957       if ((cell < cStart) || (cell >= cEnd)) continue;
1958       PetscCall(DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1959       PetscCall(DMPlexVecGetClosure(dm, NULL, locX, cell, NULL, &x));
1960       for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1961         PetscObject  obj;
1962         PetscClassId id;
1963         PetscInt     Nb, Nc, q, qc = 0;
1964 
1965         PetscCall(PetscArrayzero(grad, coordDim * numComponents));
1966         PetscCall(DMGetField(dm, field, NULL, &obj));
1967         PetscCall(PetscObjectGetClassId(obj, &id));
1968         if (id == PETSCFE_CLASSID) {
1969           PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
1970           PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
1971         } else if (id == PETSCFV_CLASSID) {
1972           PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
1973           Nb = 1;
1974         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1975         for (q = 0; q < Nq; ++q) {
1976           PetscFEGeom qgeom;
1977 
1978           qgeom.dimEmbed = fegeom.dimEmbed;
1979           qgeom.J        = &fegeom.J[q * coordDim * coordDim];
1980           qgeom.invJ     = &fegeom.invJ[q * coordDim * coordDim];
1981           qgeom.detJ     = &fegeom.detJ[q];
1982           PetscCheck(fegeom.detJ[q] > 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for element %" PetscInt_FMT ", quadrature points %" PetscInt_FMT, (double)fegeom.detJ[q], cell, q);
1983           if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolateGradient_Static((PetscFE)obj, 1, &x[fieldOffset], &qgeom, q, interpolant));
1984           else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1985           for (fc = 0; fc < Nc; ++fc) {
1986             const PetscReal wt = quadWeights[q * qNc + qc];
1987 
1988             for (d = 0; d < coordDim; ++d) grad[fc * coordDim + d] += interpolant[fc * dim + d] * wt * fegeom.detJ[q];
1989           }
1990           vol += quadWeights[q * qNc] * fegeom.detJ[q];
1991         }
1992         fieldOffset += Nb;
1993         qc += Nc;
1994       }
1995       PetscCall(DMPlexVecRestoreClosure(dm, NULL, locX, cell, NULL, &x));
1996       for (fc = 0; fc < numComponents; ++fc) {
1997         for (d = 0; d < coordDim; ++d) gradsum[fc * coordDim + d] += grad[fc * coordDim + d];
1998       }
1999       volsum += vol;
2000       if (debug) {
2001         PetscCall(PetscPrintf(PETSC_COMM_SELF, "Vertex %" PetscInt_FMT " Cell %" PetscInt_FMT " gradient: [", v, cell));
2002         for (fc = 0; fc < numComponents; ++fc) {
2003           for (d = 0; d < coordDim; ++d) {
2004             if (fc || d > 0) PetscCall(PetscPrintf(PETSC_COMM_SELF, ", "));
2005             PetscCall(PetscPrintf(PETSC_COMM_SELF, "%g", (double)PetscRealPart(grad[fc * coordDim + d])));
2006           }
2007         }
2008         PetscCall(PetscPrintf(PETSC_COMM_SELF, "]\n"));
2009       }
2010     }
2011     for (fc = 0; fc < numComponents; ++fc) {
2012       for (d = 0; d < coordDim; ++d) gradsum[fc * coordDim + d] /= volsum;
2013     }
2014     PetscCall(DMPlexRestoreTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
2015     PetscCall(DMPlexVecSetClosure(dmC, NULL, locC, v, gradsum, INSERT_VALUES));
2016   }
2017   PetscCall(PetscFree6(gradsum, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
2018   PetscFunctionReturn(PETSC_SUCCESS);
2019 }
2020 
2021 static PetscErrorCode DMPlexComputeIntegral_Internal(DM dm, Vec X, PetscInt cStart, PetscInt cEnd, PetscScalar *cintegral, void *user)
2022 {
2023   DM           dmAux = NULL;
2024   PetscDS      prob, probAux = NULL;
2025   PetscSection section, sectionAux;
2026   Vec          locX, locA;
2027   PetscInt     dim, numCells = cEnd - cStart, c, f;
2028   PetscBool    useFVM = PETSC_FALSE;
2029   /* DS */
2030   PetscInt           Nf, totDim, *uOff, *uOff_x, numConstants;
2031   PetscInt           NfAux, totDimAux, *aOff;
2032   PetscScalar       *u, *a;
2033   const PetscScalar *constants;
2034   /* Geometry */
2035   PetscFEGeom       *cgeomFEM;
2036   DM                 dmGrad;
2037   PetscQuadrature    affineQuad      = NULL;
2038   Vec                cellGeometryFVM = NULL, faceGeometryFVM = NULL, locGrad = NULL;
2039   PetscFVCellGeom   *cgeomFVM;
2040   const PetscScalar *lgrad;
2041   PetscInt           maxDegree;
2042   DMField            coordField;
2043   IS                 cellIS;
2044 
2045   PetscFunctionBegin;
2046   PetscCall(DMGetDS(dm, &prob));
2047   PetscCall(DMGetDimension(dm, &dim));
2048   PetscCall(DMGetLocalSection(dm, &section));
2049   PetscCall(DMGetNumFields(dm, &Nf));
2050   /* Determine which discretizations we have */
2051   for (f = 0; f < Nf; ++f) {
2052     PetscObject  obj;
2053     PetscClassId id;
2054 
2055     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
2056     PetscCall(PetscObjectGetClassId(obj, &id));
2057     if (id == PETSCFV_CLASSID) useFVM = PETSC_TRUE;
2058   }
2059   /* Get local solution with boundary values */
2060   PetscCall(DMGetLocalVector(dm, &locX));
2061   PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL));
2062   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX));
2063   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX));
2064   /* Read DS information */
2065   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
2066   PetscCall(PetscDSGetComponentOffsets(prob, &uOff));
2067   PetscCall(PetscDSGetComponentDerivativeOffsets(prob, &uOff_x));
2068   PetscCall(ISCreateStride(PETSC_COMM_SELF, numCells, cStart, 1, &cellIS));
2069   PetscCall(PetscDSGetConstants(prob, &numConstants, &constants));
2070   /* Read Auxiliary DS information */
2071   PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA));
2072   if (locA) {
2073     PetscCall(VecGetDM(locA, &dmAux));
2074     PetscCall(DMGetDS(dmAux, &probAux));
2075     PetscCall(PetscDSGetNumFields(probAux, &NfAux));
2076     PetscCall(DMGetLocalSection(dmAux, &sectionAux));
2077     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
2078     PetscCall(PetscDSGetComponentOffsets(probAux, &aOff));
2079   }
2080   /* Allocate data  arrays */
2081   PetscCall(PetscCalloc1(numCells * totDim, &u));
2082   if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
2083   /* Read out geometry */
2084   PetscCall(DMGetCoordinateField(dm, &coordField));
2085   PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
2086   if (maxDegree <= 1) {
2087     PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad));
2088     if (affineQuad) PetscCall(DMFieldCreateFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &cgeomFEM));
2089   }
2090   if (useFVM) {
2091     PetscFV   fv = NULL;
2092     Vec       grad;
2093     PetscInt  fStart, fEnd;
2094     PetscBool compGrad;
2095 
2096     for (f = 0; f < Nf; ++f) {
2097       PetscObject  obj;
2098       PetscClassId id;
2099 
2100       PetscCall(PetscDSGetDiscretization(prob, f, &obj));
2101       PetscCall(PetscObjectGetClassId(obj, &id));
2102       if (id == PETSCFV_CLASSID) {
2103         fv = (PetscFV)obj;
2104         break;
2105       }
2106     }
2107     PetscCall(PetscFVGetComputeGradients(fv, &compGrad));
2108     PetscCall(PetscFVSetComputeGradients(fv, PETSC_TRUE));
2109     PetscCall(DMPlexComputeGeometryFVM(dm, &cellGeometryFVM, &faceGeometryFVM));
2110     PetscCall(DMPlexComputeGradientFVM(dm, fv, faceGeometryFVM, cellGeometryFVM, &dmGrad));
2111     PetscCall(PetscFVSetComputeGradients(fv, compGrad));
2112     PetscCall(VecGetArrayRead(cellGeometryFVM, (const PetscScalar **)&cgeomFVM));
2113     /* Reconstruct and limit cell gradients */
2114     PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
2115     PetscCall(DMGetGlobalVector(dmGrad, &grad));
2116     PetscCall(DMPlexReconstructGradients_Internal(dm, fv, fStart, fEnd, faceGeometryFVM, cellGeometryFVM, locX, grad));
2117     /* Communicate gradient values */
2118     PetscCall(DMGetLocalVector(dmGrad, &locGrad));
2119     PetscCall(DMGlobalToLocalBegin(dmGrad, grad, INSERT_VALUES, locGrad));
2120     PetscCall(DMGlobalToLocalEnd(dmGrad, grad, INSERT_VALUES, locGrad));
2121     PetscCall(DMRestoreGlobalVector(dmGrad, &grad));
2122     /* Handle non-essential (e.g. outflow) boundary values */
2123     PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_FALSE, locX, 0.0, faceGeometryFVM, cellGeometryFVM, locGrad));
2124     PetscCall(VecGetArrayRead(locGrad, &lgrad));
2125   }
2126   /* Read out data from inputs */
2127   for (c = cStart; c < cEnd; ++c) {
2128     PetscScalar *x = NULL;
2129     PetscInt     i;
2130 
2131     PetscCall(DMPlexVecGetClosure(dm, section, locX, c, NULL, &x));
2132     for (i = 0; i < totDim; ++i) u[c * totDim + i] = x[i];
2133     PetscCall(DMPlexVecRestoreClosure(dm, section, locX, c, NULL, &x));
2134     if (dmAux) {
2135       PetscCall(DMPlexVecGetClosure(dmAux, sectionAux, locA, c, NULL, &x));
2136       for (i = 0; i < totDimAux; ++i) a[c * totDimAux + i] = x[i];
2137       PetscCall(DMPlexVecRestoreClosure(dmAux, sectionAux, locA, c, NULL, &x));
2138     }
2139   }
2140   /* Do integration for each field */
2141   for (f = 0; f < Nf; ++f) {
2142     PetscObject  obj;
2143     PetscClassId id;
2144     PetscInt     numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;
2145 
2146     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
2147     PetscCall(PetscObjectGetClassId(obj, &id));
2148     if (id == PETSCFE_CLASSID) {
2149       PetscFE         fe = (PetscFE)obj;
2150       PetscQuadrature q;
2151       PetscFEGeom    *chunkGeom = NULL;
2152       PetscInt        Nq, Nb;
2153 
2154       PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
2155       PetscCall(PetscFEGetQuadrature(fe, &q));
2156       PetscCall(PetscQuadratureGetData(q, NULL, NULL, &Nq, NULL, NULL));
2157       PetscCall(PetscFEGetDimension(fe, &Nb));
2158       blockSize = Nb * Nq;
2159       batchSize = numBlocks * blockSize;
2160       PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
2161       numChunks = numCells / (numBatches * batchSize);
2162       Ne        = numChunks * numBatches * batchSize;
2163       Nr        = numCells % (numBatches * batchSize);
2164       offset    = numCells - Nr;
2165       if (!affineQuad) PetscCall(DMFieldCreateFEGeom(coordField, cellIS, q, PETSC_FALSE, &cgeomFEM));
2166       PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
2167       PetscCall(PetscFEIntegrate(prob, f, Ne, chunkGeom, u, probAux, a, cintegral));
2168       PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &chunkGeom));
2169       PetscCall(PetscFEIntegrate(prob, f, Nr, chunkGeom, &u[offset * totDim], probAux, &a[offset * totDimAux], &cintegral[offset * Nf]));
2170       PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &chunkGeom));
2171       if (!affineQuad) PetscCall(PetscFEGeomDestroy(&cgeomFEM));
2172     } else if (id == PETSCFV_CLASSID) {
2173       PetscInt       foff;
2174       PetscPointFunc obj_func;
2175       PetscScalar    lint;
2176 
2177       PetscCall(PetscDSGetObjective(prob, f, &obj_func));
2178       PetscCall(PetscDSGetFieldOffset(prob, f, &foff));
2179       if (obj_func) {
2180         for (c = 0; c < numCells; ++c) {
2181           PetscScalar *u_x;
2182 
2183           PetscCall(DMPlexPointLocalRead(dmGrad, c, lgrad, &u_x));
2184           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);
2185           cintegral[c * Nf + f] += PetscRealPart(lint) * cgeomFVM[c].volume;
2186         }
2187       }
2188     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
2189   }
2190   /* Cleanup data arrays */
2191   if (useFVM) {
2192     PetscCall(VecRestoreArrayRead(locGrad, &lgrad));
2193     PetscCall(VecRestoreArrayRead(cellGeometryFVM, (const PetscScalar **)&cgeomFVM));
2194     PetscCall(DMRestoreLocalVector(dmGrad, &locGrad));
2195     PetscCall(VecDestroy(&faceGeometryFVM));
2196     PetscCall(VecDestroy(&cellGeometryFVM));
2197     PetscCall(DMDestroy(&dmGrad));
2198   }
2199   if (dmAux) PetscCall(PetscFree(a));
2200   PetscCall(PetscFree(u));
2201   /* Cleanup */
2202   if (affineQuad) PetscCall(PetscFEGeomDestroy(&cgeomFEM));
2203   PetscCall(PetscQuadratureDestroy(&affineQuad));
2204   PetscCall(ISDestroy(&cellIS));
2205   PetscCall(DMRestoreLocalVector(dm, &locX));
2206   PetscFunctionReturn(PETSC_SUCCESS);
2207 }
2208 
2209 /*@
2210   DMPlexComputeIntegralFEM - Form the integral over the domain from the global input X using pointwise functions specified by the user
2211 
2212   Input Parameters:
2213 + dm   - The mesh
2214 . X    - Global input vector
2215 - user - The user context
2216 
2217   Output Parameter:
2218 . integral - Integral for each field
2219 
2220   Level: developer
2221 
2222 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSNESComputeResidualFEM()`
2223 @*/
2224 PetscErrorCode DMPlexComputeIntegralFEM(DM dm, Vec X, PetscScalar *integral, void *user)
2225 {
2226   DM_Plex     *mesh = (DM_Plex *)dm->data;
2227   PetscScalar *cintegral, *lintegral;
2228   PetscInt     Nf, f, cellHeight, cStart, cEnd, cell;
2229 
2230   PetscFunctionBegin;
2231   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2232   PetscValidHeaderSpecific(X, VEC_CLASSID, 2);
2233   PetscAssertPointer(integral, 3);
2234   PetscCall(PetscLogEventBegin(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2235   PetscCall(DMGetNumFields(dm, &Nf));
2236   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
2237   PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
2238   /* TODO Introduce a loop over large chunks (right now this is a single chunk) */
2239   PetscCall(PetscCalloc2(Nf, &lintegral, (cEnd - cStart) * Nf, &cintegral));
2240   PetscCall(DMPlexComputeIntegral_Internal(dm, X, cStart, cEnd, cintegral, user));
2241   /* Sum up values */
2242   for (cell = cStart; cell < cEnd; ++cell) {
2243     const PetscInt c = cell - cStart;
2244 
2245     if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, "Cell Integral", Nf, &cintegral[c * Nf]));
2246     for (f = 0; f < Nf; ++f) lintegral[f] += cintegral[c * Nf + f];
2247   }
2248   PetscCall(MPIU_Allreduce(lintegral, integral, Nf, MPIU_SCALAR, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
2249   if (mesh->printFEM) {
2250     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "Integral:"));
2251     for (f = 0; f < Nf; ++f) PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), " %g", (double)PetscRealPart(integral[f])));
2252     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "\n"));
2253   }
2254   PetscCall(PetscFree2(lintegral, cintegral));
2255   PetscCall(PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2256   PetscFunctionReturn(PETSC_SUCCESS);
2257 }
2258 
2259 /*@
2260   DMPlexComputeCellwiseIntegralFEM - Form the vector of cellwise integrals F from the global input X using pointwise functions specified by the user
2261 
2262   Input Parameters:
2263 + dm   - The mesh
2264 . X    - Global input vector
2265 - user - The user context
2266 
2267   Output Parameter:
2268 . F - Cellwise integrals for each field
2269 
2270   Level: developer
2271 
2272 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSNESComputeResidualFEM()`
2273 @*/
2274 PetscErrorCode DMPlexComputeCellwiseIntegralFEM(DM dm, Vec X, Vec F, void *user)
2275 {
2276   DM_Plex     *mesh = (DM_Plex *)dm->data;
2277   DM           dmF;
2278   PetscSection sectionF;
2279   PetscScalar *cintegral, *af;
2280   PetscInt     Nf, f, cellHeight, cStart, cEnd, cell;
2281 
2282   PetscFunctionBegin;
2283   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2284   PetscValidHeaderSpecific(X, VEC_CLASSID, 2);
2285   PetscValidHeaderSpecific(F, VEC_CLASSID, 3);
2286   PetscCall(PetscLogEventBegin(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2287   PetscCall(DMGetNumFields(dm, &Nf));
2288   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
2289   PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
2290   /* TODO Introduce a loop over large chunks (right now this is a single chunk) */
2291   PetscCall(PetscCalloc1((cEnd - cStart) * Nf, &cintegral));
2292   PetscCall(DMPlexComputeIntegral_Internal(dm, X, cStart, cEnd, cintegral, user));
2293   /* Put values in F*/
2294   PetscCall(VecGetDM(F, &dmF));
2295   PetscCall(DMGetLocalSection(dmF, &sectionF));
2296   PetscCall(VecGetArray(F, &af));
2297   for (cell = cStart; cell < cEnd; ++cell) {
2298     const PetscInt c = cell - cStart;
2299     PetscInt       dof, off;
2300 
2301     if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, "Cell Integral", Nf, &cintegral[c * Nf]));
2302     PetscCall(PetscSectionGetDof(sectionF, cell, &dof));
2303     PetscCall(PetscSectionGetOffset(sectionF, cell, &off));
2304     PetscCheck(dof == Nf, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "The number of cell dofs %" PetscInt_FMT " != %" PetscInt_FMT, dof, Nf);
2305     for (f = 0; f < Nf; ++f) af[off + f] = cintegral[c * Nf + f];
2306   }
2307   PetscCall(VecRestoreArray(F, &af));
2308   PetscCall(PetscFree(cintegral));
2309   PetscCall(PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2310   PetscFunctionReturn(PETSC_SUCCESS);
2311 }
2312 
2313 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)
2314 {
2315   DM                 plex = NULL, plexA = NULL;
2316   DMEnclosureType    encAux;
2317   PetscDS            prob, probAux       = NULL;
2318   PetscSection       section, sectionAux = NULL;
2319   Vec                locA = NULL;
2320   DMField            coordField;
2321   PetscInt           Nf, totDim, *uOff, *uOff_x;
2322   PetscInt           NfAux = 0, totDimAux = 0, *aOff = NULL;
2323   PetscScalar       *u, *a = NULL;
2324   const PetscScalar *constants;
2325   PetscInt           numConstants, f;
2326 
2327   PetscFunctionBegin;
2328   PetscCall(DMGetCoordinateField(dm, &coordField));
2329   PetscCall(DMConvert(dm, DMPLEX, &plex));
2330   PetscCall(DMGetDS(dm, &prob));
2331   PetscCall(DMGetLocalSection(dm, &section));
2332   PetscCall(PetscSectionGetNumFields(section, &Nf));
2333   /* Determine which discretizations we have */
2334   for (f = 0; f < Nf; ++f) {
2335     PetscObject  obj;
2336     PetscClassId id;
2337 
2338     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
2339     PetscCall(PetscObjectGetClassId(obj, &id));
2340     PetscCheck(id != PETSCFV_CLASSID, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Not supported for FVM (field %" PetscInt_FMT ")", f);
2341   }
2342   /* Read DS information */
2343   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
2344   PetscCall(PetscDSGetComponentOffsets(prob, &uOff));
2345   PetscCall(PetscDSGetComponentDerivativeOffsets(prob, &uOff_x));
2346   PetscCall(PetscDSGetConstants(prob, &numConstants, &constants));
2347   /* Read Auxiliary DS information */
2348   PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA));
2349   if (locA) {
2350     DM dmAux;
2351 
2352     PetscCall(VecGetDM(locA, &dmAux));
2353     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
2354     PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
2355     PetscCall(DMGetDS(dmAux, &probAux));
2356     PetscCall(PetscDSGetNumFields(probAux, &NfAux));
2357     PetscCall(DMGetLocalSection(dmAux, &sectionAux));
2358     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
2359     PetscCall(PetscDSGetComponentOffsets(probAux, &aOff));
2360   }
2361   /* Integrate over points */
2362   {
2363     PetscFEGeom    *fgeom, *chunkGeom = NULL;
2364     PetscInt        maxDegree;
2365     PetscQuadrature qGeom = NULL;
2366     const PetscInt *points;
2367     PetscInt        numFaces, face, Nq, field;
2368     PetscInt        numChunks, chunkSize, chunk, Nr, offset;
2369 
2370     PetscCall(ISGetLocalSize(pointIS, &numFaces));
2371     PetscCall(ISGetIndices(pointIS, &points));
2372     PetscCall(PetscCalloc2(numFaces * totDim, &u, locA ? numFaces * totDimAux : 0, &a));
2373     PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
2374     for (field = 0; field < Nf; ++field) {
2375       PetscFE fe;
2376 
2377       PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fe));
2378       if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom));
2379       if (!qGeom) {
2380         PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
2381         PetscCall(PetscObjectReference((PetscObject)qGeom));
2382       }
2383       PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
2384       PetscCall(DMPlexGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
2385       for (face = 0; face < numFaces; ++face) {
2386         const PetscInt point = points[face], *support;
2387         PetscScalar   *x     = NULL;
2388         PetscInt       i;
2389 
2390         PetscCall(DMPlexGetSupport(dm, point, &support));
2391         PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
2392         for (i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
2393         PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
2394         if (locA) {
2395           PetscInt subp;
2396           PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
2397           PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
2398           for (i = 0; i < totDimAux; ++i) a[f * totDimAux + i] = x[i];
2399           PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
2400         }
2401       }
2402       /* Get blocking */
2403       {
2404         PetscQuadrature q;
2405         PetscInt        numBatches, batchSize, numBlocks, blockSize;
2406         PetscInt        Nq, Nb;
2407 
2408         PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
2409         PetscCall(PetscFEGetQuadrature(fe, &q));
2410         PetscCall(PetscQuadratureGetData(q, NULL, NULL, &Nq, NULL, NULL));
2411         PetscCall(PetscFEGetDimension(fe, &Nb));
2412         blockSize = Nb * Nq;
2413         batchSize = numBlocks * blockSize;
2414         chunkSize = numBatches * batchSize;
2415         PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
2416         numChunks = numFaces / chunkSize;
2417         Nr        = numFaces % chunkSize;
2418         offset    = numFaces - Nr;
2419       }
2420       /* Do integration for each field */
2421       for (chunk = 0; chunk < numChunks; ++chunk) {
2422         PetscCall(PetscFEGeomGetChunk(fgeom, chunk * chunkSize, (chunk + 1) * chunkSize, &chunkGeom));
2423         PetscCall(PetscFEIntegrateBd(prob, field, func, chunkSize, chunkGeom, u, probAux, a, fintegral));
2424         PetscCall(PetscFEGeomRestoreChunk(fgeom, 0, offset, &chunkGeom));
2425       }
2426       PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom));
2427       PetscCall(PetscFEIntegrateBd(prob, field, func, Nr, chunkGeom, &u[offset * totDim], probAux, a ? &a[offset * totDimAux] : NULL, &fintegral[offset * Nf]));
2428       PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom));
2429       /* Cleanup data arrays */
2430       PetscCall(DMPlexRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
2431       PetscCall(PetscQuadratureDestroy(&qGeom));
2432       PetscCall(PetscFree2(u, a));
2433       PetscCall(ISRestoreIndices(pointIS, &points));
2434     }
2435   }
2436   if (plex) PetscCall(DMDestroy(&plex));
2437   if (plexA) PetscCall(DMDestroy(&plexA));
2438   PetscFunctionReturn(PETSC_SUCCESS);
2439 }
2440 
2441 /*@C
2442   DMPlexComputeBdIntegral - Form the integral over the specified boundary from the global input X using pointwise functions specified by the user
2443 
2444   Input Parameters:
2445 + dm      - The mesh
2446 . X       - Global input vector
2447 . label   - The boundary `DMLabel`
2448 . numVals - The number of label values to use, or `PETSC_DETERMINE` for all values
2449 . vals    - The label values to use, or NULL for all values
2450 . func    - The function to integrate along the boundary
2451 - user    - The user context
2452 
2453   Output Parameter:
2454 . integral - Integral for each field
2455 
2456   Level: developer
2457 
2458 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeIntegralFEM()`, `DMPlexComputeBdResidualFEM()`
2459 @*/
2460 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)
2461 {
2462   Vec          locX;
2463   PetscSection section;
2464   DMLabel      depthLabel;
2465   IS           facetIS;
2466   PetscInt     dim, Nf, f, v;
2467 
2468   PetscFunctionBegin;
2469   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2470   PetscValidHeaderSpecific(X, VEC_CLASSID, 2);
2471   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 3);
2472   if (vals) PetscAssertPointer(vals, 5);
2473   PetscAssertPointer(integral, 7);
2474   PetscCall(PetscLogEventBegin(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2475   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
2476   PetscCall(DMGetDimension(dm, &dim));
2477   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
2478   PetscCall(DMGetLocalSection(dm, &section));
2479   PetscCall(PetscSectionGetNumFields(section, &Nf));
2480   /* Get local solution with boundary values */
2481   PetscCall(DMGetLocalVector(dm, &locX));
2482   PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL));
2483   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX));
2484   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX));
2485   /* Loop over label values */
2486   PetscCall(PetscArrayzero(integral, Nf));
2487   for (v = 0; v < numVals; ++v) {
2488     IS           pointIS;
2489     PetscInt     numFaces, face;
2490     PetscScalar *fintegral;
2491 
2492     PetscCall(DMLabelGetStratumIS(label, vals[v], &pointIS));
2493     if (!pointIS) continue; /* No points with that id on this process */
2494     {
2495       IS isectIS;
2496 
2497       /* TODO: Special cases of ISIntersect where it is quick to check a priori if one is a superset of the other */
2498       PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS));
2499       PetscCall(ISDestroy(&pointIS));
2500       pointIS = isectIS;
2501     }
2502     PetscCall(ISGetLocalSize(pointIS, &numFaces));
2503     PetscCall(PetscCalloc1(numFaces * Nf, &fintegral));
2504     PetscCall(DMPlexComputeBdIntegral_Internal(dm, locX, pointIS, func, fintegral, user));
2505     /* Sum point contributions into integral */
2506     for (f = 0; f < Nf; ++f)
2507       for (face = 0; face < numFaces; ++face) integral[f] += fintegral[face * Nf + f];
2508     PetscCall(PetscFree(fintegral));
2509     PetscCall(ISDestroy(&pointIS));
2510   }
2511   PetscCall(DMRestoreLocalVector(dm, &locX));
2512   PetscCall(ISDestroy(&facetIS));
2513   PetscCall(PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2514   PetscFunctionReturn(PETSC_SUCCESS);
2515 }
2516 
2517 /*@
2518   DMPlexComputeInterpolatorNested - Form the local portion of the interpolation matrix I from the coarse `DM` to a uniformly refined `DM`.
2519 
2520   Input Parameters:
2521 + dmc       - The coarse mesh
2522 . dmf       - The fine mesh
2523 . isRefined - Flag indicating regular refinement, rather than the same topology
2524 - user      - The user context
2525 
2526   Output Parameter:
2527 . In - The interpolation matrix
2528 
2529   Level: developer
2530 
2531 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorGeneral()`, `DMPlexComputeJacobianFEM()`
2532 @*/
2533 PetscErrorCode DMPlexComputeInterpolatorNested(DM dmc, DM dmf, PetscBool isRefined, Mat In, void *user)
2534 {
2535   DM_Plex     *mesh = (DM_Plex *)dmc->data;
2536   const char  *name = "Interpolator";
2537   PetscFE     *feRef;
2538   PetscFV     *fvRef;
2539   PetscSection fsection, fglobalSection;
2540   PetscSection csection, cglobalSection;
2541   PetscScalar *elemMat;
2542   PetscInt     dim, Nf, f, fieldI, fieldJ, offsetI, offsetJ, cStart, cEnd, c;
2543   PetscInt     cTotDim = 0, rTotDim = 0;
2544 
2545   PetscFunctionBegin;
2546   PetscCall(PetscLogEventBegin(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
2547   PetscCall(DMGetDimension(dmf, &dim));
2548   PetscCall(DMGetLocalSection(dmf, &fsection));
2549   PetscCall(DMGetGlobalSection(dmf, &fglobalSection));
2550   PetscCall(DMGetLocalSection(dmc, &csection));
2551   PetscCall(DMGetGlobalSection(dmc, &cglobalSection));
2552   PetscCall(PetscSectionGetNumFields(fsection, &Nf));
2553   PetscCall(DMPlexGetSimplexOrBoxCells(dmc, 0, &cStart, &cEnd));
2554   PetscCall(PetscCalloc2(Nf, &feRef, Nf, &fvRef));
2555   for (f = 0; f < Nf; ++f) {
2556     PetscObject  obj, objc;
2557     PetscClassId id, idc;
2558     PetscInt     rNb = 0, Nc = 0, cNb = 0;
2559 
2560     PetscCall(DMGetField(dmf, f, NULL, &obj));
2561     PetscCall(PetscObjectGetClassId(obj, &id));
2562     if (id == PETSCFE_CLASSID) {
2563       PetscFE fe = (PetscFE)obj;
2564 
2565       if (isRefined) {
2566         PetscCall(PetscFERefine(fe, &feRef[f]));
2567       } else {
2568         PetscCall(PetscObjectReference((PetscObject)fe));
2569         feRef[f] = fe;
2570       }
2571       PetscCall(PetscFEGetDimension(feRef[f], &rNb));
2572       PetscCall(PetscFEGetNumComponents(fe, &Nc));
2573     } else if (id == PETSCFV_CLASSID) {
2574       PetscFV        fv = (PetscFV)obj;
2575       PetscDualSpace Q;
2576 
2577       if (isRefined) {
2578         PetscCall(PetscFVRefine(fv, &fvRef[f]));
2579       } else {
2580         PetscCall(PetscObjectReference((PetscObject)fv));
2581         fvRef[f] = fv;
2582       }
2583       PetscCall(PetscFVGetDualSpace(fvRef[f], &Q));
2584       PetscCall(PetscDualSpaceGetDimension(Q, &rNb));
2585       PetscCall(PetscFVGetDualSpace(fv, &Q));
2586       PetscCall(PetscFVGetNumComponents(fv, &Nc));
2587     }
2588     PetscCall(DMGetField(dmc, f, NULL, &objc));
2589     PetscCall(PetscObjectGetClassId(objc, &idc));
2590     if (idc == PETSCFE_CLASSID) {
2591       PetscFE fe = (PetscFE)objc;
2592 
2593       PetscCall(PetscFEGetDimension(fe, &cNb));
2594     } else if (id == PETSCFV_CLASSID) {
2595       PetscFV        fv = (PetscFV)obj;
2596       PetscDualSpace Q;
2597 
2598       PetscCall(PetscFVGetDualSpace(fv, &Q));
2599       PetscCall(PetscDualSpaceGetDimension(Q, &cNb));
2600     }
2601     rTotDim += rNb;
2602     cTotDim += cNb;
2603   }
2604   PetscCall(PetscMalloc1(rTotDim * cTotDim, &elemMat));
2605   PetscCall(PetscArrayzero(elemMat, rTotDim * cTotDim));
2606   for (fieldI = 0, offsetI = 0; fieldI < Nf; ++fieldI) {
2607     PetscDualSpace   Qref;
2608     PetscQuadrature  f;
2609     const PetscReal *qpoints, *qweights;
2610     PetscReal       *points;
2611     PetscInt         npoints = 0, Nc, Np, fpdim, i, k, p, d;
2612 
2613     /* Compose points from all dual basis functionals */
2614     if (feRef[fieldI]) {
2615       PetscCall(PetscFEGetDualSpace(feRef[fieldI], &Qref));
2616       PetscCall(PetscFEGetNumComponents(feRef[fieldI], &Nc));
2617     } else {
2618       PetscCall(PetscFVGetDualSpace(fvRef[fieldI], &Qref));
2619       PetscCall(PetscFVGetNumComponents(fvRef[fieldI], &Nc));
2620     }
2621     PetscCall(PetscDualSpaceGetDimension(Qref, &fpdim));
2622     for (i = 0; i < fpdim; ++i) {
2623       PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
2624       PetscCall(PetscQuadratureGetData(f, NULL, NULL, &Np, NULL, NULL));
2625       npoints += Np;
2626     }
2627     PetscCall(PetscMalloc1(npoints * dim, &points));
2628     for (i = 0, k = 0; i < fpdim; ++i) {
2629       PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
2630       PetscCall(PetscQuadratureGetData(f, NULL, NULL, &Np, &qpoints, NULL));
2631       for (p = 0; p < Np; ++p, ++k)
2632         for (d = 0; d < dim; ++d) points[k * dim + d] = qpoints[p * dim + d];
2633     }
2634 
2635     for (fieldJ = 0, offsetJ = 0; fieldJ < Nf; ++fieldJ) {
2636       PetscObject  obj;
2637       PetscClassId id;
2638       PetscInt     NcJ = 0, cpdim = 0, j, qNc;
2639 
2640       PetscCall(DMGetField(dmc, fieldJ, NULL, &obj));
2641       PetscCall(PetscObjectGetClassId(obj, &id));
2642       if (id == PETSCFE_CLASSID) {
2643         PetscFE         fe = (PetscFE)obj;
2644         PetscTabulation T  = NULL;
2645 
2646         /* Evaluate basis at points */
2647         PetscCall(PetscFEGetNumComponents(fe, &NcJ));
2648         PetscCall(PetscFEGetDimension(fe, &cpdim));
2649         /* For now, fields only interpolate themselves */
2650         if (fieldI == fieldJ) {
2651           PetscCheck(Nc == NcJ, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of components in fine space field %" PetscInt_FMT " does not match coarse field %" PetscInt_FMT, Nc, NcJ);
2652           PetscCall(PetscFECreateTabulation(fe, 1, npoints, points, 0, &T));
2653           for (i = 0, k = 0; i < fpdim; ++i) {
2654             PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
2655             PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, NULL, &qweights));
2656             PetscCheck(qNc == NcJ, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of components in quadrature %" PetscInt_FMT " does not match coarse field %" PetscInt_FMT, qNc, NcJ);
2657             for (p = 0; p < Np; ++p, ++k) {
2658               for (j = 0; j < cpdim; ++j) {
2659                 /*
2660                    cTotDim:            Total columns in element interpolation matrix, sum of number of dual basis functionals in each field
2661                    offsetI, offsetJ:   Offsets into the larger element interpolation matrix for different fields
2662                    fpdim, i, cpdim, j: Dofs for fine and coarse grids, correspond to dual space basis functionals
2663                    qNC, Nc, Ncj, c:    Number of components in this field
2664                    Np, p:              Number of quad points in the fine grid functional i
2665                    k:                  i*Np + p, overall point number for the interpolation
2666                 */
2667                 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];
2668               }
2669             }
2670           }
2671           PetscCall(PetscTabulationDestroy(&T));
2672         }
2673       } else if (id == PETSCFV_CLASSID) {
2674         PetscFV fv = (PetscFV)obj;
2675 
2676         /* Evaluate constant function at points */
2677         PetscCall(PetscFVGetNumComponents(fv, &NcJ));
2678         cpdim = 1;
2679         /* For now, fields only interpolate themselves */
2680         if (fieldI == fieldJ) {
2681           PetscCheck(Nc == NcJ, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of components in fine space field %" PetscInt_FMT " does not match coarse field %" PetscInt_FMT, Nc, NcJ);
2682           for (i = 0, k = 0; i < fpdim; ++i) {
2683             PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
2684             PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, NULL, &qweights));
2685             PetscCheck(qNc == NcJ, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of components in quadrature %" PetscInt_FMT " does not match coarse field %" PetscInt_FMT, qNc, NcJ);
2686             for (p = 0; p < Np; ++p, ++k) {
2687               for (j = 0; j < cpdim; ++j) {
2688                 for (c = 0; c < Nc; ++c) elemMat[(offsetI + i) * cTotDim + offsetJ + j] += 1.0 * qweights[p * qNc + c];
2689               }
2690             }
2691           }
2692         }
2693       }
2694       offsetJ += cpdim;
2695     }
2696     offsetI += fpdim;
2697     PetscCall(PetscFree(points));
2698   }
2699   if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(0, name, rTotDim, cTotDim, elemMat));
2700   /* Preallocate matrix */
2701   {
2702     Mat          preallocator;
2703     PetscScalar *vals;
2704     PetscInt    *cellCIndices, *cellFIndices;
2705     PetscInt     locRows, locCols, cell;
2706 
2707     PetscCall(MatGetLocalSize(In, &locRows, &locCols));
2708     PetscCall(MatCreate(PetscObjectComm((PetscObject)In), &preallocator));
2709     PetscCall(MatSetType(preallocator, MATPREALLOCATOR));
2710     PetscCall(MatSetSizes(preallocator, locRows, locCols, PETSC_DETERMINE, PETSC_DETERMINE));
2711     PetscCall(MatSetUp(preallocator));
2712     PetscCall(PetscCalloc3(rTotDim * cTotDim, &vals, cTotDim, &cellCIndices, rTotDim, &cellFIndices));
2713     for (cell = cStart; cell < cEnd; ++cell) {
2714       if (isRefined) {
2715         PetscCall(DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, cell, cellCIndices, cellFIndices));
2716         PetscCall(MatSetValues(preallocator, rTotDim, cellFIndices, cTotDim, cellCIndices, vals, INSERT_VALUES));
2717       } else {
2718         PetscCall(DMPlexMatSetClosureGeneral(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, preallocator, cell, vals, INSERT_VALUES));
2719       }
2720     }
2721     PetscCall(PetscFree3(vals, cellCIndices, cellFIndices));
2722     PetscCall(MatAssemblyBegin(preallocator, MAT_FINAL_ASSEMBLY));
2723     PetscCall(MatAssemblyEnd(preallocator, MAT_FINAL_ASSEMBLY));
2724     PetscCall(MatPreallocatorPreallocate(preallocator, PETSC_TRUE, In));
2725     PetscCall(MatDestroy(&preallocator));
2726   }
2727   /* Fill matrix */
2728   PetscCall(MatZeroEntries(In));
2729   for (c = cStart; c < cEnd; ++c) {
2730     if (isRefined) {
2731       PetscCall(DMPlexMatSetClosureRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, In, c, elemMat, INSERT_VALUES));
2732     } else {
2733       PetscCall(DMPlexMatSetClosureGeneral(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, In, c, elemMat, INSERT_VALUES));
2734     }
2735   }
2736   for (f = 0; f < Nf; ++f) PetscCall(PetscFEDestroy(&feRef[f]));
2737   PetscCall(PetscFree2(feRef, fvRef));
2738   PetscCall(PetscFree(elemMat));
2739   PetscCall(MatAssemblyBegin(In, MAT_FINAL_ASSEMBLY));
2740   PetscCall(MatAssemblyEnd(In, MAT_FINAL_ASSEMBLY));
2741   if (mesh->printFEM > 1) {
2742     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)In), "%s:\n", name));
2743     PetscCall(MatFilter(In, 1.0e-10, PETSC_FALSE, PETSC_FALSE));
2744     PetscCall(MatView(In, NULL));
2745   }
2746   PetscCall(PetscLogEventEnd(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
2747   PetscFunctionReturn(PETSC_SUCCESS);
2748 }
2749 
2750 PetscErrorCode DMPlexComputeMassMatrixNested(DM dmc, DM dmf, Mat mass, void *user)
2751 {
2752   SETERRQ(PetscObjectComm((PetscObject)dmc), PETSC_ERR_SUP, "Laziness");
2753 }
2754 
2755 /*@
2756   DMPlexComputeInterpolatorGeneral - Form the local portion of the interpolation matrix I from the coarse `DM` to a non-nested fine `DM`.
2757 
2758   Input Parameters:
2759 + dmf  - The fine mesh
2760 . dmc  - The coarse mesh
2761 - user - The user context
2762 
2763   Output Parameter:
2764 . In - The interpolation matrix
2765 
2766   Level: developer
2767 
2768 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorNested()`, `DMPlexComputeJacobianFEM()`
2769 @*/
2770 PetscErrorCode DMPlexComputeInterpolatorGeneral(DM dmc, DM dmf, Mat In, void *user)
2771 {
2772   DM_Plex     *mesh = (DM_Plex *)dmf->data;
2773   const char  *name = "Interpolator";
2774   PetscDS      prob;
2775   Mat          interp;
2776   PetscSection fsection, globalFSection;
2777   PetscSection csection, globalCSection;
2778   PetscInt     locRows, locCols;
2779   PetscReal   *x, *v0, *J, *invJ, detJ;
2780   PetscReal   *v0c, *Jc, *invJc, detJc;
2781   PetscScalar *elemMat;
2782   PetscInt     dim, Nf, field, totDim, cStart, cEnd, cell, ccell, s;
2783 
2784   PetscFunctionBegin;
2785   PetscCall(PetscLogEventBegin(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
2786   PetscCall(DMGetCoordinateDim(dmc, &dim));
2787   PetscCall(DMGetDS(dmc, &prob));
2788   PetscCall(PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL));
2789   PetscCall(PetscDSGetNumFields(prob, &Nf));
2790   PetscCall(PetscMalloc3(dim, &v0, dim * dim, &J, dim * dim, &invJ));
2791   PetscCall(PetscMalloc3(dim, &v0c, dim * dim, &Jc, dim * dim, &invJc));
2792   PetscCall(DMGetLocalSection(dmf, &fsection));
2793   PetscCall(DMGetGlobalSection(dmf, &globalFSection));
2794   PetscCall(DMGetLocalSection(dmc, &csection));
2795   PetscCall(DMGetGlobalSection(dmc, &globalCSection));
2796   PetscCall(DMPlexGetSimplexOrBoxCells(dmf, 0, &cStart, &cEnd));
2797   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
2798   PetscCall(PetscMalloc1(totDim, &elemMat));
2799 
2800   PetscCall(MatGetLocalSize(In, &locRows, &locCols));
2801   PetscCall(MatCreate(PetscObjectComm((PetscObject)In), &interp));
2802   PetscCall(MatSetType(interp, MATPREALLOCATOR));
2803   PetscCall(MatSetSizes(interp, locRows, locCols, PETSC_DETERMINE, PETSC_DETERMINE));
2804   PetscCall(MatSetUp(interp));
2805   for (s = 0; s < 2; ++s) {
2806     for (field = 0; field < Nf; ++field) {
2807       PetscObject      obj;
2808       PetscClassId     id;
2809       PetscDualSpace   Q = NULL;
2810       PetscTabulation  T = NULL;
2811       PetscQuadrature  f;
2812       const PetscReal *qpoints, *qweights;
2813       PetscInt         Nc, qNc, Np, fpdim, off, i, d;
2814 
2815       PetscCall(PetscDSGetFieldOffset(prob, field, &off));
2816       PetscCall(PetscDSGetDiscretization(prob, field, &obj));
2817       PetscCall(PetscObjectGetClassId(obj, &id));
2818       if (id == PETSCFE_CLASSID) {
2819         PetscFE fe = (PetscFE)obj;
2820 
2821         PetscCall(PetscFEGetDualSpace(fe, &Q));
2822         PetscCall(PetscFEGetNumComponents(fe, &Nc));
2823         if (s) PetscCall(PetscFECreateTabulation(fe, 1, 1, x, 0, &T));
2824       } else if (id == PETSCFV_CLASSID) {
2825         PetscFV fv = (PetscFV)obj;
2826 
2827         PetscCall(PetscFVGetDualSpace(fv, &Q));
2828         Nc = 1;
2829       } else SETERRQ(PetscObjectComm((PetscObject)dmc), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
2830       PetscCall(PetscDualSpaceGetDimension(Q, &fpdim));
2831       /* For each fine grid cell */
2832       for (cell = cStart; cell < cEnd; ++cell) {
2833         PetscInt *findices, *cindices;
2834         PetscInt  numFIndices, numCIndices;
2835 
2836         PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
2837         PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ));
2838         PetscCheck(numFIndices == totDim, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fine indices %" PetscInt_FMT " != %" PetscInt_FMT " dual basis vecs", numFIndices, totDim);
2839         for (i = 0; i < fpdim; ++i) {
2840           Vec                pointVec;
2841           PetscScalar       *pV;
2842           PetscSF            coarseCellSF = NULL;
2843           const PetscSFNode *coarseCells;
2844           PetscInt           numCoarseCells, cpdim, row = findices[i + off], q, c, j;
2845 
2846           /* Get points from the dual basis functional quadrature */
2847           PetscCall(PetscDualSpaceGetFunctional(Q, i, &f));
2848           PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, &qpoints, &qweights));
2849           PetscCheck(qNc == Nc, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of components in quadrature %" PetscInt_FMT " does not match coarse field %" PetscInt_FMT, qNc, Nc);
2850           PetscCall(VecCreateSeq(PETSC_COMM_SELF, Np * dim, &pointVec));
2851           PetscCall(VecSetBlockSize(pointVec, dim));
2852           PetscCall(VecGetArray(pointVec, &pV));
2853           for (q = 0; q < Np; ++q) {
2854             const PetscReal xi0[3] = {-1., -1., -1.};
2855 
2856             /* Transform point to real space */
2857             CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x);
2858             for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d];
2859           }
2860           PetscCall(VecRestoreArray(pointVec, &pV));
2861           /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
2862           /* OPT: Read this out from preallocation information */
2863           PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF));
2864           /* Update preallocation info */
2865           PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells));
2866           PetscCheck(numCoarseCells == Np, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located");
2867           PetscCall(VecGetArray(pointVec, &pV));
2868           for (ccell = 0; ccell < numCoarseCells; ++ccell) {
2869             PetscReal       pVReal[3];
2870             const PetscReal xi0[3] = {-1., -1., -1.};
2871 
2872             PetscCall(DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
2873             if (id == PETSCFE_CLASSID) PetscCall(PetscFEGetDimension((PetscFE)obj, &cpdim));
2874             else cpdim = 1;
2875 
2876             if (s) {
2877               /* Transform points from real space to coarse reference space */
2878               PetscCall(DMPlexComputeCellGeometryFEM(dmc, coarseCells[ccell].index, NULL, v0c, Jc, invJc, &detJc));
2879               for (d = 0; d < dim; ++d) pVReal[d] = PetscRealPart(pV[ccell * dim + d]);
2880               CoordinatesRealToRef(dim, dim, xi0, v0c, invJc, pVReal, x);
2881 
2882               if (id == PETSCFE_CLASSID) {
2883                 /* Evaluate coarse basis on contained point */
2884                 PetscCall(PetscFEComputeTabulation((PetscFE)obj, 1, x, 0, T));
2885                 PetscCall(PetscArrayzero(elemMat, cpdim));
2886                 /* Get elemMat entries by multiplying by weight */
2887                 for (j = 0; j < cpdim; ++j) {
2888                   for (c = 0; c < Nc; ++c) elemMat[j] += T->T[0][j * Nc + c] * qweights[ccell * qNc + c];
2889                 }
2890               } else {
2891                 for (j = 0; j < cpdim; ++j) {
2892                   for (c = 0; c < Nc; ++c) elemMat[j] += 1.0 * qweights[ccell * qNc + c];
2893                 }
2894               }
2895               if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat));
2896             }
2897             /* Update interpolator */
2898             PetscCheck(numCIndices == totDim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, totDim);
2899             PetscCall(MatSetValues(interp, 1, &row, cpdim, &cindices[off], elemMat, INSERT_VALUES));
2900             PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
2901           }
2902           PetscCall(VecRestoreArray(pointVec, &pV));
2903           PetscCall(PetscSFDestroy(&coarseCellSF));
2904           PetscCall(VecDestroy(&pointVec));
2905         }
2906         PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
2907       }
2908       if (s && id == PETSCFE_CLASSID) PetscCall(PetscTabulationDestroy(&T));
2909     }
2910     if (!s) {
2911       PetscCall(MatAssemblyBegin(interp, MAT_FINAL_ASSEMBLY));
2912       PetscCall(MatAssemblyEnd(interp, MAT_FINAL_ASSEMBLY));
2913       PetscCall(MatPreallocatorPreallocate(interp, PETSC_TRUE, In));
2914       PetscCall(MatDestroy(&interp));
2915       interp = In;
2916     }
2917   }
2918   PetscCall(PetscFree3(v0, J, invJ));
2919   PetscCall(PetscFree3(v0c, Jc, invJc));
2920   PetscCall(PetscFree(elemMat));
2921   PetscCall(MatAssemblyBegin(In, MAT_FINAL_ASSEMBLY));
2922   PetscCall(MatAssemblyEnd(In, MAT_FINAL_ASSEMBLY));
2923   PetscCall(PetscLogEventEnd(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
2924   PetscFunctionReturn(PETSC_SUCCESS);
2925 }
2926 
2927 /*@
2928   DMPlexComputeMassMatrixGeneral - Form the local portion of the mass matrix M from the coarse `DM` to a non-nested fine `DM`.
2929 
2930   Input Parameters:
2931 + dmf  - The fine mesh
2932 . dmc  - The coarse mesh
2933 - user - The user context
2934 
2935   Output Parameter:
2936 . mass - The mass matrix
2937 
2938   Level: developer
2939 
2940 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeMassMatrixNested()`, `DMPlexComputeInterpolatorNested()`, `DMPlexComputeInterpolatorGeneral()`, `DMPlexComputeJacobianFEM()`
2941 @*/
2942 PetscErrorCode DMPlexComputeMassMatrixGeneral(DM dmc, DM dmf, Mat mass, void *user)
2943 {
2944   DM_Plex     *mesh = (DM_Plex *)dmf->data;
2945   const char  *name = "Mass Matrix";
2946   PetscDS      prob;
2947   PetscSection fsection, csection, globalFSection, globalCSection;
2948   PetscHSetIJ  ht;
2949   PetscLayout  rLayout;
2950   PetscInt    *dnz, *onz;
2951   PetscInt     locRows, rStart, rEnd;
2952   PetscReal   *x, *v0, *J, *invJ, detJ;
2953   PetscReal   *v0c, *Jc, *invJc, detJc;
2954   PetscScalar *elemMat;
2955   PetscInt     dim, Nf, field, totDim, cStart, cEnd, cell, ccell;
2956 
2957   PetscFunctionBegin;
2958   PetscCall(DMGetCoordinateDim(dmc, &dim));
2959   PetscCall(DMGetDS(dmc, &prob));
2960   PetscCall(PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL));
2961   PetscCall(PetscDSGetNumFields(prob, &Nf));
2962   PetscCall(PetscMalloc3(dim, &v0, dim * dim, &J, dim * dim, &invJ));
2963   PetscCall(PetscMalloc3(dim, &v0c, dim * dim, &Jc, dim * dim, &invJc));
2964   PetscCall(DMGetLocalSection(dmf, &fsection));
2965   PetscCall(DMGetGlobalSection(dmf, &globalFSection));
2966   PetscCall(DMGetLocalSection(dmc, &csection));
2967   PetscCall(DMGetGlobalSection(dmc, &globalCSection));
2968   PetscCall(DMPlexGetHeightStratum(dmf, 0, &cStart, &cEnd));
2969   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
2970   PetscCall(PetscMalloc1(totDim, &elemMat));
2971 
2972   PetscCall(MatGetLocalSize(mass, &locRows, NULL));
2973   PetscCall(PetscLayoutCreate(PetscObjectComm((PetscObject)mass), &rLayout));
2974   PetscCall(PetscLayoutSetLocalSize(rLayout, locRows));
2975   PetscCall(PetscLayoutSetBlockSize(rLayout, 1));
2976   PetscCall(PetscLayoutSetUp(rLayout));
2977   PetscCall(PetscLayoutGetRange(rLayout, &rStart, &rEnd));
2978   PetscCall(PetscLayoutDestroy(&rLayout));
2979   PetscCall(PetscCalloc2(locRows, &dnz, locRows, &onz));
2980   PetscCall(PetscHSetIJCreate(&ht));
2981   for (field = 0; field < Nf; ++field) {
2982     PetscObject      obj;
2983     PetscClassId     id;
2984     PetscQuadrature  quad;
2985     const PetscReal *qpoints;
2986     PetscInt         Nq, Nc, i, d;
2987 
2988     PetscCall(PetscDSGetDiscretization(prob, field, &obj));
2989     PetscCall(PetscObjectGetClassId(obj, &id));
2990     if (id == PETSCFE_CLASSID) PetscCall(PetscFEGetQuadrature((PetscFE)obj, &quad));
2991     else PetscCall(PetscFVGetQuadrature((PetscFV)obj, &quad));
2992     PetscCall(PetscQuadratureGetData(quad, NULL, &Nc, &Nq, &qpoints, NULL));
2993     /* For each fine grid cell */
2994     for (cell = cStart; cell < cEnd; ++cell) {
2995       Vec                pointVec;
2996       PetscScalar       *pV;
2997       PetscSF            coarseCellSF = NULL;
2998       const PetscSFNode *coarseCells;
2999       PetscInt           numCoarseCells, q, c;
3000       PetscInt          *findices, *cindices;
3001       PetscInt           numFIndices, numCIndices;
3002 
3003       PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3004       PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ));
3005       /* Get points from the quadrature */
3006       PetscCall(VecCreateSeq(PETSC_COMM_SELF, Nq * dim, &pointVec));
3007       PetscCall(VecSetBlockSize(pointVec, dim));
3008       PetscCall(VecGetArray(pointVec, &pV));
3009       for (q = 0; q < Nq; ++q) {
3010         const PetscReal xi0[3] = {-1., -1., -1.};
3011 
3012         /* Transform point to real space */
3013         CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x);
3014         for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d];
3015       }
3016       PetscCall(VecRestoreArray(pointVec, &pV));
3017       /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
3018       PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF));
3019       PetscCall(PetscSFViewFromOptions(coarseCellSF, NULL, "-interp_sf_view"));
3020       /* Update preallocation info */
3021       PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells));
3022       PetscCheck(numCoarseCells == Nq, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located");
3023       {
3024         PetscHashIJKey key;
3025         PetscBool      missing;
3026 
3027         for (i = 0; i < numFIndices; ++i) {
3028           key.i = findices[i];
3029           if (key.i >= 0) {
3030             /* Get indices for coarse elements */
3031             for (ccell = 0; ccell < numCoarseCells; ++ccell) {
3032               PetscCall(DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3033               for (c = 0; c < numCIndices; ++c) {
3034                 key.j = cindices[c];
3035                 if (key.j < 0) continue;
3036                 PetscCall(PetscHSetIJQueryAdd(ht, key, &missing));
3037                 if (missing) {
3038                   if ((key.j >= rStart) && (key.j < rEnd)) ++dnz[key.i - rStart];
3039                   else ++onz[key.i - rStart];
3040                 }
3041               }
3042               PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3043             }
3044           }
3045         }
3046       }
3047       PetscCall(PetscSFDestroy(&coarseCellSF));
3048       PetscCall(VecDestroy(&pointVec));
3049       PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3050     }
3051   }
3052   PetscCall(PetscHSetIJDestroy(&ht));
3053   PetscCall(MatXAIJSetPreallocation(mass, 1, dnz, onz, NULL, NULL));
3054   PetscCall(MatSetOption(mass, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_TRUE));
3055   PetscCall(PetscFree2(dnz, onz));
3056   for (field = 0; field < Nf; ++field) {
3057     PetscObject      obj;
3058     PetscClassId     id;
3059     PetscTabulation  T, Tfine;
3060     PetscQuadrature  quad;
3061     const PetscReal *qpoints, *qweights;
3062     PetscInt         Nq, Nc, i, d;
3063 
3064     PetscCall(PetscDSGetDiscretization(prob, field, &obj));
3065     PetscCall(PetscObjectGetClassId(obj, &id));
3066     if (id == PETSCFE_CLASSID) {
3067       PetscCall(PetscFEGetQuadrature((PetscFE)obj, &quad));
3068       PetscCall(PetscFEGetCellTabulation((PetscFE)obj, 1, &Tfine));
3069       PetscCall(PetscFECreateTabulation((PetscFE)obj, 1, 1, x, 0, &T));
3070     } else {
3071       PetscCall(PetscFVGetQuadrature((PetscFV)obj, &quad));
3072     }
3073     PetscCall(PetscQuadratureGetData(quad, NULL, &Nc, &Nq, &qpoints, &qweights));
3074     /* For each fine grid cell */
3075     for (cell = cStart; cell < cEnd; ++cell) {
3076       Vec                pointVec;
3077       PetscScalar       *pV;
3078       PetscSF            coarseCellSF = NULL;
3079       const PetscSFNode *coarseCells;
3080       PetscInt           numCoarseCells, cpdim, q, c, j;
3081       PetscInt          *findices, *cindices;
3082       PetscInt           numFIndices, numCIndices;
3083 
3084       PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3085       PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ));
3086       /* Get points from the quadrature */
3087       PetscCall(VecCreateSeq(PETSC_COMM_SELF, Nq * dim, &pointVec));
3088       PetscCall(VecSetBlockSize(pointVec, dim));
3089       PetscCall(VecGetArray(pointVec, &pV));
3090       for (q = 0; q < Nq; ++q) {
3091         const PetscReal xi0[3] = {-1., -1., -1.};
3092 
3093         /* Transform point to real space */
3094         CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x);
3095         for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d];
3096       }
3097       PetscCall(VecRestoreArray(pointVec, &pV));
3098       /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
3099       PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF));
3100       /* Update matrix */
3101       PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells));
3102       PetscCheck(numCoarseCells == Nq, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located");
3103       PetscCall(VecGetArray(pointVec, &pV));
3104       for (ccell = 0; ccell < numCoarseCells; ++ccell) {
3105         PetscReal       pVReal[3];
3106         const PetscReal xi0[3] = {-1., -1., -1.};
3107 
3108         PetscCall(DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3109         /* Transform points from real space to coarse reference space */
3110         PetscCall(DMPlexComputeCellGeometryFEM(dmc, coarseCells[ccell].index, NULL, v0c, Jc, invJc, &detJc));
3111         for (d = 0; d < dim; ++d) pVReal[d] = PetscRealPart(pV[ccell * dim + d]);
3112         CoordinatesRealToRef(dim, dim, xi0, v0c, invJc, pVReal, x);
3113 
3114         if (id == PETSCFE_CLASSID) {
3115           PetscFE fe = (PetscFE)obj;
3116 
3117           /* Evaluate coarse basis on contained point */
3118           PetscCall(PetscFEGetDimension(fe, &cpdim));
3119           PetscCall(PetscFEComputeTabulation(fe, 1, x, 0, T));
3120           /* Get elemMat entries by multiplying by weight */
3121           for (i = 0; i < numFIndices; ++i) {
3122             PetscCall(PetscArrayzero(elemMat, cpdim));
3123             for (j = 0; j < cpdim; ++j) {
3124               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;
3125             }
3126             /* Update interpolator */
3127             if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat));
3128             PetscCheck(numCIndices == cpdim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, cpdim);
3129             PetscCall(MatSetValues(mass, 1, &findices[i], numCIndices, cindices, elemMat, ADD_VALUES));
3130           }
3131         } else {
3132           cpdim = 1;
3133           for (i = 0; i < numFIndices; ++i) {
3134             PetscCall(PetscArrayzero(elemMat, cpdim));
3135             for (j = 0; j < cpdim; ++j) {
3136               for (c = 0; c < Nc; ++c) elemMat[j] += 1.0 * 1.0 * qweights[ccell * Nc + c] * detJ;
3137             }
3138             /* Update interpolator */
3139             if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat));
3140             PetscCall(PetscPrintf(PETSC_COMM_SELF, "Nq: %" PetscInt_FMT " %" PetscInt_FMT " Nf: %" PetscInt_FMT " %" PetscInt_FMT " Nc: %" PetscInt_FMT " %" PetscInt_FMT "\n", ccell, Nq, i, numFIndices, j, numCIndices));
3141             PetscCheck(numCIndices == cpdim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, cpdim);
3142             PetscCall(MatSetValues(mass, 1, &findices[i], numCIndices, cindices, elemMat, ADD_VALUES));
3143           }
3144         }
3145         PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3146       }
3147       PetscCall(VecRestoreArray(pointVec, &pV));
3148       PetscCall(PetscSFDestroy(&coarseCellSF));
3149       PetscCall(VecDestroy(&pointVec));
3150       PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3151     }
3152     if (id == PETSCFE_CLASSID) PetscCall(PetscTabulationDestroy(&T));
3153   }
3154   PetscCall(PetscFree3(v0, J, invJ));
3155   PetscCall(PetscFree3(v0c, Jc, invJc));
3156   PetscCall(PetscFree(elemMat));
3157   PetscCall(MatAssemblyBegin(mass, MAT_FINAL_ASSEMBLY));
3158   PetscCall(MatAssemblyEnd(mass, MAT_FINAL_ASSEMBLY));
3159   PetscFunctionReturn(PETSC_SUCCESS);
3160 }
3161 
3162 /*@
3163   DMPlexComputeInjectorFEM - Compute a mapping from coarse unknowns to fine unknowns
3164 
3165   Input Parameters:
3166 + dmc  - The coarse mesh
3167 . dmf  - The fine mesh
3168 - user - The user context
3169 
3170   Output Parameter:
3171 . sc - The mapping
3172 
3173   Level: developer
3174 
3175 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorNested()`, `DMPlexComputeJacobianFEM()`
3176 @*/
3177 PetscErrorCode DMPlexComputeInjectorFEM(DM dmc, DM dmf, VecScatter *sc, void *user)
3178 {
3179   PetscDS      prob;
3180   PetscFE     *feRef;
3181   PetscFV     *fvRef;
3182   Vec          fv, cv;
3183   IS           fis, cis;
3184   PetscSection fsection, fglobalSection, csection, cglobalSection;
3185   PetscInt    *cmap, *cellCIndices, *cellFIndices, *cindices, *findices;
3186   PetscInt     cTotDim, fTotDim = 0, Nf, f, field, cStart, cEnd, c, dim, d, startC, endC, offsetC, offsetF, m;
3187   PetscBool   *needAvg;
3188 
3189   PetscFunctionBegin;
3190   PetscCall(PetscLogEventBegin(DMPLEX_InjectorFEM, dmc, dmf, 0, 0));
3191   PetscCall(DMGetDimension(dmf, &dim));
3192   PetscCall(DMGetLocalSection(dmf, &fsection));
3193   PetscCall(DMGetGlobalSection(dmf, &fglobalSection));
3194   PetscCall(DMGetLocalSection(dmc, &csection));
3195   PetscCall(DMGetGlobalSection(dmc, &cglobalSection));
3196   PetscCall(PetscSectionGetNumFields(fsection, &Nf));
3197   PetscCall(DMPlexGetSimplexOrBoxCells(dmc, 0, &cStart, &cEnd));
3198   PetscCall(DMGetDS(dmc, &prob));
3199   PetscCall(PetscCalloc3(Nf, &feRef, Nf, &fvRef, Nf, &needAvg));
3200   for (f = 0; f < Nf; ++f) {
3201     PetscObject  obj;
3202     PetscClassId id;
3203     PetscInt     fNb = 0, Nc = 0;
3204 
3205     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
3206     PetscCall(PetscObjectGetClassId(obj, &id));
3207     if (id == PETSCFE_CLASSID) {
3208       PetscFE    fe = (PetscFE)obj;
3209       PetscSpace sp;
3210       PetscInt   maxDegree;
3211 
3212       PetscCall(PetscFERefine(fe, &feRef[f]));
3213       PetscCall(PetscFEGetDimension(feRef[f], &fNb));
3214       PetscCall(PetscFEGetNumComponents(fe, &Nc));
3215       PetscCall(PetscFEGetBasisSpace(fe, &sp));
3216       PetscCall(PetscSpaceGetDegree(sp, NULL, &maxDegree));
3217       if (!maxDegree) needAvg[f] = PETSC_TRUE;
3218     } else if (id == PETSCFV_CLASSID) {
3219       PetscFV        fv = (PetscFV)obj;
3220       PetscDualSpace Q;
3221 
3222       PetscCall(PetscFVRefine(fv, &fvRef[f]));
3223       PetscCall(PetscFVGetDualSpace(fvRef[f], &Q));
3224       PetscCall(PetscDualSpaceGetDimension(Q, &fNb));
3225       PetscCall(PetscFVGetNumComponents(fv, &Nc));
3226       needAvg[f] = PETSC_TRUE;
3227     }
3228     fTotDim += fNb;
3229   }
3230   PetscCall(PetscDSGetTotalDimension(prob, &cTotDim));
3231   PetscCall(PetscMalloc1(cTotDim, &cmap));
3232   for (field = 0, offsetC = 0, offsetF = 0; field < Nf; ++field) {
3233     PetscFE        feC;
3234     PetscFV        fvC;
3235     PetscDualSpace QF, QC;
3236     PetscInt       order = -1, NcF, NcC, fpdim, cpdim;
3237 
3238     if (feRef[field]) {
3239       PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&feC));
3240       PetscCall(PetscFEGetNumComponents(feC, &NcC));
3241       PetscCall(PetscFEGetNumComponents(feRef[field], &NcF));
3242       PetscCall(PetscFEGetDualSpace(feRef[field], &QF));
3243       PetscCall(PetscDualSpaceGetOrder(QF, &order));
3244       PetscCall(PetscDualSpaceGetDimension(QF, &fpdim));
3245       PetscCall(PetscFEGetDualSpace(feC, &QC));
3246       PetscCall(PetscDualSpaceGetDimension(QC, &cpdim));
3247     } else {
3248       PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fvC));
3249       PetscCall(PetscFVGetNumComponents(fvC, &NcC));
3250       PetscCall(PetscFVGetNumComponents(fvRef[field], &NcF));
3251       PetscCall(PetscFVGetDualSpace(fvRef[field], &QF));
3252       PetscCall(PetscDualSpaceGetDimension(QF, &fpdim));
3253       PetscCall(PetscFVGetDualSpace(fvC, &QC));
3254       PetscCall(PetscDualSpaceGetDimension(QC, &cpdim));
3255     }
3256     PetscCheck(NcF == NcC, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of components in fine space field %" PetscInt_FMT " does not match coarse field %" PetscInt_FMT, NcF, NcC);
3257     for (c = 0; c < cpdim; ++c) {
3258       PetscQuadrature  cfunc;
3259       const PetscReal *cqpoints, *cqweights;
3260       PetscInt         NqcC, NpC;
3261       PetscBool        found = PETSC_FALSE;
3262 
3263       PetscCall(PetscDualSpaceGetFunctional(QC, c, &cfunc));
3264       PetscCall(PetscQuadratureGetData(cfunc, NULL, &NqcC, &NpC, &cqpoints, &cqweights));
3265       PetscCheck(NqcC == NcC, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of quadrature components %" PetscInt_FMT " must match number of field components %" PetscInt_FMT, NqcC, NcC);
3266       PetscCheck(NpC == 1 || !feRef[field], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Do not know how to do injection for moments");
3267       for (f = 0; f < fpdim; ++f) {
3268         PetscQuadrature  ffunc;
3269         const PetscReal *fqpoints, *fqweights;
3270         PetscReal        sum = 0.0;
3271         PetscInt         NqcF, NpF;
3272 
3273         PetscCall(PetscDualSpaceGetFunctional(QF, f, &ffunc));
3274         PetscCall(PetscQuadratureGetData(ffunc, NULL, &NqcF, &NpF, &fqpoints, &fqweights));
3275         PetscCheck(NqcF == NcF, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of quadrature components %" PetscInt_FMT " must match number of field components %" PetscInt_FMT, NqcF, NcF);
3276         if (NpC != NpF) continue;
3277         for (d = 0; d < dim; ++d) sum += PetscAbsReal(cqpoints[d] - fqpoints[d]);
3278         if (sum > 1.0e-9) continue;
3279         for (d = 0; d < NcC; ++d) sum += PetscAbsReal(cqweights[d] * fqweights[d]);
3280         if (sum < 1.0e-9) continue;
3281         cmap[offsetC + c] = offsetF + f;
3282         found             = PETSC_TRUE;
3283         break;
3284       }
3285       if (!found) {
3286         /* TODO We really want the average here, but some asshole put VecScatter in the interface */
3287         if (fvRef[field] || (feRef[field] && order == 0)) {
3288           cmap[offsetC + c] = offsetF + 0;
3289         } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Could not locate matching functional for injection");
3290       }
3291     }
3292     offsetC += cpdim;
3293     offsetF += fpdim;
3294   }
3295   for (f = 0; f < Nf; ++f) {
3296     PetscCall(PetscFEDestroy(&feRef[f]));
3297     PetscCall(PetscFVDestroy(&fvRef[f]));
3298   }
3299   PetscCall(PetscFree3(feRef, fvRef, needAvg));
3300 
3301   PetscCall(DMGetGlobalVector(dmf, &fv));
3302   PetscCall(DMGetGlobalVector(dmc, &cv));
3303   PetscCall(VecGetOwnershipRange(cv, &startC, &endC));
3304   PetscCall(PetscSectionGetConstrainedStorageSize(cglobalSection, &m));
3305   PetscCall(PetscMalloc2(cTotDim, &cellCIndices, fTotDim, &cellFIndices));
3306   PetscCall(PetscMalloc1(m, &cindices));
3307   PetscCall(PetscMalloc1(m, &findices));
3308   for (d = 0; d < m; ++d) cindices[d] = findices[d] = -1;
3309   for (c = cStart; c < cEnd; ++c) {
3310     PetscCall(DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, c, cellCIndices, cellFIndices));
3311     for (d = 0; d < cTotDim; ++d) {
3312       if ((cellCIndices[d] < startC) || (cellCIndices[d] >= endC)) continue;
3313       PetscCheck(!(findices[cellCIndices[d] - startC] >= 0) || !(findices[cellCIndices[d] - startC] != cellFIndices[cmap[d]]), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Coarse dof %" PetscInt_FMT " maps to both %" PetscInt_FMT " and %" PetscInt_FMT, cindices[cellCIndices[d] - startC], findices[cellCIndices[d] - startC], cellFIndices[cmap[d]]);
3314       cindices[cellCIndices[d] - startC] = cellCIndices[d];
3315       findices[cellCIndices[d] - startC] = cellFIndices[cmap[d]];
3316     }
3317   }
3318   PetscCall(PetscFree(cmap));
3319   PetscCall(PetscFree2(cellCIndices, cellFIndices));
3320 
3321   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, m, cindices, PETSC_OWN_POINTER, &cis));
3322   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, m, findices, PETSC_OWN_POINTER, &fis));
3323   PetscCall(VecScatterCreate(cv, cis, fv, fis, sc));
3324   PetscCall(ISDestroy(&cis));
3325   PetscCall(ISDestroy(&fis));
3326   PetscCall(DMRestoreGlobalVector(dmf, &fv));
3327   PetscCall(DMRestoreGlobalVector(dmc, &cv));
3328   PetscCall(PetscLogEventEnd(DMPLEX_InjectorFEM, dmc, dmf, 0, 0));
3329   PetscFunctionReturn(PETSC_SUCCESS);
3330 }
3331 
3332 /*@C
3333   DMPlexGetCellFields - Retrieve the field values values for a chunk of cells
3334 
3335   Input Parameters:
3336 + dm     - The `DM`
3337 . cellIS - The cells to include
3338 . locX   - A local vector with the solution fields
3339 . locX_t - A local vector with solution field time derivatives, or NULL
3340 - locA   - A local vector with auxiliary fields, or NULL
3341 
3342   Output Parameters:
3343 + u   - The field coefficients
3344 . u_t - The fields derivative coefficients
3345 - a   - The auxiliary field coefficients
3346 
3347   Level: developer
3348 
3349 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
3350 @*/
3351 PetscErrorCode DMPlexGetCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
3352 {
3353   DM              plex, plexA = NULL;
3354   DMEnclosureType encAux;
3355   PetscSection    section, sectionAux;
3356   PetscDS         prob;
3357   const PetscInt *cells;
3358   PetscInt        cStart, cEnd, numCells, totDim, totDimAux, c;
3359 
3360   PetscFunctionBegin;
3361   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3362   PetscValidHeaderSpecific(locX, VEC_CLASSID, 3);
3363   if (locX_t) PetscValidHeaderSpecific(locX_t, VEC_CLASSID, 4);
3364   if (locA) PetscValidHeaderSpecific(locA, VEC_CLASSID, 5);
3365   PetscAssertPointer(u, 6);
3366   PetscAssertPointer(u_t, 7);
3367   PetscAssertPointer(a, 8);
3368   PetscCall(DMPlexConvertPlex(dm, &plex, PETSC_FALSE));
3369   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
3370   PetscCall(DMGetLocalSection(dm, &section));
3371   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL));
3372   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
3373   if (locA) {
3374     DM      dmAux;
3375     PetscDS probAux;
3376 
3377     PetscCall(VecGetDM(locA, &dmAux));
3378     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
3379     PetscCall(DMPlexConvertPlex(dmAux, &plexA, PETSC_FALSE));
3380     PetscCall(DMGetLocalSection(dmAux, &sectionAux));
3381     PetscCall(DMGetDS(dmAux, &probAux));
3382     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
3383   }
3384   numCells = cEnd - cStart;
3385   PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u));
3386   if (locX_t) PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u_t));
3387   else *u_t = NULL;
3388   if (locA) PetscCall(DMGetWorkArray(dm, numCells * totDimAux, MPIU_SCALAR, a));
3389   else *a = NULL;
3390   for (c = cStart; c < cEnd; ++c) {
3391     const PetscInt cell = cells ? cells[c] : c;
3392     const PetscInt cind = c - cStart;
3393     PetscScalar   *x = NULL, *x_t = NULL, *ul = *u, *ul_t = *u_t, *al = *a;
3394     PetscInt       i;
3395 
3396     PetscCall(DMPlexVecGetClosure(plex, section, locX, cell, NULL, &x));
3397     for (i = 0; i < totDim; ++i) ul[cind * totDim + i] = x[i];
3398     PetscCall(DMPlexVecRestoreClosure(plex, section, locX, cell, NULL, &x));
3399     if (locX_t) {
3400       PetscCall(DMPlexVecGetClosure(plex, section, locX_t, cell, NULL, &x_t));
3401       for (i = 0; i < totDim; ++i) ul_t[cind * totDim + i] = x_t[i];
3402       PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, cell, NULL, &x_t));
3403     }
3404     if (locA) {
3405       PetscInt subcell;
3406       PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, cell, &subcell));
3407       PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subcell, NULL, &x));
3408       for (i = 0; i < totDimAux; ++i) al[cind * totDimAux + i] = x[i];
3409       PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subcell, NULL, &x));
3410     }
3411   }
3412   PetscCall(DMDestroy(&plex));
3413   if (locA) PetscCall(DMDestroy(&plexA));
3414   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
3415   PetscFunctionReturn(PETSC_SUCCESS);
3416 }
3417 
3418 /*@C
3419   DMPlexRestoreCellFields - Restore the field values values for a chunk of cells
3420 
3421   Input Parameters:
3422 + dm     - The `DM`
3423 . cellIS - The cells to include
3424 . locX   - A local vector with the solution fields
3425 . locX_t - A local vector with solution field time derivatives, or NULL
3426 - locA   - A local vector with auxiliary fields, or NULL
3427 
3428   Output Parameters:
3429 + u   - The field coefficients
3430 . u_t - The fields derivative coefficients
3431 - a   - The auxiliary field coefficients
3432 
3433   Level: developer
3434 
3435 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
3436 @*/
3437 PetscErrorCode DMPlexRestoreCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
3438 {
3439   PetscFunctionBegin;
3440   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u));
3441   if (locX_t) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u_t));
3442   if (locA) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, a));
3443   PetscFunctionReturn(PETSC_SUCCESS);
3444 }
3445 
3446 static PetscErrorCode DMPlexGetHybridCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
3447 {
3448   DM              plex, plexA = NULL;
3449   DMEnclosureType encAux;
3450   PetscSection    section, sectionAux;
3451   PetscDS         ds, dsIn;
3452   const PetscInt *cells;
3453   PetscInt        cStart, cEnd, numCells, c, totDim, totDimAux, Nf, f;
3454 
3455   PetscFunctionBegin;
3456   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3457   PetscValidHeaderSpecific(cellIS, IS_CLASSID, 2);
3458   PetscValidHeaderSpecific(locX, VEC_CLASSID, 3);
3459   if (locX_t) { PetscValidHeaderSpecific(locX_t, VEC_CLASSID, 4); }
3460   if (locA) { PetscValidHeaderSpecific(locA, VEC_CLASSID, 5); }
3461   PetscAssertPointer(u, 6);
3462   PetscAssertPointer(u_t, 7);
3463   PetscAssertPointer(a, 8);
3464   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
3465   numCells = cEnd - cStart;
3466   PetscCall(DMPlexConvertPlex(dm, &plex, PETSC_FALSE));
3467   PetscCall(DMGetLocalSection(dm, &section));
3468   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn));
3469   PetscCall(PetscDSGetNumFields(dsIn, &Nf));
3470   PetscCall(PetscDSGetTotalDimension(dsIn, &totDim));
3471   if (locA) {
3472     DM      dmAux;
3473     PetscDS probAux;
3474 
3475     PetscCall(VecGetDM(locA, &dmAux));
3476     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
3477     PetscCall(DMPlexConvertPlex(dmAux, &plexA, PETSC_FALSE));
3478     PetscCall(DMGetLocalSection(dmAux, &sectionAux));
3479     PetscCall(DMGetDS(dmAux, &probAux));
3480     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
3481   }
3482   PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u));
3483   if (locX_t) PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u_t));
3484   else {
3485     *u_t = NULL;
3486   }
3487   if (locA) PetscCall(DMGetWorkArray(dm, numCells * totDimAux, MPIU_SCALAR, a));
3488   else {
3489     *a = NULL;
3490   }
3491   // Loop over cohesive cells
3492   for (c = cStart; c < cEnd; ++c) {
3493     const PetscInt  cell = cells ? cells[c] : c;
3494     const PetscInt  cind = c - cStart;
3495     PetscScalar    *xf = NULL, *xc = NULL, *x = NULL, *xf_t = NULL, *xc_t = NULL;
3496     PetscScalar    *ul = &(*u)[cind * totDim], *ul_t = u_t ? &(*u_t)[cind * totDim] : NULL;
3497     const PetscInt *cone, *ornt;
3498     PetscInt        Nx = 0, Nxf, s;
3499 
3500     PetscCall(DMPlexGetCone(dm, cell, &cone));
3501     PetscCall(DMPlexGetConeOrientation(dm, cell, &ornt));
3502     // Put in cohesive unknowns
3503     PetscCall(DMPlexVecGetClosure(plex, section, locX, cell, &Nxf, &xf));
3504     if (locX_t) PetscCall(DMPlexVecGetClosure(plex, section, locX_t, cell, NULL, &xf_t));
3505     for (f = 0; f < Nf; ++f) {
3506       PetscInt  fdofIn, foff, foffIn;
3507       PetscBool cohesive;
3508 
3509       PetscCall(PetscDSGetCohesive(dsIn, f, &cohesive));
3510       if (!cohesive) continue;
3511       PetscCall(PetscDSGetFieldSize(dsIn, f, &fdofIn));
3512       PetscCall(PetscDSGetFieldOffsetCohesive(ds, f, &foff));
3513       PetscCall(PetscDSGetFieldOffsetCohesive(dsIn, f, &foffIn));
3514       for (PetscInt i = 0; i < fdofIn; ++i) ul[foffIn + i] = xf[foff + i];
3515       if (locX_t)
3516         for (PetscInt i = 0; i < fdofIn; ++i) ul_t[foffIn + i] = xf_t[foff + i];
3517       Nx += fdofIn;
3518     }
3519     PetscCall(DMPlexVecRestoreClosure(plex, section, locX, cell, &Nxf, &xf));
3520     if (locX_t) PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, cell, NULL, &xf_t));
3521     // Loop over sides of surface
3522     for (s = 0; s < 2; ++s) {
3523       const PetscInt *support;
3524       const PetscInt  face = cone[s];
3525       PetscInt        ssize, ncell, Nxc;
3526 
3527       // I don't think I need the face to have 0 orientation in the hybrid cell
3528       //PetscCheck(!ornt[s], PETSC_COMM_SELF, PETSC_ERR_SUP, "Face %" PetscInt_FMT " in hybrid cell %" PetscInt_FMT " has orientation %" PetscInt_FMT " != 0", face, cell, ornt[s]);
3529       PetscCall(DMPlexGetSupport(dm, face, &support));
3530       PetscCall(DMPlexGetSupportSize(dm, face, &ssize));
3531       if (support[0] == cell) ncell = support[1];
3532       else if (support[1] == cell) ncell = support[0];
3533       else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", face, cell);
3534       // Get closure of both face and cell, stick in cell for normal fields and face for cohesive fields
3535       PetscCall(DMPlexVecGetClosure(plex, section, locX, ncell, &Nxc, &xc));
3536       if (locX_t) PetscCall(DMPlexVecGetClosure(plex, section, locX_t, ncell, NULL, &xc_t));
3537       for (f = 0; f < Nf; ++f) {
3538         PetscInt  fdofIn, foffIn;
3539         PetscBool cohesive;
3540 
3541         PetscCall(PetscDSGetCohesive(dsIn, f, &cohesive));
3542         if (cohesive) continue;
3543         PetscCall(PetscDSGetFieldSize(dsIn, f, &fdofIn));
3544         PetscCall(PetscDSGetFieldOffsetCohesive(dsIn, f, &foffIn));
3545         for (PetscInt i = 0; i < fdofIn; ++i) ul[foffIn + s * fdofIn + i] = xc[foffIn + i];
3546         if (locX_t)
3547           for (PetscInt i = 0; i < fdofIn; ++i) ul_t[foffIn + s * fdofIn + i] = xc_t[foffIn + i];
3548         Nx += fdofIn;
3549       }
3550       PetscCall(DMPlexVecRestoreClosure(plex, section, locX, ncell, &Nxc, &xc));
3551       if (locX_t) PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, ncell, NULL, &xc_t));
3552     }
3553     PetscCheck(Nx == totDim, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Closure size %" PetscInt_FMT " for cell %" PetscInt_FMT " does not match DS size %" PetscInt_FMT, Nx, cell, totDim);
3554 
3555     if (locA) {
3556       PetscScalar *al = &(*a)[cind * totDimAux];
3557       PetscInt     subcell;
3558 
3559       PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, cell, &subcell));
3560       PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subcell, &Nx, &x));
3561       PetscCheck(Nx == totDimAux, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Closure size %" PetscInt_FMT " for subcell %" PetscInt_FMT "does not match DS size %" PetscInt_FMT, Nx, subcell, totDimAux);
3562       for (PetscInt i = 0; i < totDimAux; ++i) al[i] = x[i];
3563       PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subcell, &Nx, &x));
3564     }
3565   }
3566   PetscCall(DMDestroy(&plex));
3567   PetscCall(DMDestroy(&plexA));
3568   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
3569   PetscFunctionReturn(PETSC_SUCCESS);
3570 }
3571 
3572 /*
3573   DMPlexGetHybridFields - Get the field values for the negative side (s = 0) and positive side (s = 1) of the interface
3574 
3575   Input Parameters:
3576 + dm      - The full domain DM
3577 . dmX     - An array of DM for the field, say an auxiliary DM, indexed by s
3578 . dsX     - An array of PetscDS for the field, indexed by s
3579 . cellIS  - The interface cells for which we want values
3580 . locX    - An array of local vectors with the field values, indexed by s
3581 - useCell - Flag to have values come from neighboring cell rather than endcap face
3582 
3583   Output Parameter:
3584 . x       - An array of field values, indexed by s
3585 
3586   Note:
3587   The arrays in `x` will be allocated using `DMGetWorkArray()`, and must be returned using `DMPlexRestoreHybridFields()`.
3588 
3589   Level: advanced
3590 
3591 .seealso: `DMPlexRestoreHybridFields()`, `DMGetWorkArray()`
3592 */
3593 static PetscErrorCode DMPlexGetHybridFields(DM dm, DM dmX[], PetscDS dsX[], IS cellIS, Vec locX[], PetscBool useCell, PetscScalar *x[])
3594 {
3595   DM              plexX[2];
3596   DMEnclosureType encX[2];
3597   PetscSection    sectionX[2];
3598   const PetscInt *cells;
3599   PetscInt        cStart, cEnd, numCells, c, s, totDimX[2];
3600 
3601   PetscFunctionBegin;
3602   PetscAssertPointer(locX, 5);
3603   if (!locX[0] || !locX[1]) PetscFunctionReturn(PETSC_SUCCESS);
3604   PetscAssertPointer(dmX, 2);
3605   PetscAssertPointer(dsX, 3);
3606   PetscValidHeaderSpecific(cellIS, IS_CLASSID, 4);
3607   PetscAssertPointer(x, 7);
3608   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
3609   numCells = cEnd - cStart;
3610   for (s = 0; s < 2; ++s) {
3611     PetscValidHeaderSpecific(dmX[s], DM_CLASSID, 2);
3612     PetscValidHeaderSpecific(dsX[s], PETSCDS_CLASSID, 3);
3613     PetscValidHeaderSpecific(locX[s], VEC_CLASSID, 5);
3614     PetscCall(DMPlexConvertPlex(dmX[s], &plexX[s], PETSC_FALSE));
3615     PetscCall(DMGetEnclosureRelation(dmX[s], dm, &encX[s]));
3616     PetscCall(DMGetLocalSection(dmX[s], &sectionX[s]));
3617     PetscCall(PetscDSGetTotalDimension(dsX[s], &totDimX[s]));
3618     PetscCall(DMGetWorkArray(dmX[s], numCells * totDimX[s], MPIU_SCALAR, &x[s]));
3619   }
3620   for (c = cStart; c < cEnd; ++c) {
3621     const PetscInt  cell = cells ? cells[c] : c;
3622     const PetscInt  cind = c - cStart;
3623     const PetscInt *cone, *ornt;
3624 
3625     PetscCall(DMPlexGetCone(dm, cell, &cone));
3626     PetscCall(DMPlexGetConeOrientation(dm, cell, &ornt));
3627     //PetscCheck(!ornt[0], PETSC_COMM_SELF, PETSC_ERR_SUP, "Face %" PetscInt_FMT " in hybrid cell %" PetscInt_FMT " has orientation %" PetscInt_FMT " != 0", cone[0], cell, ornt[0]);
3628     for (s = 0; s < 2; ++s) {
3629       const PetscInt tdX     = totDimX[s];
3630       PetscScalar   *closure = NULL, *xl = &x[s][cind * tdX];
3631       PetscInt       face = cone[s], point = face, subpoint, Nx, i;
3632 
3633       if (useCell) {
3634         const PetscInt *support;
3635         PetscInt        ssize;
3636 
3637         PetscCall(DMPlexGetSupport(dm, face, &support));
3638         PetscCall(DMPlexGetSupportSize(dm, face, &ssize));
3639         PetscCheck(ssize == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " from cell %" PetscInt_FMT " has support size %" PetscInt_FMT " != 2", face, cell, ssize);
3640         if (support[0] == cell) point = support[1];
3641         else if (support[1] == cell) point = support[0];
3642         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", face, cell);
3643       }
3644       PetscCall(DMGetEnclosurePoint(plexX[s], dm, encX[s], point, &subpoint));
3645       PetscCall(DMPlexVecGetOrientedClosure_Internal(plexX[s], sectionX[s], locX[s], subpoint, ornt[s], &Nx, &closure));
3646       PetscCheck(Nx == tdX, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Closure size %" PetscInt_FMT " for subpoint %" PetscInt_FMT " does not match DS size %" PetscInt_FMT, Nx, subpoint, tdX);
3647       for (i = 0; i < Nx; ++i) xl[i] = closure[i];
3648       PetscCall(DMPlexVecRestoreClosure(plexX[s], sectionX[s], locX[s], subpoint, &Nx, &closure));
3649     }
3650   }
3651   for (s = 0; s < 2; ++s) PetscCall(DMDestroy(&plexX[s]));
3652   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
3653   PetscFunctionReturn(PETSC_SUCCESS);
3654 }
3655 
3656 static PetscErrorCode DMPlexRestoreHybridFields(DM dm, DM dmX[], PetscDS dsX[], IS cellIS, Vec locX[], PetscBool useCell, PetscScalar *x[])
3657 {
3658   PetscFunctionBegin;
3659   if (!locX[0] || !locX[1]) PetscFunctionReturn(PETSC_SUCCESS);
3660   PetscCall(DMRestoreWorkArray(dmX[0], 0, MPIU_SCALAR, &x[0]));
3661   PetscCall(DMRestoreWorkArray(dmX[1], 0, MPIU_SCALAR, &x[1]));
3662   PetscFunctionReturn(PETSC_SUCCESS);
3663 }
3664 
3665 /*@C
3666   DMPlexGetFaceFields - Retrieve the field values values for a chunk of faces
3667 
3668   Input Parameters:
3669 + dm           - The `DM`
3670 . fStart       - The first face to include
3671 . fEnd         - The first face to exclude
3672 . locX         - A local vector with the solution fields
3673 . locX_t       - A local vector with solution field time derivatives, or NULL
3674 . faceGeometry - A local vector with face geometry
3675 . cellGeometry - A local vector with cell geometry
3676 - locGrad      - A local vector with field gradients, or NULL
3677 
3678   Output Parameters:
3679 + Nface - The number of faces with field values
3680 . uL    - The field values at the left side of the face
3681 - uR    - The field values at the right side of the face
3682 
3683   Level: developer
3684 
3685 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellFields()`
3686 @*/
3687 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)
3688 {
3689   DM                 dmFace, dmCell, dmGrad = NULL;
3690   PetscSection       section;
3691   PetscDS            prob;
3692   DMLabel            ghostLabel;
3693   const PetscScalar *facegeom, *cellgeom, *x, *lgrad;
3694   PetscBool         *isFE;
3695   PetscInt           dim, Nf, f, Nc, numFaces = fEnd - fStart, iface, face;
3696 
3697   PetscFunctionBegin;
3698   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3699   PetscValidHeaderSpecific(locX, VEC_CLASSID, 4);
3700   if (locX_t) PetscValidHeaderSpecific(locX_t, VEC_CLASSID, 5);
3701   PetscValidHeaderSpecific(faceGeometry, VEC_CLASSID, 6);
3702   PetscValidHeaderSpecific(cellGeometry, VEC_CLASSID, 7);
3703   if (locGrad) PetscValidHeaderSpecific(locGrad, VEC_CLASSID, 8);
3704   PetscAssertPointer(uL, 10);
3705   PetscAssertPointer(uR, 11);
3706   PetscCall(DMGetDimension(dm, &dim));
3707   PetscCall(DMGetDS(dm, &prob));
3708   PetscCall(DMGetLocalSection(dm, &section));
3709   PetscCall(PetscDSGetNumFields(prob, &Nf));
3710   PetscCall(PetscDSGetTotalComponents(prob, &Nc));
3711   PetscCall(PetscMalloc1(Nf, &isFE));
3712   for (f = 0; f < Nf; ++f) {
3713     PetscObject  obj;
3714     PetscClassId id;
3715 
3716     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
3717     PetscCall(PetscObjectGetClassId(obj, &id));
3718     if (id == PETSCFE_CLASSID) {
3719       isFE[f] = PETSC_TRUE;
3720     } else if (id == PETSCFV_CLASSID) {
3721       isFE[f] = PETSC_FALSE;
3722     } else {
3723       isFE[f] = PETSC_FALSE;
3724     }
3725   }
3726   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
3727   PetscCall(VecGetArrayRead(locX, &x));
3728   PetscCall(VecGetDM(faceGeometry, &dmFace));
3729   PetscCall(VecGetArrayRead(faceGeometry, &facegeom));
3730   PetscCall(VecGetDM(cellGeometry, &dmCell));
3731   PetscCall(VecGetArrayRead(cellGeometry, &cellgeom));
3732   if (locGrad) {
3733     PetscCall(VecGetDM(locGrad, &dmGrad));
3734     PetscCall(VecGetArrayRead(locGrad, &lgrad));
3735   }
3736   PetscCall(DMGetWorkArray(dm, numFaces * Nc, MPIU_SCALAR, uL));
3737   PetscCall(DMGetWorkArray(dm, numFaces * Nc, MPIU_SCALAR, uR));
3738   /* Right now just eat the extra work for FE (could make a cell loop) */
3739   for (face = fStart, iface = 0; face < fEnd; ++face) {
3740     const PetscInt  *cells;
3741     PetscFVFaceGeom *fg;
3742     PetscFVCellGeom *cgL, *cgR;
3743     PetscScalar     *xL, *xR, *gL, *gR;
3744     PetscScalar     *uLl = *uL, *uRl = *uR;
3745     PetscInt         ghost, nsupp, nchild;
3746 
3747     PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
3748     PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
3749     PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
3750     if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
3751     PetscCall(DMPlexPointLocalRead(dmFace, face, facegeom, &fg));
3752     PetscCall(DMPlexGetSupport(dm, face, &cells));
3753     PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL));
3754     PetscCall(DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR));
3755     for (f = 0; f < Nf; ++f) {
3756       PetscInt off;
3757 
3758       PetscCall(PetscDSGetComponentOffset(prob, f, &off));
3759       if (isFE[f]) {
3760         const PetscInt *cone;
3761         PetscInt        comp, coneSizeL, coneSizeR, faceLocL, faceLocR, ldof, rdof, d;
3762 
3763         xL = xR = NULL;
3764         PetscCall(PetscSectionGetFieldComponents(section, f, &comp));
3765         PetscCall(DMPlexVecGetClosure(dm, section, locX, cells[0], &ldof, (PetscScalar **)&xL));
3766         PetscCall(DMPlexVecGetClosure(dm, section, locX, cells[1], &rdof, (PetscScalar **)&xR));
3767         PetscCall(DMPlexGetCone(dm, cells[0], &cone));
3768         PetscCall(DMPlexGetConeSize(dm, cells[0], &coneSizeL));
3769         for (faceLocL = 0; faceLocL < coneSizeL; ++faceLocL)
3770           if (cone[faceLocL] == face) break;
3771         PetscCall(DMPlexGetCone(dm, cells[1], &cone));
3772         PetscCall(DMPlexGetConeSize(dm, cells[1], &coneSizeR));
3773         for (faceLocR = 0; faceLocR < coneSizeR; ++faceLocR)
3774           if (cone[faceLocR] == face) break;
3775         PetscCheck(faceLocL != coneSizeL || faceLocR != coneSizeR, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find face %" PetscInt_FMT " in cone of cell %" PetscInt_FMT " or cell %" PetscInt_FMT, face, cells[0], cells[1]);
3776         /* Check that FEM field has values in the right cell (sometimes its an FV ghost cell) */
3777         /* TODO: this is a hack that might not be right for nonconforming */
3778         if (faceLocL < coneSizeL) {
3779           PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocL, xL, &uLl[iface * Nc + off]));
3780           if (rdof == ldof && faceLocR < coneSizeR) PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface * Nc + off]));
3781           else {
3782             for (d = 0; d < comp; ++d) uRl[iface * Nc + off + d] = uLl[iface * Nc + off + d];
3783           }
3784         } else {
3785           PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface * Nc + off]));
3786           PetscCall(PetscSectionGetFieldComponents(section, f, &comp));
3787           for (d = 0; d < comp; ++d) uLl[iface * Nc + off + d] = uRl[iface * Nc + off + d];
3788         }
3789         PetscCall(DMPlexVecRestoreClosure(dm, section, locX, cells[0], &ldof, (PetscScalar **)&xL));
3790         PetscCall(DMPlexVecRestoreClosure(dm, section, locX, cells[1], &rdof, (PetscScalar **)&xR));
3791       } else {
3792         PetscFV  fv;
3793         PetscInt numComp, c;
3794 
3795         PetscCall(PetscDSGetDiscretization(prob, f, (PetscObject *)&fv));
3796         PetscCall(PetscFVGetNumComponents(fv, &numComp));
3797         PetscCall(DMPlexPointLocalFieldRead(dm, cells[0], f, x, &xL));
3798         PetscCall(DMPlexPointLocalFieldRead(dm, cells[1], f, x, &xR));
3799         if (dmGrad) {
3800           PetscReal dxL[3], dxR[3];
3801 
3802           PetscCall(DMPlexPointLocalRead(dmGrad, cells[0], lgrad, &gL));
3803           PetscCall(DMPlexPointLocalRead(dmGrad, cells[1], lgrad, &gR));
3804           DMPlex_WaxpyD_Internal(dim, -1, cgL->centroid, fg->centroid, dxL);
3805           DMPlex_WaxpyD_Internal(dim, -1, cgR->centroid, fg->centroid, dxR);
3806           for (c = 0; c < numComp; ++c) {
3807             uLl[iface * Nc + off + c] = xL[c] + DMPlex_DotD_Internal(dim, &gL[c * dim], dxL);
3808             uRl[iface * Nc + off + c] = xR[c] + DMPlex_DotD_Internal(dim, &gR[c * dim], dxR);
3809           }
3810         } else {
3811           for (c = 0; c < numComp; ++c) {
3812             uLl[iface * Nc + off + c] = xL[c];
3813             uRl[iface * Nc + off + c] = xR[c];
3814           }
3815         }
3816       }
3817     }
3818     ++iface;
3819   }
3820   *Nface = iface;
3821   PetscCall(VecRestoreArrayRead(locX, &x));
3822   PetscCall(VecRestoreArrayRead(faceGeometry, &facegeom));
3823   PetscCall(VecRestoreArrayRead(cellGeometry, &cellgeom));
3824   if (locGrad) PetscCall(VecRestoreArrayRead(locGrad, &lgrad));
3825   PetscCall(PetscFree(isFE));
3826   PetscFunctionReturn(PETSC_SUCCESS);
3827 }
3828 
3829 /*@C
3830   DMPlexRestoreFaceFields - Restore the field values values for a chunk of faces
3831 
3832   Input Parameters:
3833 + dm           - The `DM`
3834 . fStart       - The first face to include
3835 . fEnd         - The first face to exclude
3836 . locX         - A local vector with the solution fields
3837 . locX_t       - A local vector with solution field time derivatives, or NULL
3838 . faceGeometry - A local vector with face geometry
3839 . cellGeometry - A local vector with cell geometry
3840 - locGrad      - A local vector with field gradients, or NULL
3841 
3842   Output Parameters:
3843 + Nface - The number of faces with field values
3844 . uL    - The field values at the left side of the face
3845 - uR    - The field values at the right side of the face
3846 
3847   Level: developer
3848 
3849 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
3850 @*/
3851 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)
3852 {
3853   PetscFunctionBegin;
3854   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uL));
3855   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uR));
3856   PetscFunctionReturn(PETSC_SUCCESS);
3857 }
3858 
3859 /*@C
3860   DMPlexGetFaceGeometry - Retrieve the geometric values for a chunk of faces
3861 
3862   Input Parameters:
3863 + dm           - The `DM`
3864 . fStart       - The first face to include
3865 . fEnd         - The first face to exclude
3866 . faceGeometry - A local vector with face geometry
3867 - cellGeometry - A local vector with cell geometry
3868 
3869   Output Parameters:
3870 + Nface - The number of faces with field values
3871 . fgeom - The extract the face centroid and normal
3872 - vol   - The cell volume
3873 
3874   Level: developer
3875 
3876 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellFields()`
3877 @*/
3878 PetscErrorCode DMPlexGetFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom **fgeom, PetscReal **vol)
3879 {
3880   DM                 dmFace, dmCell;
3881   DMLabel            ghostLabel;
3882   const PetscScalar *facegeom, *cellgeom;
3883   PetscInt           dim, numFaces = fEnd - fStart, iface, face;
3884 
3885   PetscFunctionBegin;
3886   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3887   PetscValidHeaderSpecific(faceGeometry, VEC_CLASSID, 4);
3888   PetscValidHeaderSpecific(cellGeometry, VEC_CLASSID, 5);
3889   PetscAssertPointer(fgeom, 7);
3890   PetscAssertPointer(vol, 8);
3891   PetscCall(DMGetDimension(dm, &dim));
3892   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
3893   PetscCall(VecGetDM(faceGeometry, &dmFace));
3894   PetscCall(VecGetArrayRead(faceGeometry, &facegeom));
3895   PetscCall(VecGetDM(cellGeometry, &dmCell));
3896   PetscCall(VecGetArrayRead(cellGeometry, &cellgeom));
3897   PetscCall(PetscMalloc1(numFaces, fgeom));
3898   PetscCall(DMGetWorkArray(dm, numFaces * 2, MPIU_SCALAR, vol));
3899   for (face = fStart, iface = 0; face < fEnd; ++face) {
3900     const PetscInt  *cells;
3901     PetscFVFaceGeom *fg;
3902     PetscFVCellGeom *cgL, *cgR;
3903     PetscFVFaceGeom *fgeoml = *fgeom;
3904     PetscReal       *voll   = *vol;
3905     PetscInt         ghost, d, nchild, nsupp;
3906 
3907     PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
3908     PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
3909     PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
3910     if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
3911     PetscCall(DMPlexPointLocalRead(dmFace, face, facegeom, &fg));
3912     PetscCall(DMPlexGetSupport(dm, face, &cells));
3913     PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL));
3914     PetscCall(DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR));
3915     for (d = 0; d < dim; ++d) {
3916       fgeoml[iface].centroid[d] = fg->centroid[d];
3917       fgeoml[iface].normal[d]   = fg->normal[d];
3918     }
3919     voll[iface * 2 + 0] = cgL->volume;
3920     voll[iface * 2 + 1] = cgR->volume;
3921     ++iface;
3922   }
3923   *Nface = iface;
3924   PetscCall(VecRestoreArrayRead(faceGeometry, &facegeom));
3925   PetscCall(VecRestoreArrayRead(cellGeometry, &cellgeom));
3926   PetscFunctionReturn(PETSC_SUCCESS);
3927 }
3928 
3929 /*@C
3930   DMPlexRestoreFaceGeometry - Restore the field values values for a chunk of faces
3931 
3932   Input Parameters:
3933 + dm           - The `DM`
3934 . fStart       - The first face to include
3935 . fEnd         - The first face to exclude
3936 . faceGeometry - A local vector with face geometry
3937 - cellGeometry - A local vector with cell geometry
3938 
3939   Output Parameters:
3940 + Nface - The number of faces with field values
3941 . fgeom - The extract the face centroid and normal
3942 - vol   - The cell volume
3943 
3944   Level: developer
3945 
3946 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
3947 @*/
3948 PetscErrorCode DMPlexRestoreFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom **fgeom, PetscReal **vol)
3949 {
3950   PetscFunctionBegin;
3951   PetscCall(PetscFree(*fgeom));
3952   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_REAL, vol));
3953   PetscFunctionReturn(PETSC_SUCCESS);
3954 }
3955 
3956 PetscErrorCode DMSNESGetFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
3957 {
3958   char           composeStr[33] = {0};
3959   PetscObjectId  id;
3960   PetscContainer container;
3961 
3962   PetscFunctionBegin;
3963   PetscCall(PetscObjectGetId((PetscObject)quad, &id));
3964   PetscCall(PetscSNPrintf(composeStr, 32, "DMSNESGetFEGeom_%" PetscInt64_FMT "\n", id));
3965   PetscCall(PetscObjectQuery((PetscObject)pointIS, composeStr, (PetscObject *)&container));
3966   if (container) {
3967     PetscCall(PetscContainerGetPointer(container, (void **)geom));
3968   } else {
3969     PetscCall(DMFieldCreateFEGeom(coordField, pointIS, quad, faceData, geom));
3970     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &container));
3971     PetscCall(PetscContainerSetPointer(container, (void *)*geom));
3972     PetscCall(PetscContainerSetUserDestroy(container, PetscContainerUserDestroy_PetscFEGeom));
3973     PetscCall(PetscObjectCompose((PetscObject)pointIS, composeStr, (PetscObject)container));
3974     PetscCall(PetscContainerDestroy(&container));
3975   }
3976   PetscFunctionReturn(PETSC_SUCCESS);
3977 }
3978 
3979 PetscErrorCode DMSNESRestoreFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
3980 {
3981   PetscFunctionBegin;
3982   *geom = NULL;
3983   PetscFunctionReturn(PETSC_SUCCESS);
3984 }
3985 
3986 PetscErrorCode DMPlexComputeResidual_Patch_Internal(DM dm, PetscSection section, IS cellIS, PetscReal t, Vec locX, Vec locX_t, Vec locF, void *user)
3987 {
3988   DM_Plex        *mesh       = (DM_Plex *)dm->data;
3989   const char     *name       = "Residual";
3990   DM              dmAux      = NULL;
3991   DMLabel         ghostLabel = NULL;
3992   PetscDS         prob       = NULL;
3993   PetscDS         probAux    = NULL;
3994   PetscBool       useFEM     = PETSC_FALSE;
3995   PetscBool       isImplicit = (locX_t || t == PETSC_MIN_REAL) ? PETSC_TRUE : PETSC_FALSE;
3996   DMField         coordField = NULL;
3997   Vec             locA;
3998   PetscScalar    *u = NULL, *u_t, *a, *uL = NULL, *uR = NULL;
3999   IS              chunkIS;
4000   const PetscInt *cells;
4001   PetscInt        cStart, cEnd, numCells;
4002   PetscInt        Nf, f, totDim, totDimAux, numChunks, cellChunkSize, chunk, fStart, fEnd;
4003   PetscInt        maxDegree = PETSC_MAX_INT;
4004   PetscFormKey    key;
4005   PetscQuadrature affineQuad = NULL, *quads = NULL;
4006   PetscFEGeom    *affineGeom = NULL, **geoms = NULL;
4007 
4008   PetscFunctionBegin;
4009   PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
4010   /* FEM+FVM */
4011   /* 1: Get sizes from dm and dmAux */
4012   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
4013   PetscCall(DMGetDS(dm, &prob));
4014   PetscCall(PetscDSGetNumFields(prob, &Nf));
4015   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
4016   PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA));
4017   if (locA) {
4018     PetscCall(VecGetDM(locA, &dmAux));
4019     PetscCall(DMGetDS(dmAux, &probAux));
4020     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
4021   }
4022   /* 2: Get geometric data */
4023   for (f = 0; f < Nf; ++f) {
4024     PetscObject  obj;
4025     PetscClassId id;
4026     PetscBool    fimp;
4027 
4028     PetscCall(PetscDSGetImplicit(prob, f, &fimp));
4029     if (isImplicit != fimp) continue;
4030     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4031     PetscCall(PetscObjectGetClassId(obj, &id));
4032     if (id == PETSCFE_CLASSID) useFEM = PETSC_TRUE;
4033     PetscCheck(id != PETSCFV_CLASSID, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Use of FVM with PCPATCH not yet implemented");
4034   }
4035   if (useFEM) {
4036     PetscCall(DMGetCoordinateField(dm, &coordField));
4037     PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
4038     if (maxDegree <= 1) {
4039       PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad));
4040       if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
4041     } else {
4042       PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
4043       for (f = 0; f < Nf; ++f) {
4044         PetscObject  obj;
4045         PetscClassId id;
4046         PetscBool    fimp;
4047 
4048         PetscCall(PetscDSGetImplicit(prob, f, &fimp));
4049         if (isImplicit != fimp) continue;
4050         PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4051         PetscCall(PetscObjectGetClassId(obj, &id));
4052         if (id == PETSCFE_CLASSID) {
4053           PetscFE fe = (PetscFE)obj;
4054 
4055           PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
4056           PetscCall(PetscObjectReference((PetscObject)quads[f]));
4057           PetscCall(DMSNESGetFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
4058         }
4059       }
4060     }
4061   }
4062   /* Loop over chunks */
4063   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
4064   PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
4065   if (useFEM) PetscCall(ISCreate(PETSC_COMM_SELF, &chunkIS));
4066   numCells      = cEnd - cStart;
4067   numChunks     = 1;
4068   cellChunkSize = numCells / numChunks;
4069   numChunks     = PetscMin(1, numCells);
4070   key.label     = NULL;
4071   key.value     = 0;
4072   key.part      = 0;
4073   for (chunk = 0; chunk < numChunks; ++chunk) {
4074     PetscScalar     *elemVec, *fluxL = NULL, *fluxR = NULL;
4075     PetscReal       *vol   = NULL;
4076     PetscFVFaceGeom *fgeom = NULL;
4077     PetscInt         cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
4078     PetscInt         numFaces = 0;
4079 
4080     /* Extract field coefficients */
4081     if (useFEM) {
4082       PetscCall(ISGetPointSubrange(chunkIS, cS, cE, cells));
4083       PetscCall(DMPlexGetCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
4084       PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
4085       PetscCall(PetscArrayzero(elemVec, numCells * totDim));
4086     }
4087     /* TODO We will interlace both our field coefficients (u, u_t, uL, uR, etc.) and our output (elemVec, fL, fR). I think this works */
4088     /* Loop over fields */
4089     for (f = 0; f < Nf; ++f) {
4090       PetscObject  obj;
4091       PetscClassId id;
4092       PetscBool    fimp;
4093       PetscInt     numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;
4094 
4095       key.field = f;
4096       PetscCall(PetscDSGetImplicit(prob, f, &fimp));
4097       if (isImplicit != fimp) continue;
4098       PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4099       PetscCall(PetscObjectGetClassId(obj, &id));
4100       if (id == PETSCFE_CLASSID) {
4101         PetscFE         fe        = (PetscFE)obj;
4102         PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[f];
4103         PetscFEGeom    *chunkGeom = NULL;
4104         PetscQuadrature quad      = affineQuad ? affineQuad : quads[f];
4105         PetscInt        Nq, Nb;
4106 
4107         PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
4108         PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
4109         PetscCall(PetscFEGetDimension(fe, &Nb));
4110         blockSize = Nb;
4111         batchSize = numBlocks * blockSize;
4112         PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
4113         numChunks = numCells / (numBatches * batchSize);
4114         Ne        = numChunks * numBatches * batchSize;
4115         Nr        = numCells % (numBatches * batchSize);
4116         offset    = numCells - Nr;
4117         /* Integrate FE residual to get elemVec (need fields at quadrature points) */
4118         /*   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) */
4119         PetscCall(PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom));
4120         PetscCall(PetscFEIntegrateResidual(prob, key, Ne, chunkGeom, u, u_t, probAux, a, t, elemVec));
4121         PetscCall(PetscFEGeomGetChunk(geom, offset, numCells, &chunkGeom));
4122         PetscCall(PetscFEIntegrateResidual(prob, key, Nr, chunkGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, &a[offset * totDimAux], t, &elemVec[offset * totDim]));
4123         PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &chunkGeom));
4124       } else if (id == PETSCFV_CLASSID) {
4125         PetscFV fv = (PetscFV)obj;
4126 
4127         Ne = numFaces;
4128         /* Riemann solve over faces (need fields at face centroids) */
4129         /*   We need to evaluate FE fields at those coordinates */
4130         PetscCall(PetscFVIntegrateRHSFunction(fv, prob, f, Ne, fgeom, vol, uL, uR, fluxL, fluxR));
4131       } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
4132     }
4133     /* Loop over domain */
4134     if (useFEM) {
4135       /* Add elemVec to locX */
4136       for (c = cS; c < cE; ++c) {
4137         const PetscInt cell = cells ? cells[c] : c;
4138         const PetscInt cind = c - cStart;
4139 
4140         if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, name, totDim, &elemVec[cind * totDim]));
4141         if (ghostLabel) {
4142           PetscInt ghostVal;
4143 
4144           PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
4145           if (ghostVal > 0) continue;
4146         }
4147         PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind * totDim], ADD_ALL_VALUES));
4148       }
4149     }
4150     /* Handle time derivative */
4151     if (locX_t) {
4152       PetscScalar *x_t, *fa;
4153 
4154       PetscCall(VecGetArray(locF, &fa));
4155       PetscCall(VecGetArray(locX_t, &x_t));
4156       for (f = 0; f < Nf; ++f) {
4157         PetscFV      fv;
4158         PetscObject  obj;
4159         PetscClassId id;
4160         PetscInt     pdim, d;
4161 
4162         PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4163         PetscCall(PetscObjectGetClassId(obj, &id));
4164         if (id != PETSCFV_CLASSID) continue;
4165         fv = (PetscFV)obj;
4166         PetscCall(PetscFVGetNumComponents(fv, &pdim));
4167         for (c = cS; c < cE; ++c) {
4168           const PetscInt cell = cells ? cells[c] : c;
4169           PetscScalar   *u_t, *r;
4170 
4171           if (ghostLabel) {
4172             PetscInt ghostVal;
4173 
4174             PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
4175             if (ghostVal > 0) continue;
4176           }
4177           PetscCall(DMPlexPointLocalFieldRead(dm, cell, f, x_t, &u_t));
4178           PetscCall(DMPlexPointLocalFieldRef(dm, cell, f, fa, &r));
4179           for (d = 0; d < pdim; ++d) r[d] += u_t[d];
4180         }
4181       }
4182       PetscCall(VecRestoreArray(locX_t, &x_t));
4183       PetscCall(VecRestoreArray(locF, &fa));
4184     }
4185     if (useFEM) {
4186       PetscCall(DMPlexRestoreCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
4187       PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
4188     }
4189   }
4190   if (useFEM) PetscCall(ISDestroy(&chunkIS));
4191   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
4192   /* TODO Could include boundary residual here (see DMPlexComputeResidual_Internal) */
4193   if (useFEM) {
4194     if (maxDegree <= 1) {
4195       PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
4196       PetscCall(PetscQuadratureDestroy(&affineQuad));
4197     } else {
4198       for (f = 0; f < Nf; ++f) {
4199         PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
4200         PetscCall(PetscQuadratureDestroy(&quads[f]));
4201       }
4202       PetscCall(PetscFree2(quads, geoms));
4203     }
4204   }
4205   PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
4206   PetscFunctionReturn(PETSC_SUCCESS);
4207 }
4208 
4209 /*
4210   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
4211 
4212   X   - The local solution vector
4213   X_t - The local solution time derivative vector, or NULL
4214 */
4215 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)
4216 {
4217   DM_Plex        *mesh = (DM_Plex *)dm->data;
4218   const char     *name = "Jacobian", *nameP = "JacobianPre";
4219   DM              dmAux = NULL;
4220   PetscDS         prob, probAux = NULL;
4221   PetscSection    sectionAux = NULL;
4222   Vec             A;
4223   DMField         coordField;
4224   PetscFEGeom    *cgeomFEM;
4225   PetscQuadrature qGeom = NULL;
4226   Mat             J = Jac, JP = JacP;
4227   PetscScalar    *work, *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL, *elemMatP = NULL, *elemMatD = NULL;
4228   PetscBool       hasJac, hasPrec, hasDyn, assembleJac, *isFE, hasFV = PETSC_FALSE;
4229   const PetscInt *cells;
4230   PetscFormKey    key;
4231   PetscInt        Nf, fieldI, fieldJ, maxDegree, numCells, cStart, cEnd, numChunks, chunkSize, chunk, totDim, totDimAux = 0, sz, wsz, off = 0, offCell = 0;
4232 
4233   PetscFunctionBegin;
4234   PetscCall(ISGetLocalSize(cellIS, &numCells));
4235   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
4236   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
4237   PetscCall(DMGetDS(dm, &prob));
4238   PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &A));
4239   if (A) {
4240     PetscCall(VecGetDM(A, &dmAux));
4241     PetscCall(DMGetLocalSection(dmAux, &sectionAux));
4242     PetscCall(DMGetDS(dmAux, &probAux));
4243   }
4244   /* Get flags */
4245   PetscCall(PetscDSGetNumFields(prob, &Nf));
4246   PetscCall(DMGetWorkArray(dm, Nf, MPIU_BOOL, &isFE));
4247   for (fieldI = 0; fieldI < Nf; ++fieldI) {
4248     PetscObject  disc;
4249     PetscClassId id;
4250     PetscCall(PetscDSGetDiscretization(prob, fieldI, &disc));
4251     PetscCall(PetscObjectGetClassId(disc, &id));
4252     if (id == PETSCFE_CLASSID) {
4253       isFE[fieldI] = PETSC_TRUE;
4254     } else if (id == PETSCFV_CLASSID) {
4255       hasFV        = PETSC_TRUE;
4256       isFE[fieldI] = PETSC_FALSE;
4257     }
4258   }
4259   PetscCall(PetscDSHasJacobian(prob, &hasJac));
4260   PetscCall(PetscDSHasJacobianPreconditioner(prob, &hasPrec));
4261   PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
4262   assembleJac = hasJac && hasPrec && (Jac != JacP) ? PETSC_TRUE : PETSC_FALSE;
4263   hasDyn      = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
4264   if (hasFV) PetscCall(MatSetOption(JP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE)); /* No allocated space for FV stuff, so ignore the zero entries */
4265   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
4266   if (probAux) PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
4267   /* Compute batch sizes */
4268   if (isFE[0]) {
4269     PetscFE         fe;
4270     PetscQuadrature q;
4271     PetscInt        numQuadPoints, numBatches, batchSize, numBlocks, blockSize, Nb;
4272 
4273     PetscCall(PetscDSGetDiscretization(prob, 0, (PetscObject *)&fe));
4274     PetscCall(PetscFEGetQuadrature(fe, &q));
4275     PetscCall(PetscQuadratureGetData(q, NULL, NULL, &numQuadPoints, NULL, NULL));
4276     PetscCall(PetscFEGetDimension(fe, &Nb));
4277     PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
4278     blockSize = Nb * numQuadPoints;
4279     batchSize = numBlocks * blockSize;
4280     chunkSize = numBatches * batchSize;
4281     numChunks = numCells / chunkSize + numCells % chunkSize;
4282     PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
4283   } else {
4284     chunkSize = numCells;
4285     numChunks = 1;
4286   }
4287   /* Get work space */
4288   wsz = (((X ? 1 : 0) + (X_t ? 1 : 0) + (dmAux ? 1 : 0)) * totDim + ((hasJac ? 1 : 0) + (hasPrec ? 1 : 0) + (hasDyn ? 1 : 0)) * totDim * totDim) * chunkSize;
4289   PetscCall(DMGetWorkArray(dm, wsz, MPIU_SCALAR, &work));
4290   PetscCall(PetscArrayzero(work, wsz));
4291   off      = 0;
4292   u        = X ? (sz = chunkSize * totDim, off += sz, work + off - sz) : NULL;
4293   u_t      = X_t ? (sz = chunkSize * totDim, off += sz, work + off - sz) : NULL;
4294   a        = dmAux ? (sz = chunkSize * totDimAux, off += sz, work + off - sz) : NULL;
4295   elemMat  = hasJac ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL;
4296   elemMatP = hasPrec ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL;
4297   elemMatD = hasDyn ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL;
4298   PetscCheck(off == wsz, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Error is workspace size %" PetscInt_FMT " should be %" PetscInt_FMT, off, wsz);
4299   /* Setup geometry */
4300   PetscCall(DMGetCoordinateField(dm, &coordField));
4301   PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
4302   if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
4303   if (!qGeom) {
4304     PetscFE fe;
4305 
4306     PetscCall(PetscDSGetDiscretization(prob, 0, (PetscObject *)&fe));
4307     PetscCall(PetscFEGetQuadrature(fe, &qGeom));
4308     PetscCall(PetscObjectReference((PetscObject)qGeom));
4309   }
4310   PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
4311   /* Compute volume integrals */
4312   if (assembleJac) PetscCall(MatZeroEntries(J));
4313   PetscCall(MatZeroEntries(JP));
4314   key.label = NULL;
4315   key.value = 0;
4316   key.part  = 0;
4317   for (chunk = 0; chunk < numChunks; ++chunk, offCell += chunkSize) {
4318     const PetscInt Ncell = PetscMin(chunkSize, numCells - offCell);
4319     PetscInt       c;
4320 
4321     /* Extract values */
4322     for (c = 0; c < Ncell; ++c) {
4323       const PetscInt cell = cells ? cells[c + offCell] : c + offCell;
4324       PetscScalar   *x = NULL, *x_t = NULL;
4325       PetscInt       i;
4326 
4327       if (X) {
4328         PetscCall(DMPlexVecGetClosure(dm, section, X, cell, NULL, &x));
4329         for (i = 0; i < totDim; ++i) u[c * totDim + i] = x[i];
4330         PetscCall(DMPlexVecRestoreClosure(dm, section, X, cell, NULL, &x));
4331       }
4332       if (X_t) {
4333         PetscCall(DMPlexVecGetClosure(dm, section, X_t, cell, NULL, &x_t));
4334         for (i = 0; i < totDim; ++i) u_t[c * totDim + i] = x_t[i];
4335         PetscCall(DMPlexVecRestoreClosure(dm, section, X_t, cell, NULL, &x_t));
4336       }
4337       if (dmAux) {
4338         PetscCall(DMPlexVecGetClosure(dmAux, sectionAux, A, cell, NULL, &x));
4339         for (i = 0; i < totDimAux; ++i) a[c * totDimAux + i] = x[i];
4340         PetscCall(DMPlexVecRestoreClosure(dmAux, sectionAux, A, cell, NULL, &x));
4341       }
4342     }
4343     for (fieldI = 0; fieldI < Nf; ++fieldI) {
4344       PetscFE fe;
4345       PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
4346       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
4347         key.field = fieldI * Nf + fieldJ;
4348         if (hasJac) PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMat));
4349         if (hasPrec) PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatP));
4350         if (hasDyn) PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatD));
4351       }
4352       /* For finite volume, add the identity */
4353       if (!isFE[fieldI]) {
4354         PetscFV  fv;
4355         PetscInt eOffset = 0, Nc, fc, foff;
4356 
4357         PetscCall(PetscDSGetFieldOffset(prob, fieldI, &foff));
4358         PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fv));
4359         PetscCall(PetscFVGetNumComponents(fv, &Nc));
4360         for (c = 0; c < chunkSize; ++c, eOffset += totDim * totDim) {
4361           for (fc = 0; fc < Nc; ++fc) {
4362             const PetscInt i = foff + fc;
4363             if (hasJac) elemMat[eOffset + i * totDim + i] = 1.0;
4364             if (hasPrec) elemMatP[eOffset + i * totDim + i] = 1.0;
4365           }
4366         }
4367       }
4368     }
4369     /*   Add contribution from X_t */
4370     if (hasDyn) {
4371       for (c = 0; c < chunkSize * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
4372     }
4373     /* Insert values into matrix */
4374     for (c = 0; c < Ncell; ++c) {
4375       const PetscInt cell = cells ? cells[c + offCell] : c + offCell;
4376       if (mesh->printFEM > 1) {
4377         if (hasJac) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[(c - cStart) * totDim * totDim]));
4378         if (hasPrec) PetscCall(DMPrintCellMatrix(cell, nameP, totDim, totDim, &elemMatP[(c - cStart) * totDim * totDim]));
4379       }
4380       if (assembleJac) PetscCall(DMPlexMatSetClosure(dm, section, globalSection, Jac, cell, &elemMat[(c - cStart) * totDim * totDim], ADD_VALUES));
4381       PetscCall(DMPlexMatSetClosure(dm, section, globalSection, JP, cell, &elemMat[(c - cStart) * totDim * totDim], ADD_VALUES));
4382     }
4383   }
4384   /* Cleanup */
4385   PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
4386   PetscCall(PetscQuadratureDestroy(&qGeom));
4387   if (hasFV) PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE));
4388   PetscCall(DMRestoreWorkArray(dm, Nf, MPIU_BOOL, &isFE));
4389   PetscCall(DMRestoreWorkArray(dm, ((1 + (X_t ? 1 : 0) + (dmAux ? 1 : 0)) * totDim + ((hasJac ? 1 : 0) + (hasPrec ? 1 : 0) + (hasDyn ? 1 : 0)) * totDim * totDim) * chunkSize, MPIU_SCALAR, &work));
4390   /* Compute boundary integrals */
4391   /* PetscCall(DMPlexComputeBdJacobian_Internal(dm, X, X_t, t, X_tShift, Jac, JacP, ctx)); */
4392   /* Assemble matrix */
4393   if (assembleJac) {
4394     PetscCall(MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY));
4395     PetscCall(MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY));
4396   }
4397   PetscCall(MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY));
4398   PetscCall(MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY));
4399   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
4400   PetscFunctionReturn(PETSC_SUCCESS);
4401 }
4402 
4403 /******** FEM Assembly Function ********/
4404 
4405 static PetscErrorCode DMConvertPlex_Internal(DM dm, DM *plex, PetscBool copy)
4406 {
4407   PetscBool isPlex;
4408 
4409   PetscFunctionBegin;
4410   PetscCall(PetscObjectTypeCompare((PetscObject)dm, DMPLEX, &isPlex));
4411   if (isPlex) {
4412     *plex = dm;
4413     PetscCall(PetscObjectReference((PetscObject)dm));
4414   } else {
4415     PetscCall(PetscObjectQuery((PetscObject)dm, "dm_plex", (PetscObject *)plex));
4416     if (!*plex) {
4417       PetscCall(DMConvert(dm, DMPLEX, plex));
4418       PetscCall(PetscObjectCompose((PetscObject)dm, "dm_plex", (PetscObject)*plex));
4419       if (copy) PetscCall(DMCopyAuxiliaryVec(dm, *plex));
4420     } else {
4421       PetscCall(PetscObjectReference((PetscObject)*plex));
4422     }
4423   }
4424   PetscFunctionReturn(PETSC_SUCCESS);
4425 }
4426 
4427 /*@
4428   DMPlexGetGeometryFVM - Return precomputed geometric data
4429 
4430   Collective
4431 
4432   Input Parameter:
4433 . dm - The `DM`
4434 
4435   Output Parameters:
4436 + facegeom  - The values precomputed from face geometry
4437 . cellgeom  - The values precomputed from cell geometry
4438 - minRadius - The minimum radius over the mesh of an inscribed sphere in a cell
4439 
4440   Level: developer
4441 
4442 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMTSSetRHSFunctionLocal()`
4443 @*/
4444 PetscErrorCode DMPlexGetGeometryFVM(DM dm, Vec *facegeom, Vec *cellgeom, PetscReal *minRadius)
4445 {
4446   DM plex;
4447 
4448   PetscFunctionBegin;
4449   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4450   PetscCall(DMConvertPlex_Internal(dm, &plex, PETSC_TRUE));
4451   PetscCall(DMPlexGetDataFVM(plex, NULL, cellgeom, facegeom, NULL));
4452   if (minRadius) PetscCall(DMPlexGetMinRadius(plex, minRadius));
4453   PetscCall(DMDestroy(&plex));
4454   PetscFunctionReturn(PETSC_SUCCESS);
4455 }
4456 
4457 /*@
4458   DMPlexGetGradientDM - Return gradient data layout
4459 
4460   Collective
4461 
4462   Input Parameters:
4463 + dm - The `DM`
4464 - fv - The `PetscFV`
4465 
4466   Output Parameter:
4467 . dmGrad - The layout for gradient values
4468 
4469   Level: developer
4470 
4471 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetGeometryFVM()`
4472 @*/
4473 PetscErrorCode DMPlexGetGradientDM(DM dm, PetscFV fv, DM *dmGrad)
4474 {
4475   DM        plex;
4476   PetscBool computeGradients;
4477 
4478   PetscFunctionBegin;
4479   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4480   PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
4481   PetscAssertPointer(dmGrad, 3);
4482   PetscCall(PetscFVGetComputeGradients(fv, &computeGradients));
4483   if (!computeGradients) {
4484     *dmGrad = NULL;
4485     PetscFunctionReturn(PETSC_SUCCESS);
4486   }
4487   PetscCall(DMConvertPlex_Internal(dm, &plex, PETSC_TRUE));
4488   PetscCall(DMPlexGetDataFVM(plex, fv, NULL, NULL, dmGrad));
4489   PetscCall(DMDestroy(&plex));
4490   PetscFunctionReturn(PETSC_SUCCESS);
4491 }
4492 
4493 static PetscErrorCode DMPlexComputeBdResidual_Single_Internal(DM dm, PetscReal t, PetscWeakForm wf, PetscFormKey key, Vec locX, Vec locX_t, Vec locF, DMField coordField, IS facetIS)
4494 {
4495   DM_Plex        *mesh = (DM_Plex *)dm->data;
4496   DM              plex = NULL, plexA = NULL;
4497   DMEnclosureType encAux;
4498   PetscDS         prob, probAux       = NULL;
4499   PetscSection    section, sectionAux = NULL;
4500   Vec             locA = NULL;
4501   PetscScalar    *u = NULL, *u_t = NULL, *a = NULL, *elemVec = NULL;
4502   PetscInt        totDim, totDimAux = 0;
4503 
4504   PetscFunctionBegin;
4505   PetscCall(DMConvert(dm, DMPLEX, &plex));
4506   PetscCall(DMGetLocalSection(dm, &section));
4507   PetscCall(DMGetDS(dm, &prob));
4508   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
4509   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &locA));
4510   if (locA) {
4511     DM dmAux;
4512 
4513     PetscCall(VecGetDM(locA, &dmAux));
4514     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
4515     PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
4516     PetscCall(DMGetDS(plexA, &probAux));
4517     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
4518     PetscCall(DMGetLocalSection(plexA, &sectionAux));
4519   }
4520   {
4521     PetscFEGeom    *fgeom;
4522     PetscInt        maxDegree;
4523     PetscQuadrature qGeom = NULL;
4524     IS              pointIS;
4525     const PetscInt *points;
4526     PetscInt        numFaces, face, Nq;
4527 
4528     PetscCall(DMLabelGetStratumIS(key.label, key.value, &pointIS));
4529     if (!pointIS) goto end; /* No points with that id on this process */
4530     {
4531       IS isectIS;
4532 
4533       /* TODO: Special cases of ISIntersect where it is quick to check a priori if one is a superset of the other */
4534       PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS));
4535       PetscCall(ISDestroy(&pointIS));
4536       pointIS = isectIS;
4537     }
4538     PetscCall(ISGetLocalSize(pointIS, &numFaces));
4539     PetscCall(ISGetIndices(pointIS, &points));
4540     PetscCall(PetscMalloc4(numFaces * totDim, &u, locX_t ? numFaces * totDim : 0, &u_t, numFaces * totDim, &elemVec, locA ? numFaces * totDimAux : 0, &a));
4541     PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
4542     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom));
4543     if (!qGeom) {
4544       PetscFE fe;
4545 
4546       PetscCall(PetscDSGetDiscretization(prob, key.field, (PetscObject *)&fe));
4547       PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
4548       PetscCall(PetscObjectReference((PetscObject)qGeom));
4549     }
4550     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
4551     PetscCall(DMSNESGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
4552     for (face = 0; face < numFaces; ++face) {
4553       const PetscInt point = points[face], *support;
4554       PetscScalar   *x     = NULL;
4555       PetscInt       i;
4556 
4557       PetscCall(DMPlexGetSupport(dm, point, &support));
4558       PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
4559       for (i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
4560       PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
4561       if (locX_t) {
4562         PetscCall(DMPlexVecGetClosure(plex, section, locX_t, support[0], NULL, &x));
4563         for (i = 0; i < totDim; ++i) u_t[face * totDim + i] = x[i];
4564         PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, support[0], NULL, &x));
4565       }
4566       if (locA) {
4567         PetscInt subp;
4568 
4569         PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
4570         PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
4571         for (i = 0; i < totDimAux; ++i) a[face * totDimAux + i] = x[i];
4572         PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
4573       }
4574     }
4575     PetscCall(PetscArrayzero(elemVec, numFaces * totDim));
4576     {
4577       PetscFE      fe;
4578       PetscInt     Nb;
4579       PetscFEGeom *chunkGeom = NULL;
4580       /* Conforming batches */
4581       PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
4582       /* Remainder */
4583       PetscInt Nr, offset;
4584 
4585       PetscCall(PetscDSGetDiscretization(prob, key.field, (PetscObject *)&fe));
4586       PetscCall(PetscFEGetDimension(fe, &Nb));
4587       PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
4588       /* TODO: documentation is unclear about what is going on with these numbers: how should Nb / Nq factor in ? */
4589       blockSize = Nb;
4590       batchSize = numBlocks * blockSize;
4591       PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
4592       numChunks = numFaces / (numBatches * batchSize);
4593       Ne        = numChunks * numBatches * batchSize;
4594       Nr        = numFaces % (numBatches * batchSize);
4595       offset    = numFaces - Nr;
4596       PetscCall(PetscFEGeomGetChunk(fgeom, 0, offset, &chunkGeom));
4597       PetscCall(PetscFEIntegrateBdResidual(prob, wf, key, Ne, chunkGeom, u, u_t, probAux, a, t, elemVec));
4598       PetscCall(PetscFEGeomRestoreChunk(fgeom, 0, offset, &chunkGeom));
4599       PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom));
4600       PetscCall(PetscFEIntegrateBdResidual(prob, wf, key, Nr, chunkGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, a ? &a[offset * totDimAux] : NULL, t, &elemVec[offset * totDim]));
4601       PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom));
4602     }
4603     for (face = 0; face < numFaces; ++face) {
4604       const PetscInt point = points[face], *support;
4605 
4606       if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(point, "BdResidual", totDim, &elemVec[face * totDim]));
4607       PetscCall(DMPlexGetSupport(plex, point, &support));
4608       PetscCall(DMPlexVecSetClosure(plex, NULL, locF, support[0], &elemVec[face * totDim], ADD_ALL_VALUES));
4609     }
4610     PetscCall(DMSNESRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
4611     PetscCall(PetscQuadratureDestroy(&qGeom));
4612     PetscCall(ISRestoreIndices(pointIS, &points));
4613     PetscCall(ISDestroy(&pointIS));
4614     PetscCall(PetscFree4(u, u_t, elemVec, a));
4615   }
4616 end:
4617   PetscCall(DMDestroy(&plex));
4618   PetscCall(DMDestroy(&plexA));
4619   PetscFunctionReturn(PETSC_SUCCESS);
4620 }
4621 
4622 PetscErrorCode DMPlexComputeBdResidualSingle(DM dm, PetscReal t, PetscWeakForm wf, PetscFormKey key, Vec locX, Vec locX_t, Vec locF)
4623 {
4624   DMField  coordField;
4625   DMLabel  depthLabel;
4626   IS       facetIS;
4627   PetscInt dim;
4628 
4629   PetscFunctionBegin;
4630   PetscCall(DMGetDimension(dm, &dim));
4631   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
4632   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
4633   PetscCall(DMGetCoordinateField(dm, &coordField));
4634   PetscCall(DMPlexComputeBdResidual_Single_Internal(dm, t, wf, key, locX, locX_t, locF, coordField, facetIS));
4635   PetscCall(ISDestroy(&facetIS));
4636   PetscFunctionReturn(PETSC_SUCCESS);
4637 }
4638 
4639 static PetscErrorCode DMPlexComputeBdResidual_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
4640 {
4641   PetscDS  prob;
4642   PetscInt numBd, bd;
4643   DMField  coordField = NULL;
4644   IS       facetIS    = NULL;
4645   DMLabel  depthLabel;
4646   PetscInt dim;
4647 
4648   PetscFunctionBegin;
4649   PetscCall(DMGetDS(dm, &prob));
4650   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
4651   PetscCall(DMGetDimension(dm, &dim));
4652   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
4653   PetscCall(PetscDSGetNumBoundary(prob, &numBd));
4654   for (bd = 0; bd < numBd; ++bd) {
4655     PetscWeakForm           wf;
4656     DMBoundaryConditionType type;
4657     DMLabel                 label;
4658     const PetscInt         *values;
4659     PetscInt                field, numValues, v;
4660     PetscObject             obj;
4661     PetscClassId            id;
4662     PetscFormKey            key;
4663 
4664     PetscCall(PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &field, NULL, NULL, NULL, NULL, NULL));
4665     if (type & DM_BC_ESSENTIAL) continue;
4666     PetscCall(PetscDSGetDiscretization(prob, field, &obj));
4667     PetscCall(PetscObjectGetClassId(obj, &id));
4668     if (id != PETSCFE_CLASSID) continue;
4669     if (!facetIS) {
4670       DMLabel  depthLabel;
4671       PetscInt dim;
4672 
4673       PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
4674       PetscCall(DMGetDimension(dm, &dim));
4675       PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
4676     }
4677     PetscCall(DMGetCoordinateField(dm, &coordField));
4678     for (v = 0; v < numValues; ++v) {
4679       key.label = label;
4680       key.value = values[v];
4681       key.field = field;
4682       key.part  = 0;
4683       PetscCall(DMPlexComputeBdResidual_Single_Internal(dm, t, wf, key, locX, locX_t, locF, coordField, facetIS));
4684     }
4685   }
4686   PetscCall(ISDestroy(&facetIS));
4687   PetscFunctionReturn(PETSC_SUCCESS);
4688 }
4689 
4690 PetscErrorCode DMPlexComputeResidual_Internal(DM dm, PetscFormKey key, IS cellIS, PetscReal time, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
4691 {
4692   DM_Plex         *mesh       = (DM_Plex *)dm->data;
4693   const char      *name       = "Residual";
4694   DM               dmAux      = NULL;
4695   DM               dmGrad     = NULL;
4696   DMLabel          ghostLabel = NULL;
4697   PetscDS          ds         = NULL;
4698   PetscDS          dsAux      = NULL;
4699   PetscSection     section    = NULL;
4700   PetscBool        useFEM     = PETSC_FALSE;
4701   PetscBool        useFVM     = PETSC_FALSE;
4702   PetscBool        isImplicit = (locX_t || time == PETSC_MIN_REAL) ? PETSC_TRUE : PETSC_FALSE;
4703   PetscFV          fvm        = NULL;
4704   PetscFVCellGeom *cgeomFVM   = NULL;
4705   PetscFVFaceGeom *fgeomFVM   = NULL;
4706   DMField          coordField = NULL;
4707   Vec              locA, cellGeometryFVM = NULL, faceGeometryFVM = NULL, grad, locGrad = NULL;
4708   PetscScalar     *u = NULL, *u_t, *a, *uL, *uR;
4709   IS               chunkIS;
4710   const PetscInt  *cells;
4711   PetscInt         cStart, cEnd, numCells;
4712   PetscInt         Nf, f, totDim, totDimAux, numChunks, cellChunkSize, faceChunkSize, chunk, fStart, fEnd;
4713   PetscInt         maxDegree  = PETSC_MAX_INT;
4714   PetscQuadrature  affineQuad = NULL, *quads = NULL;
4715   PetscFEGeom     *affineGeom = NULL, **geoms = NULL;
4716 
4717   PetscFunctionBegin;
4718   if (!cellIS) PetscFunctionReturn(PETSC_SUCCESS);
4719   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
4720   if (cStart >= cEnd) PetscFunctionReturn(PETSC_SUCCESS);
4721   PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
4722   /* TODO The places where we have to use isFE are probably the member functions for the PetscDisc class */
4723   /* TODO The FVM geometry is over-manipulated. Make the precalc functions return exactly what we need */
4724   /* FEM+FVM */
4725   PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
4726   /* 1: Get sizes from dm and dmAux */
4727   PetscCall(DMGetLocalSection(dm, &section));
4728   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
4729   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, NULL));
4730   PetscCall(PetscDSGetNumFields(ds, &Nf));
4731   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
4732   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &locA));
4733   if (locA) {
4734     PetscInt subcell;
4735     PetscCall(VecGetDM(locA, &dmAux));
4736     PetscCall(DMGetEnclosurePoint(dmAux, dm, DM_ENC_UNKNOWN, cells ? cells[cStart] : cStart, &subcell));
4737     PetscCall(DMGetCellDS(dmAux, subcell, &dsAux, NULL));
4738     PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
4739   }
4740   /* 2: Get geometric data */
4741   for (f = 0; f < Nf; ++f) {
4742     PetscObject  obj;
4743     PetscClassId id;
4744     PetscBool    fimp;
4745 
4746     PetscCall(PetscDSGetImplicit(ds, f, &fimp));
4747     if (isImplicit != fimp) continue;
4748     PetscCall(PetscDSGetDiscretization(ds, f, &obj));
4749     PetscCall(PetscObjectGetClassId(obj, &id));
4750     if (id == PETSCFE_CLASSID) useFEM = PETSC_TRUE;
4751     if (id == PETSCFV_CLASSID) {
4752       useFVM = PETSC_TRUE;
4753       fvm    = (PetscFV)obj;
4754     }
4755   }
4756   if (useFEM) {
4757     PetscCall(DMGetCoordinateField(dm, &coordField));
4758     PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
4759     if (maxDegree <= 1) {
4760       PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad));
4761       if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
4762     } else {
4763       PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
4764       for (f = 0; f < Nf; ++f) {
4765         PetscObject  obj;
4766         PetscClassId id;
4767         PetscBool    fimp;
4768 
4769         PetscCall(PetscDSGetImplicit(ds, f, &fimp));
4770         if (isImplicit != fimp) continue;
4771         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
4772         PetscCall(PetscObjectGetClassId(obj, &id));
4773         if (id == PETSCFE_CLASSID) {
4774           PetscFE fe = (PetscFE)obj;
4775 
4776           PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
4777           PetscCall(PetscObjectReference((PetscObject)quads[f]));
4778           PetscCall(DMSNESGetFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
4779         }
4780       }
4781     }
4782   }
4783   if (useFVM) {
4784     PetscCall(DMPlexGetGeometryFVM(dm, &faceGeometryFVM, &cellGeometryFVM, NULL));
4785     PetscCall(VecGetArrayRead(faceGeometryFVM, (const PetscScalar **)&fgeomFVM));
4786     PetscCall(VecGetArrayRead(cellGeometryFVM, (const PetscScalar **)&cgeomFVM));
4787     /* Reconstruct and limit cell gradients */
4788     PetscCall(DMPlexGetGradientDM(dm, fvm, &dmGrad));
4789     if (dmGrad) {
4790       PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
4791       PetscCall(DMGetGlobalVector(dmGrad, &grad));
4792       PetscCall(DMPlexReconstructGradients_Internal(dm, fvm, fStart, fEnd, faceGeometryFVM, cellGeometryFVM, locX, grad));
4793       /* Communicate gradient values */
4794       PetscCall(DMGetLocalVector(dmGrad, &locGrad));
4795       PetscCall(DMGlobalToLocalBegin(dmGrad, grad, INSERT_VALUES, locGrad));
4796       PetscCall(DMGlobalToLocalEnd(dmGrad, grad, INSERT_VALUES, locGrad));
4797       PetscCall(DMRestoreGlobalVector(dmGrad, &grad));
4798     }
4799     /* Handle non-essential (e.g. outflow) boundary values */
4800     PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_FALSE, locX, time, faceGeometryFVM, cellGeometryFVM, locGrad));
4801   }
4802   /* Loop over chunks */
4803   if (useFEM) PetscCall(ISCreate(PETSC_COMM_SELF, &chunkIS));
4804   numCells      = cEnd - cStart;
4805   numChunks     = 1;
4806   cellChunkSize = numCells / numChunks;
4807   faceChunkSize = (fEnd - fStart) / numChunks;
4808   numChunks     = PetscMin(1, numCells);
4809   for (chunk = 0; chunk < numChunks; ++chunk) {
4810     PetscScalar     *elemVec, *fluxL, *fluxR;
4811     PetscReal       *vol;
4812     PetscFVFaceGeom *fgeom;
4813     PetscInt         cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
4814     PetscInt         fS = fStart + chunk * faceChunkSize, fE = PetscMin(fS + faceChunkSize, fEnd), numFaces = 0, face;
4815 
4816     /* Extract field coefficients */
4817     if (useFEM) {
4818       PetscCall(ISGetPointSubrange(chunkIS, cS, cE, cells));
4819       PetscCall(DMPlexGetCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
4820       PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
4821       PetscCall(PetscArrayzero(elemVec, numCells * totDim));
4822     }
4823     if (useFVM) {
4824       PetscCall(DMPlexGetFaceFields(dm, fS, fE, locX, locX_t, faceGeometryFVM, cellGeometryFVM, locGrad, &numFaces, &uL, &uR));
4825       PetscCall(DMPlexGetFaceGeometry(dm, fS, fE, faceGeometryFVM, cellGeometryFVM, &numFaces, &fgeom, &vol));
4826       PetscCall(DMGetWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxL));
4827       PetscCall(DMGetWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxR));
4828       PetscCall(PetscArrayzero(fluxL, numFaces * totDim));
4829       PetscCall(PetscArrayzero(fluxR, numFaces * totDim));
4830     }
4831     /* TODO We will interlace both our field coefficients (u, u_t, uL, uR, etc.) and our output (elemVec, fL, fR). I think this works */
4832     /* Loop over fields */
4833     for (f = 0; f < Nf; ++f) {
4834       PetscObject  obj;
4835       PetscClassId id;
4836       PetscBool    fimp;
4837       PetscInt     numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;
4838 
4839       key.field = f;
4840       PetscCall(PetscDSGetImplicit(ds, f, &fimp));
4841       if (isImplicit != fimp) continue;
4842       PetscCall(PetscDSGetDiscretization(ds, f, &obj));
4843       PetscCall(PetscObjectGetClassId(obj, &id));
4844       if (id == PETSCFE_CLASSID) {
4845         PetscFE         fe        = (PetscFE)obj;
4846         PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[f];
4847         PetscFEGeom    *chunkGeom = NULL;
4848         PetscQuadrature quad      = affineQuad ? affineQuad : quads[f];
4849         PetscInt        Nq, Nb;
4850 
4851         PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
4852         PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
4853         PetscCall(PetscFEGetDimension(fe, &Nb));
4854         blockSize = Nb;
4855         batchSize = numBlocks * blockSize;
4856         PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
4857         numChunks = numCells / (numBatches * batchSize);
4858         Ne        = numChunks * numBatches * batchSize;
4859         Nr        = numCells % (numBatches * batchSize);
4860         offset    = numCells - Nr;
4861         /* Integrate FE residual to get elemVec (need fields at quadrature points) */
4862         /*   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) */
4863         PetscCall(PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom));
4864         PetscCall(PetscFEIntegrateResidual(ds, key, Ne, chunkGeom, u, u_t, dsAux, a, t, elemVec));
4865         PetscCall(PetscFEGeomGetChunk(geom, offset, numCells, &chunkGeom));
4866         PetscCall(PetscFEIntegrateResidual(ds, key, Nr, chunkGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, dsAux, a ? &a[offset * totDimAux] : NULL, t, &elemVec[offset * totDim]));
4867         PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &chunkGeom));
4868       } else if (id == PETSCFV_CLASSID) {
4869         PetscFV fv = (PetscFV)obj;
4870 
4871         Ne = numFaces;
4872         /* Riemann solve over faces (need fields at face centroids) */
4873         /*   We need to evaluate FE fields at those coordinates */
4874         PetscCall(PetscFVIntegrateRHSFunction(fv, ds, f, Ne, fgeom, vol, uL, uR, fluxL, fluxR));
4875       } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
4876     }
4877     /* Loop over domain */
4878     if (useFEM) {
4879       /* Add elemVec to locX */
4880       for (c = cS; c < cE; ++c) {
4881         const PetscInt cell = cells ? cells[c] : c;
4882         const PetscInt cind = c - cStart;
4883 
4884         if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, name, totDim, &elemVec[cind * totDim]));
4885         if (ghostLabel) {
4886           PetscInt ghostVal;
4887 
4888           PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
4889           if (ghostVal > 0) continue;
4890         }
4891         PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind * totDim], ADD_ALL_VALUES));
4892       }
4893     }
4894     if (useFVM) {
4895       PetscScalar *fa;
4896       PetscInt     iface;
4897 
4898       PetscCall(VecGetArray(locF, &fa));
4899       for (f = 0; f < Nf; ++f) {
4900         PetscFV      fv;
4901         PetscObject  obj;
4902         PetscClassId id;
4903         PetscInt     foff, pdim;
4904 
4905         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
4906         PetscCall(PetscDSGetFieldOffset(ds, f, &foff));
4907         PetscCall(PetscObjectGetClassId(obj, &id));
4908         if (id != PETSCFV_CLASSID) continue;
4909         fv = (PetscFV)obj;
4910         PetscCall(PetscFVGetNumComponents(fv, &pdim));
4911         /* Accumulate fluxes to cells */
4912         for (face = fS, iface = 0; face < fE; ++face) {
4913           const PetscInt *scells;
4914           PetscScalar    *fL = NULL, *fR = NULL;
4915           PetscInt        ghost, d, nsupp, nchild;
4916 
4917           PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
4918           PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
4919           PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
4920           if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
4921           PetscCall(DMPlexGetSupport(dm, face, &scells));
4922           PetscCall(DMLabelGetValue(ghostLabel, scells[0], &ghost));
4923           if (ghost <= 0) PetscCall(DMPlexPointLocalFieldRef(dm, scells[0], f, fa, &fL));
4924           PetscCall(DMLabelGetValue(ghostLabel, scells[1], &ghost));
4925           if (ghost <= 0) PetscCall(DMPlexPointLocalFieldRef(dm, scells[1], f, fa, &fR));
4926           for (d = 0; d < pdim; ++d) {
4927             if (fL) fL[d] -= fluxL[iface * totDim + foff + d];
4928             if (fR) fR[d] += fluxR[iface * totDim + foff + d];
4929           }
4930           ++iface;
4931         }
4932       }
4933       PetscCall(VecRestoreArray(locF, &fa));
4934     }
4935     /* Handle time derivative */
4936     if (locX_t) {
4937       PetscScalar *x_t, *fa;
4938 
4939       PetscCall(VecGetArray(locF, &fa));
4940       PetscCall(VecGetArray(locX_t, &x_t));
4941       for (f = 0; f < Nf; ++f) {
4942         PetscFV      fv;
4943         PetscObject  obj;
4944         PetscClassId id;
4945         PetscInt     pdim, d;
4946 
4947         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
4948         PetscCall(PetscObjectGetClassId(obj, &id));
4949         if (id != PETSCFV_CLASSID) continue;
4950         fv = (PetscFV)obj;
4951         PetscCall(PetscFVGetNumComponents(fv, &pdim));
4952         for (c = cS; c < cE; ++c) {
4953           const PetscInt cell = cells ? cells[c] : c;
4954           PetscScalar   *u_t, *r;
4955 
4956           if (ghostLabel) {
4957             PetscInt ghostVal;
4958 
4959             PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
4960             if (ghostVal > 0) continue;
4961           }
4962           PetscCall(DMPlexPointLocalFieldRead(dm, cell, f, x_t, &u_t));
4963           PetscCall(DMPlexPointLocalFieldRef(dm, cell, f, fa, &r));
4964           for (d = 0; d < pdim; ++d) r[d] += u_t[d];
4965         }
4966       }
4967       PetscCall(VecRestoreArray(locX_t, &x_t));
4968       PetscCall(VecRestoreArray(locF, &fa));
4969     }
4970     if (useFEM) {
4971       PetscCall(DMPlexRestoreCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
4972       PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
4973     }
4974     if (useFVM) {
4975       PetscCall(DMPlexRestoreFaceFields(dm, fS, fE, locX, locX_t, faceGeometryFVM, cellGeometryFVM, locGrad, &numFaces, &uL, &uR));
4976       PetscCall(DMPlexRestoreFaceGeometry(dm, fS, fE, faceGeometryFVM, cellGeometryFVM, &numFaces, &fgeom, &vol));
4977       PetscCall(DMRestoreWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxL));
4978       PetscCall(DMRestoreWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxR));
4979       if (dmGrad) PetscCall(DMRestoreLocalVector(dmGrad, &locGrad));
4980     }
4981   }
4982   if (useFEM) PetscCall(ISDestroy(&chunkIS));
4983   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
4984 
4985   if (useFEM) {
4986     PetscCall(DMPlexComputeBdResidual_Internal(dm, locX, locX_t, t, locF, user));
4987 
4988     if (maxDegree <= 1) {
4989       PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
4990       PetscCall(PetscQuadratureDestroy(&affineQuad));
4991     } else {
4992       for (f = 0; f < Nf; ++f) {
4993         PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
4994         PetscCall(PetscQuadratureDestroy(&quads[f]));
4995       }
4996       PetscCall(PetscFree2(quads, geoms));
4997     }
4998   }
4999 
5000   /* FEM */
5001   /* 1: Get sizes from dm and dmAux */
5002   /* 2: Get geometric data */
5003   /* 3: Handle boundary values */
5004   /* 4: Loop over domain */
5005   /*   Extract coefficients */
5006   /* Loop over fields */
5007   /*   Set tiling for FE*/
5008   /*   Integrate FE residual to get elemVec */
5009   /*     Loop over subdomain */
5010   /*       Loop over quad points */
5011   /*         Transform coords to real space */
5012   /*         Evaluate field and aux fields at point */
5013   /*         Evaluate residual at point */
5014   /*         Transform residual to real space */
5015   /*       Add residual to elemVec */
5016   /* Loop over domain */
5017   /*   Add elemVec to locX */
5018 
5019   /* FVM */
5020   /* Get geometric data */
5021   /* If using gradients */
5022   /*   Compute gradient data */
5023   /*   Loop over domain faces */
5024   /*     Count computational faces */
5025   /*     Reconstruct cell gradient */
5026   /*   Loop over domain cells */
5027   /*     Limit cell gradients */
5028   /* Handle boundary values */
5029   /* Loop over domain faces */
5030   /*   Read out field, centroid, normal, volume for each side of face */
5031   /* Riemann solve over faces */
5032   /* Loop over domain faces */
5033   /*   Accumulate fluxes to cells */
5034   /* TODO Change printFEM to printDisc here */
5035   if (mesh->printFEM) {
5036     Vec          locFbc;
5037     PetscInt     pStart, pEnd, p, maxDof;
5038     PetscScalar *zeroes;
5039 
5040     PetscCall(VecDuplicate(locF, &locFbc));
5041     PetscCall(VecCopy(locF, locFbc));
5042     PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5043     PetscCall(PetscSectionGetMaxDof(section, &maxDof));
5044     PetscCall(PetscCalloc1(maxDof, &zeroes));
5045     for (p = pStart; p < pEnd; p++) PetscCall(VecSetValuesSection(locFbc, section, p, zeroes, INSERT_BC_VALUES));
5046     PetscCall(PetscFree(zeroes));
5047     PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc));
5048     PetscCall(VecDestroy(&locFbc));
5049   }
5050   PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
5051   PetscFunctionReturn(PETSC_SUCCESS);
5052 }
5053 
5054 /*
5055   1) Allow multiple kernels for BdResidual for hybrid DS
5056 
5057   DONE 2) Get out dsAux for either side at the same time as cohesive cell dsAux
5058 
5059   DONE 3) Change DMGetCellFields() to get different aux data a[] for each side
5060      - I think I just need to replace a[] with the closure from each face
5061 
5062   4) Run both kernels for each non-hybrid field with correct dsAux, and then hybrid field as before
5063 */
5064 PetscErrorCode DMPlexComputeResidual_Hybrid_Internal(DM dm, PetscFormKey key[], IS cellIS, PetscReal time, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
5065 {
5066   DM_Plex        *mesh       = (DM_Plex *)dm->data;
5067   const char     *name       = "Hybrid Residual";
5068   DM              dmAux[3]   = {NULL, NULL, NULL};
5069   DMLabel         ghostLabel = NULL;
5070   PetscDS         ds         = NULL;
5071   PetscDS         dsIn       = NULL;
5072   PetscDS         dsAux[3]   = {NULL, NULL, NULL};
5073   Vec             locA[3]    = {NULL, NULL, NULL};
5074   DM              dmScale[3] = {NULL, NULL, NULL};
5075   PetscDS         dsScale[3] = {NULL, NULL, NULL};
5076   Vec             locS[3]    = {NULL, NULL, NULL};
5077   PetscSection    section    = NULL;
5078   DMField         coordField = NULL;
5079   PetscScalar    *a[3]       = {NULL, NULL, NULL};
5080   PetscScalar    *s[3]       = {NULL, NULL, NULL};
5081   PetscScalar    *u          = NULL, *u_t;
5082   PetscScalar    *elemVecNeg, *elemVecPos, *elemVecCoh;
5083   IS              chunkIS;
5084   const PetscInt *cells;
5085   PetscInt       *faces;
5086   PetscInt        cStart, cEnd, numCells;
5087   PetscInt        Nf, f, totDim, totDimIn, totDimAux[3], totDimScale[3], numChunks, cellChunkSize, chunk;
5088   PetscInt        maxDegree  = PETSC_MAX_INT;
5089   PetscQuadrature affineQuad = NULL, *quads = NULL;
5090   PetscFEGeom    *affineGeom = NULL, **geoms = NULL;
5091 
5092   PetscFunctionBegin;
5093   if (!cellIS) PetscFunctionReturn(PETSC_SUCCESS);
5094   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
5095   PetscCall(ISGetLocalSize(cellIS, &numCells));
5096   if (cStart >= cEnd) PetscFunctionReturn(PETSC_SUCCESS);
5097   if ((key[0].label == key[1].label) && (key[0].value == key[1].value) && (key[0].part == key[1].part)) {
5098     const char *name;
5099     PetscCall(PetscObjectGetName((PetscObject)key[0].label, &name));
5100     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);
5101   }
5102   PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
5103   /* TODO The places where we have to use isFE are probably the member functions for the PetscDisc class */
5104   /* FEM */
5105   /* 1: Get sizes from dm and dmAux */
5106   PetscCall(DMGetSection(dm, &section));
5107   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
5108   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn));
5109   PetscCall(PetscDSGetNumFields(ds, &Nf));
5110   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
5111   PetscCall(PetscDSGetTotalDimension(dsIn, &totDimIn));
5112   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, key[2].value, key[2].part, &locA[2]));
5113   if (locA[2]) {
5114     const PetscInt cellStart = cells ? cells[cStart] : cStart;
5115 
5116     PetscCall(VecGetDM(locA[2], &dmAux[2]));
5117     PetscCall(DMGetCellDS(dmAux[2], cellStart, &dsAux[2], NULL));
5118     PetscCall(PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]));
5119     {
5120       const PetscInt *cone;
5121       PetscInt        c;
5122 
5123       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
5124       for (c = 0; c < 2; ++c) {
5125         const PetscInt *support;
5126         PetscInt        ssize, s;
5127 
5128         PetscCall(DMPlexGetSupport(dm, cone[c], &support));
5129         PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
5130         PetscCheck(ssize == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " from cell %" PetscInt_FMT " has support size %" PetscInt_FMT " != 2", cone[c], cellStart, ssize);
5131         if (support[0] == cellStart) s = 1;
5132         else if (support[1] == cellStart) s = 0;
5133         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
5134         PetscCall(DMGetAuxiliaryVec(dm, key[c].label, key[c].value, key[c].part, &locA[c]));
5135         PetscCheck(locA[c], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Must have auxiliary vector for (%p, %" PetscInt_FMT ", %" PetscInt_FMT ")", (void *)key[c].label, key[c].value, key[c].part);
5136         if (locA[c]) PetscCall(VecGetDM(locA[c], &dmAux[c]));
5137         else dmAux[c] = dmAux[2];
5138         PetscCall(DMGetCellDS(dmAux[c], support[s], &dsAux[c], NULL));
5139         PetscCall(PetscDSGetTotalDimension(dsAux[c], &totDimAux[c]));
5140       }
5141     }
5142   }
5143   /* Handle mass matrix scaling
5144        The field in key[2] is the field to be scaled, and the scaling field is the first in the dsScale */
5145   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, -key[2].value, key[2].part, &locS[2]));
5146   if (locS[2]) {
5147     const PetscInt cellStart = cells ? cells[cStart] : cStart;
5148     PetscInt       Nb, Nbs;
5149 
5150     PetscCall(VecGetDM(locS[2], &dmScale[2]));
5151     PetscCall(DMGetCellDS(dmScale[2], cellStart, &dsScale[2], NULL));
5152     PetscCall(PetscDSGetTotalDimension(dsScale[2], &totDimScale[2]));
5153     // BRAD: This is not set correctly
5154     key[2].field = 2;
5155     PetscCall(PetscDSGetFieldSize(ds, key[2].field, &Nb));
5156     PetscCall(PetscDSGetFieldSize(dsScale[2], 0, &Nbs));
5157     PetscCheck(Nb == Nbs, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Field %" PetscInt_FMT " of size %" PetscInt_FMT " cannot be scaled by field of size %" PetscInt_FMT, key[2].field, Nb, Nbs);
5158     {
5159       const PetscInt *cone;
5160       PetscInt        c;
5161 
5162       locS[1] = locS[0] = locS[2];
5163       dmScale[1] = dmScale[0] = dmScale[2];
5164       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
5165       for (c = 0; c < 2; ++c) {
5166         const PetscInt *support;
5167         PetscInt        ssize, s;
5168 
5169         PetscCall(DMPlexGetSupport(dm, cone[c], &support));
5170         PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
5171         PetscCheck(ssize == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " from cell %" PetscInt_FMT " has support size %" PetscInt_FMT " != 2", cone[c], cellStart, ssize);
5172         if (support[0] == cellStart) s = 1;
5173         else if (support[1] == cellStart) s = 0;
5174         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
5175         PetscCall(DMGetCellDS(dmScale[c], support[s], &dsScale[c], NULL));
5176         PetscCall(PetscDSGetTotalDimension(dsScale[c], &totDimScale[c]));
5177       }
5178     }
5179   }
5180   /* 2: Setup geometric data */
5181   PetscCall(DMGetCoordinateField(dm, &coordField));
5182   PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
5183   if (maxDegree > 1) {
5184     PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
5185     for (f = 0; f < Nf; ++f) {
5186       PetscFE fe;
5187 
5188       PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
5189       if (fe) {
5190         PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
5191         PetscCall(PetscObjectReference((PetscObject)quads[f]));
5192       }
5193     }
5194   }
5195   /* Loop over chunks */
5196   cellChunkSize = numCells;
5197   numChunks     = !numCells ? 0 : PetscCeilReal(((PetscReal)numCells) / cellChunkSize);
5198   PetscCall(PetscCalloc1(2 * cellChunkSize, &faces));
5199   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 2 * cellChunkSize, faces, PETSC_USE_POINTER, &chunkIS));
5200   /* Extract field coefficients */
5201   /* NOTE This needs the end cap faces to have identical orientations */
5202   PetscCall(DMPlexGetHybridCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
5203   PetscCall(DMPlexGetHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
5204   PetscCall(DMPlexGetHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_TRUE, s));
5205   PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecNeg));
5206   PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecPos));
5207   PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecCoh));
5208   for (chunk = 0; chunk < numChunks; ++chunk) {
5209     PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
5210 
5211     PetscCall(PetscArrayzero(elemVecNeg, cellChunkSize * totDim));
5212     PetscCall(PetscArrayzero(elemVecPos, cellChunkSize * totDim));
5213     PetscCall(PetscArrayzero(elemVecCoh, cellChunkSize * totDim));
5214     /* Get faces */
5215     for (c = cS; c < cE; ++c) {
5216       const PetscInt  cell = cells ? cells[c] : c;
5217       const PetscInt *cone;
5218       PetscCall(DMPlexGetCone(dm, cell, &cone));
5219       faces[(c - cS) * 2 + 0] = cone[0];
5220       faces[(c - cS) * 2 + 1] = cone[1];
5221     }
5222     PetscCall(ISGeneralSetIndices(chunkIS, 2 * cellChunkSize, faces, PETSC_USE_POINTER));
5223     /* Get geometric data */
5224     if (maxDegree <= 1) {
5225       if (!affineQuad) PetscCall(DMFieldCreateDefaultQuadrature(coordField, chunkIS, &affineQuad));
5226       if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, affineQuad, PETSC_TRUE, &affineGeom));
5227     } else {
5228       for (f = 0; f < Nf; ++f) {
5229         if (quads[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, quads[f], PETSC_TRUE, &geoms[f]));
5230       }
5231     }
5232     /* Loop over fields */
5233     for (f = 0; f < Nf; ++f) {
5234       PetscFE         fe;
5235       PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[f];
5236       PetscFEGeom    *chunkGeom = NULL, *remGeom = NULL;
5237       PetscQuadrature quad = affineQuad ? affineQuad : quads[f];
5238       PetscInt        numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb;
5239       PetscBool       isCohesiveField;
5240 
5241       PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
5242       if (!fe) continue;
5243       PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5244       PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
5245       PetscCall(PetscFEGetDimension(fe, &Nb));
5246       blockSize = Nb;
5247       batchSize = numBlocks * blockSize;
5248       PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5249       numChunks = numCells / (numBatches * batchSize);
5250       Ne        = numChunks * numBatches * batchSize;
5251       Nr        = numCells % (numBatches * batchSize);
5252       offset    = numCells - Nr;
5253       PetscCall(PetscFEGeomGetChunk(geom, 0, offset * 2, &chunkGeom));
5254       PetscCall(PetscFEGeomGetChunk(geom, offset * 2, numCells * 2, &remGeom));
5255       PetscCall(PetscDSGetCohesive(ds, f, &isCohesiveField));
5256       chunkGeom->isCohesive = remGeom->isCohesive = PETSC_TRUE;
5257       key[0].field                                = f;
5258       key[1].field                                = f;
5259       key[2].field                                = f;
5260       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, elemVecNeg));
5261       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[0], 0, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[0], &a[0][offset * totDimAux[0]], t, &elemVecNeg[offset * totDim]));
5262       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, elemVecPos));
5263       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[1], 1, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[1], &a[1][offset * totDimAux[1]], t, &elemVecPos[offset * totDim]));
5264       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, elemVecCoh));
5265       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[2], 2, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[2], &a[2][offset * totDimAux[2]], t, &elemVecCoh[offset * totDim]));
5266       PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &remGeom));
5267       PetscCall(PetscFEGeomRestoreChunk(geom, 0, offset, &chunkGeom));
5268     }
5269     /* Add elemVec to locX */
5270     for (c = cS; c < cE; ++c) {
5271       const PetscInt cell = cells ? cells[c] : c;
5272       const PetscInt cind = c - cStart;
5273       PetscInt       i;
5274 
5275       /* Scale element values */
5276       if (locS[0]) {
5277         PetscInt  Nb, off = cind * totDim, soff = cind * totDimScale[0];
5278         PetscBool cohesive;
5279 
5280         for (f = 0; f < Nf; ++f) {
5281           PetscCall(PetscDSGetFieldSize(ds, f, &Nb));
5282           PetscCall(PetscDSGetCohesive(ds, f, &cohesive));
5283           if (f == key[2].field) {
5284             PetscCheck(cohesive, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Scaling should not happen for face fields");
5285             // No cohesive scaling field is currently input
5286             for (i = 0; i < Nb; ++i) elemVecCoh[off + i] += s[0][soff + i] * elemVecNeg[off + i] + s[1][soff + i] * elemVecPos[off + i];
5287             off += Nb;
5288           } else {
5289             const PetscInt N = cohesive ? Nb : Nb * 2;
5290 
5291             for (i = 0; i < N; ++i) elemVecCoh[off + i] += elemVecNeg[off + i] + elemVecPos[off + i];
5292             off += N;
5293           }
5294         }
5295       } else {
5296         for (i = cind * totDim; i < (cind + 1) * totDim; ++i) elemVecCoh[i] += elemVecNeg[i] + elemVecPos[i];
5297       }
5298       if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, name, totDim, &elemVecCoh[cind * totDim]));
5299       if (ghostLabel) {
5300         PetscInt ghostVal;
5301 
5302         PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
5303         if (ghostVal > 0) continue;
5304       }
5305       PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVecCoh[cind * totDim], ADD_ALL_VALUES));
5306     }
5307   }
5308   PetscCall(DMPlexRestoreCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
5309   PetscCall(DMPlexRestoreHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
5310   PetscCall(DMPlexRestoreHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_TRUE, s));
5311   PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecNeg));
5312   PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecPos));
5313   PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecCoh));
5314   PetscCall(PetscFree(faces));
5315   PetscCall(ISDestroy(&chunkIS));
5316   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
5317   if (maxDegree <= 1) {
5318     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
5319     PetscCall(PetscQuadratureDestroy(&affineQuad));
5320   } else {
5321     for (f = 0; f < Nf; ++f) {
5322       if (geoms) PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
5323       if (quads) PetscCall(PetscQuadratureDestroy(&quads[f]));
5324     }
5325     PetscCall(PetscFree2(quads, geoms));
5326   }
5327   if (mesh->printFEM) {
5328     Vec          locFbc;
5329     PetscInt     pStart, pEnd, p, maxDof;
5330     PetscScalar *zeroes;
5331 
5332     PetscCall(VecDuplicate(locF, &locFbc));
5333     PetscCall(VecCopy(locF, locFbc));
5334     PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5335     PetscCall(PetscSectionGetMaxDof(section, &maxDof));
5336     PetscCall(PetscCalloc1(maxDof, &zeroes));
5337     for (p = pStart; p < pEnd; p++) PetscCall(VecSetValuesSection(locFbc, section, p, zeroes, INSERT_BC_VALUES));
5338     PetscCall(PetscFree(zeroes));
5339     PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc));
5340     PetscCall(VecDestroy(&locFbc));
5341   }
5342   PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
5343   PetscFunctionReturn(PETSC_SUCCESS);
5344 }
5345 
5346 static 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)
5347 {
5348   DM_Plex        *mesh = (DM_Plex *)dm->data;
5349   DM              plex = NULL, plexA = NULL, tdm;
5350   DMEnclosureType encAux;
5351   PetscDS         prob, probAux       = NULL;
5352   PetscSection    section, sectionAux = NULL;
5353   PetscSection    globalSection;
5354   Vec             locA = NULL, tv;
5355   PetscScalar    *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL;
5356   PetscInt        v;
5357   PetscInt        Nf, totDim, totDimAux = 0;
5358   PetscBool       transform;
5359 
5360   PetscFunctionBegin;
5361   PetscCall(DMConvert(dm, DMPLEX, &plex));
5362   PetscCall(DMHasBasisTransform(dm, &transform));
5363   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
5364   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
5365   PetscCall(DMGetLocalSection(dm, &section));
5366   PetscCall(DMGetDS(dm, &prob));
5367   PetscCall(PetscDSGetNumFields(prob, &Nf));
5368   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
5369   PetscCall(DMGetAuxiliaryVec(dm, label, values[0], 0, &locA));
5370   if (locA) {
5371     DM dmAux;
5372 
5373     PetscCall(VecGetDM(locA, &dmAux));
5374     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
5375     PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
5376     PetscCall(DMGetDS(plexA, &probAux));
5377     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
5378     PetscCall(DMGetLocalSection(plexA, &sectionAux));
5379   }
5380 
5381   PetscCall(DMGetGlobalSection(dm, &globalSection));
5382   for (v = 0; v < numValues; ++v) {
5383     PetscFEGeom    *fgeom;
5384     PetscInt        maxDegree;
5385     PetscQuadrature qGeom = NULL;
5386     IS              pointIS;
5387     const PetscInt *points;
5388     PetscFormKey    key;
5389     PetscInt        numFaces, face, Nq;
5390 
5391     key.label = label;
5392     key.value = values[v];
5393     key.part  = 0;
5394     PetscCall(DMLabelGetStratumIS(label, values[v], &pointIS));
5395     if (!pointIS) continue; /* No points with that id on this process */
5396     {
5397       IS isectIS;
5398 
5399       /* TODO: Special cases of ISIntersect where it is quick to check a prior if one is a superset of the other */
5400       PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS));
5401       PetscCall(ISDestroy(&pointIS));
5402       pointIS = isectIS;
5403     }
5404     PetscCall(ISGetLocalSize(pointIS, &numFaces));
5405     PetscCall(ISGetIndices(pointIS, &points));
5406     PetscCall(PetscMalloc4(numFaces * totDim, &u, locX_t ? numFaces * totDim : 0, &u_t, numFaces * totDim * totDim, &elemMat, locA ? numFaces * totDimAux : 0, &a));
5407     PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
5408     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom));
5409     if (!qGeom) {
5410       PetscFE fe;
5411 
5412       PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
5413       PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
5414       PetscCall(PetscObjectReference((PetscObject)qGeom));
5415     }
5416     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
5417     PetscCall(DMSNESGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
5418     for (face = 0; face < numFaces; ++face) {
5419       const PetscInt point = points[face], *support;
5420       PetscScalar   *x     = NULL;
5421       PetscInt       i;
5422 
5423       PetscCall(DMPlexGetSupport(dm, point, &support));
5424       PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
5425       for (i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
5426       PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
5427       if (locX_t) {
5428         PetscCall(DMPlexVecGetClosure(plex, section, locX_t, support[0], NULL, &x));
5429         for (i = 0; i < totDim; ++i) u_t[face * totDim + i] = x[i];
5430         PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, support[0], NULL, &x));
5431       }
5432       if (locA) {
5433         PetscInt subp;
5434         PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
5435         PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
5436         for (i = 0; i < totDimAux; ++i) a[face * totDimAux + i] = x[i];
5437         PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
5438       }
5439     }
5440     PetscCall(PetscArrayzero(elemMat, numFaces * totDim * totDim));
5441     {
5442       PetscFE  fe;
5443       PetscInt Nb;
5444       /* Conforming batches */
5445       PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
5446       /* Remainder */
5447       PetscFEGeom *chunkGeom = NULL;
5448       PetscInt     fieldJ, Nr, offset;
5449 
5450       PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
5451       PetscCall(PetscFEGetDimension(fe, &Nb));
5452       PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5453       blockSize = Nb;
5454       batchSize = numBlocks * blockSize;
5455       PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5456       numChunks = numFaces / (numBatches * batchSize);
5457       Ne        = numChunks * numBatches * batchSize;
5458       Nr        = numFaces % (numBatches * batchSize);
5459       offset    = numFaces - Nr;
5460       PetscCall(PetscFEGeomGetChunk(fgeom, 0, offset, &chunkGeom));
5461       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5462         key.field = fieldI * Nf + fieldJ;
5463         PetscCall(PetscFEIntegrateBdJacobian(prob, wf, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat));
5464       }
5465       PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom));
5466       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5467         key.field = fieldI * Nf + fieldJ;
5468         PetscCall(PetscFEIntegrateBdJacobian(prob, wf, key, Nr, chunkGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, a ? &a[offset * totDimAux] : NULL, t, X_tShift, &elemMat[offset * totDim * totDim]));
5469       }
5470       PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom));
5471     }
5472     for (face = 0; face < numFaces; ++face) {
5473       const PetscInt point = points[face], *support;
5474 
5475       /* Transform to global basis before insertion in Jacobian */
5476       PetscCall(DMPlexGetSupport(plex, point, &support));
5477       if (transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, support[0], PETSC_TRUE, totDim, &elemMat[face * totDim * totDim]));
5478       if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(point, "BdJacobian", totDim, totDim, &elemMat[face * totDim * totDim]));
5479       PetscCall(DMPlexMatSetClosure(plex, section, globalSection, JacP, support[0], &elemMat[face * totDim * totDim], ADD_VALUES));
5480     }
5481     PetscCall(DMSNESRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
5482     PetscCall(PetscQuadratureDestroy(&qGeom));
5483     PetscCall(ISRestoreIndices(pointIS, &points));
5484     PetscCall(ISDestroy(&pointIS));
5485     PetscCall(PetscFree4(u, u_t, elemMat, a));
5486   }
5487   if (plex) PetscCall(DMDestroy(&plex));
5488   if (plexA) PetscCall(DMDestroy(&plexA));
5489   PetscFunctionReturn(PETSC_SUCCESS);
5490 }
5491 
5492 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)
5493 {
5494   DMField  coordField;
5495   DMLabel  depthLabel;
5496   IS       facetIS;
5497   PetscInt dim;
5498 
5499   PetscFunctionBegin;
5500   PetscCall(DMGetDimension(dm, &dim));
5501   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5502   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
5503   PetscCall(DMGetCoordinateField(dm, &coordField));
5504   PetscCall(DMPlexComputeBdJacobian_Single_Internal(dm, t, wf, label, numValues, values, field, locX, locX_t, X_tShift, Jac, JacP, coordField, facetIS));
5505   PetscCall(ISDestroy(&facetIS));
5506   PetscFunctionReturn(PETSC_SUCCESS);
5507 }
5508 
5509 static PetscErrorCode DMPlexComputeBdJacobian_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, PetscReal X_tShift, Mat Jac, Mat JacP, void *user)
5510 {
5511   PetscDS  prob;
5512   PetscInt dim, numBd, bd;
5513   DMLabel  depthLabel;
5514   DMField  coordField = NULL;
5515   IS       facetIS;
5516 
5517   PetscFunctionBegin;
5518   PetscCall(DMGetDS(dm, &prob));
5519   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5520   PetscCall(DMGetDimension(dm, &dim));
5521   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
5522   PetscCall(PetscDSGetNumBoundary(prob, &numBd));
5523   PetscCall(DMGetCoordinateField(dm, &coordField));
5524   for (bd = 0; bd < numBd; ++bd) {
5525     PetscWeakForm           wf;
5526     DMBoundaryConditionType type;
5527     DMLabel                 label;
5528     const PetscInt         *values;
5529     PetscInt                fieldI, numValues;
5530     PetscObject             obj;
5531     PetscClassId            id;
5532 
5533     PetscCall(PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &fieldI, NULL, NULL, NULL, NULL, NULL));
5534     if (type & DM_BC_ESSENTIAL) continue;
5535     PetscCall(PetscDSGetDiscretization(prob, fieldI, &obj));
5536     PetscCall(PetscObjectGetClassId(obj, &id));
5537     if (id != PETSCFE_CLASSID) continue;
5538     PetscCall(DMPlexComputeBdJacobian_Single_Internal(dm, t, wf, label, numValues, values, fieldI, locX, locX_t, X_tShift, Jac, JacP, coordField, facetIS));
5539   }
5540   PetscCall(ISDestroy(&facetIS));
5541   PetscFunctionReturn(PETSC_SUCCESS);
5542 }
5543 
5544 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)
5545 {
5546   DM_Plex        *mesh  = (DM_Plex *)dm->data;
5547   const char     *name  = "Jacobian";
5548   DM              dmAux = NULL, plex, tdm;
5549   DMEnclosureType encAux;
5550   Vec             A, tv;
5551   DMField         coordField;
5552   PetscDS         prob, probAux = NULL;
5553   PetscSection    section, globalSection, sectionAux;
5554   PetscScalar    *elemMat, *elemMatP, *elemMatD, *u, *u_t, *a = NULL;
5555   const PetscInt *cells;
5556   PetscInt        Nf, fieldI, fieldJ;
5557   PetscInt        totDim, totDimAux = 0, cStart, cEnd, numCells, c;
5558   PetscBool       hasJac = PETSC_FALSE, hasPrec = PETSC_FALSE, hasDyn, hasFV = PETSC_FALSE, transform;
5559 
5560   PetscFunctionBegin;
5561   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
5562   if (!cellIS) goto end;
5563   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
5564   PetscCall(ISGetLocalSize(cellIS, &numCells));
5565   if (cStart >= cEnd) goto end;
5566   PetscCall(DMHasBasisTransform(dm, &transform));
5567   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
5568   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
5569   PetscCall(DMGetLocalSection(dm, &section));
5570   PetscCall(DMGetGlobalSection(dm, &globalSection));
5571   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL));
5572   PetscCall(PetscDSGetNumFields(prob, &Nf));
5573   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
5574   PetscCall(PetscDSHasJacobian(prob, &hasJac));
5575   PetscCall(PetscDSHasJacobianPreconditioner(prob, &hasPrec));
5576   /* user passed in the same matrix, avoid double contributions and
5577      only assemble the Jacobian */
5578   if (hasJac && Jac == JacP) hasPrec = PETSC_FALSE;
5579   PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
5580   hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
5581   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &A));
5582   if (A) {
5583     PetscCall(VecGetDM(A, &dmAux));
5584     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
5585     PetscCall(DMConvert(dmAux, DMPLEX, &plex));
5586     PetscCall(DMGetLocalSection(plex, &sectionAux));
5587     PetscCall(DMGetDS(dmAux, &probAux));
5588     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
5589   }
5590   PetscCall(PetscMalloc5(numCells * totDim, &u, X_t ? numCells * totDim : 0, &u_t, hasJac ? numCells * totDim * totDim : 0, &elemMat, hasPrec ? numCells * totDim * totDim : 0, &elemMatP, hasDyn ? numCells * totDim * totDim : 0, &elemMatD));
5591   if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
5592   PetscCall(DMGetCoordinateField(dm, &coordField));
5593   for (c = cStart; c < cEnd; ++c) {
5594     const PetscInt cell = cells ? cells[c] : c;
5595     const PetscInt cind = c - cStart;
5596     PetscScalar   *x = NULL, *x_t = NULL;
5597     PetscInt       i;
5598 
5599     PetscCall(DMPlexVecGetClosure(dm, section, X, cell, NULL, &x));
5600     for (i = 0; i < totDim; ++i) u[cind * totDim + i] = x[i];
5601     PetscCall(DMPlexVecRestoreClosure(dm, section, X, cell, NULL, &x));
5602     if (X_t) {
5603       PetscCall(DMPlexVecGetClosure(dm, section, X_t, cell, NULL, &x_t));
5604       for (i = 0; i < totDim; ++i) u_t[cind * totDim + i] = x_t[i];
5605       PetscCall(DMPlexVecRestoreClosure(dm, section, X_t, cell, NULL, &x_t));
5606     }
5607     if (dmAux) {
5608       PetscInt subcell;
5609       PetscCall(DMGetEnclosurePoint(dmAux, dm, encAux, cell, &subcell));
5610       PetscCall(DMPlexVecGetClosure(plex, sectionAux, A, subcell, NULL, &x));
5611       for (i = 0; i < totDimAux; ++i) a[cind * totDimAux + i] = x[i];
5612       PetscCall(DMPlexVecRestoreClosure(plex, sectionAux, A, subcell, NULL, &x));
5613     }
5614   }
5615   if (hasJac) PetscCall(PetscArrayzero(elemMat, numCells * totDim * totDim));
5616   if (hasPrec) PetscCall(PetscArrayzero(elemMatP, numCells * totDim * totDim));
5617   if (hasDyn) PetscCall(PetscArrayzero(elemMatD, numCells * totDim * totDim));
5618   for (fieldI = 0; fieldI < Nf; ++fieldI) {
5619     PetscClassId    id;
5620     PetscFE         fe;
5621     PetscQuadrature qGeom = NULL;
5622     PetscInt        Nb;
5623     /* Conforming batches */
5624     PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
5625     /* Remainder */
5626     PetscInt     Nr, offset, Nq;
5627     PetscInt     maxDegree;
5628     PetscFEGeom *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL;
5629 
5630     PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
5631     PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
5632     if (id == PETSCFV_CLASSID) {
5633       hasFV = PETSC_TRUE;
5634       continue;
5635     }
5636     PetscCall(PetscFEGetDimension(fe, &Nb));
5637     PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5638     PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
5639     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
5640     if (!qGeom) {
5641       PetscCall(PetscFEGetQuadrature(fe, &qGeom));
5642       PetscCall(PetscObjectReference((PetscObject)qGeom));
5643     }
5644     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
5645     PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
5646     blockSize = Nb;
5647     batchSize = numBlocks * blockSize;
5648     PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5649     numChunks = numCells / (numBatches * batchSize);
5650     Ne        = numChunks * numBatches * batchSize;
5651     Nr        = numCells % (numBatches * batchSize);
5652     offset    = numCells - Nr;
5653     PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
5654     PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &remGeom));
5655     for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5656       key.field = fieldI * Nf + fieldJ;
5657       if (hasJac) {
5658         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat));
5659         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Nr, remGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, a ? &a[offset * totDimAux] : NULL, t, X_tShift, &elemMat[offset * totDim * totDim]));
5660       }
5661       if (hasPrec) {
5662         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatP));
5663         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, key, Nr, remGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, a ? &a[offset * totDimAux] : NULL, t, X_tShift, &elemMatP[offset * totDim * totDim]));
5664       }
5665       if (hasDyn) {
5666         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatD));
5667         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Nr, remGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, a ? &a[offset * totDimAux] : NULL, t, X_tShift, &elemMatD[offset * totDim * totDim]));
5668       }
5669     }
5670     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &remGeom));
5671     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, 0, offset, &chunkGeom));
5672     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
5673     PetscCall(PetscQuadratureDestroy(&qGeom));
5674   }
5675   /*   Add contribution from X_t */
5676   if (hasDyn) {
5677     for (c = 0; c < numCells * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
5678   }
5679   if (hasFV) {
5680     PetscClassId id;
5681     PetscFV      fv;
5682     PetscInt     offsetI, NcI, NbI = 1, fc, f;
5683 
5684     for (fieldI = 0; fieldI < Nf; ++fieldI) {
5685       PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fv));
5686       PetscCall(PetscDSGetFieldOffset(prob, fieldI, &offsetI));
5687       PetscCall(PetscObjectGetClassId((PetscObject)fv, &id));
5688       if (id != PETSCFV_CLASSID) continue;
5689       /* Put in the identity */
5690       PetscCall(PetscFVGetNumComponents(fv, &NcI));
5691       for (c = cStart; c < cEnd; ++c) {
5692         const PetscInt cind    = c - cStart;
5693         const PetscInt eOffset = cind * totDim * totDim;
5694         for (fc = 0; fc < NcI; ++fc) {
5695           for (f = 0; f < NbI; ++f) {
5696             const PetscInt i = offsetI + f * NcI + fc;
5697             if (hasPrec) {
5698               if (hasJac) elemMat[eOffset + i * totDim + i] = 1.0;
5699               elemMatP[eOffset + i * totDim + i] = 1.0;
5700             } else {
5701               elemMat[eOffset + i * totDim + i] = 1.0;
5702             }
5703           }
5704         }
5705       }
5706     }
5707     /* No allocated space for FV stuff, so ignore the zero entries */
5708     PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE));
5709   }
5710   /* Insert values into matrix */
5711   for (c = cStart; c < cEnd; ++c) {
5712     const PetscInt cell = cells ? cells[c] : c;
5713     const PetscInt cind = c - cStart;
5714 
5715     /* Transform to global basis before insertion in Jacobian */
5716     if (transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, cell, PETSC_TRUE, totDim, &elemMat[cind * totDim * totDim]));
5717     if (hasPrec) {
5718       if (hasJac) {
5719         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
5720         PetscCall(DMPlexMatSetClosure(dm, section, globalSection, Jac, cell, &elemMat[cind * totDim * totDim], ADD_VALUES));
5721       }
5722       if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatP[cind * totDim * totDim]));
5723       PetscCall(DMPlexMatSetClosure(dm, section, globalSection, JacP, cell, &elemMatP[cind * totDim * totDim], ADD_VALUES));
5724     } else {
5725       if (hasJac) {
5726         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
5727         PetscCall(DMPlexMatSetClosure(dm, section, globalSection, JacP, cell, &elemMat[cind * totDim * totDim], ADD_VALUES));
5728       }
5729     }
5730   }
5731   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
5732   if (hasFV) PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE));
5733   PetscCall(PetscFree5(u, u_t, elemMat, elemMatP, elemMatD));
5734   if (dmAux) {
5735     PetscCall(PetscFree(a));
5736     PetscCall(DMDestroy(&plex));
5737   }
5738   /* Compute boundary integrals */
5739   PetscCall(DMPlexComputeBdJacobian_Internal(dm, X, X_t, t, X_tShift, Jac, JacP, user));
5740   /* Assemble matrix */
5741 end : {
5742   PetscBool assOp = hasJac && hasPrec ? PETSC_TRUE : PETSC_FALSE, gassOp;
5743 
5744   PetscCall(MPIU_Allreduce(&assOp, &gassOp, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
5745   if (hasJac && hasPrec) {
5746     PetscCall(MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY));
5747     PetscCall(MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY));
5748   }
5749 }
5750   PetscCall(MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY));
5751   PetscCall(MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY));
5752   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
5753   PetscFunctionReturn(PETSC_SUCCESS);
5754 }
5755 
5756 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)
5757 {
5758   DM_Plex        *mesh          = (DM_Plex *)dm->data;
5759   const char     *name          = "Hybrid Jacobian";
5760   DM              dmAux[3]      = {NULL, NULL, NULL};
5761   DMLabel         ghostLabel    = NULL;
5762   DM              plex          = NULL;
5763   DM              plexA         = NULL;
5764   PetscDS         ds            = NULL;
5765   PetscDS         dsIn          = NULL;
5766   PetscDS         dsAux[3]      = {NULL, NULL, NULL};
5767   Vec             locA[3]       = {NULL, NULL, NULL};
5768   DM              dmScale[3]    = {NULL, NULL, NULL};
5769   PetscDS         dsScale[3]    = {NULL, NULL, NULL};
5770   Vec             locS[3]       = {NULL, NULL, NULL};
5771   PetscSection    section       = NULL;
5772   PetscSection    sectionAux[3] = {NULL, NULL, NULL};
5773   DMField         coordField    = NULL;
5774   PetscScalar    *a[3]          = {NULL, NULL, NULL};
5775   PetscScalar    *s[3]          = {NULL, NULL, NULL};
5776   PetscScalar    *u             = NULL, *u_t;
5777   PetscScalar    *elemMatNeg, *elemMatPos, *elemMatCoh;
5778   PetscScalar    *elemMatNegP, *elemMatPosP, *elemMatCohP;
5779   PetscSection    globalSection;
5780   IS              chunkIS;
5781   const PetscInt *cells;
5782   PetscInt       *faces;
5783   PetscInt        cStart, cEnd, numCells;
5784   PetscInt        Nf, fieldI, fieldJ, totDim, totDimIn, totDimAux[3], totDimScale[3], numChunks, cellChunkSize, chunk;
5785   PetscInt        maxDegree  = PETSC_MAX_INT;
5786   PetscQuadrature affineQuad = NULL, *quads = NULL;
5787   PetscFEGeom    *affineGeom = NULL, **geoms = NULL;
5788   PetscBool       hasBdJac, hasBdPrec;
5789 
5790   PetscFunctionBegin;
5791   if (!cellIS) PetscFunctionReturn(PETSC_SUCCESS);
5792   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
5793   PetscCall(ISGetLocalSize(cellIS, &numCells));
5794   if (cStart >= cEnd) PetscFunctionReturn(PETSC_SUCCESS);
5795   if ((key[0].label == key[1].label) && (key[0].value == key[1].value) && (key[0].part == key[1].part)) {
5796     const char *name;
5797     PetscCall(PetscObjectGetName((PetscObject)key[0].label, &name));
5798     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);
5799   }
5800   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
5801   PetscCall(DMConvert(dm, DMPLEX, &plex));
5802   PetscCall(DMGetSection(dm, &section));
5803   PetscCall(DMGetGlobalSection(dm, &globalSection));
5804   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
5805   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn));
5806   PetscCall(PetscDSGetNumFields(ds, &Nf));
5807   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
5808   PetscCall(PetscDSGetTotalDimension(dsIn, &totDimIn));
5809   PetscCall(PetscDSHasBdJacobian(ds, &hasBdJac));
5810   PetscCall(PetscDSHasBdJacobianPreconditioner(ds, &hasBdPrec));
5811   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, key[2].value, key[2].part, &locA[2]));
5812   if (locA[2]) {
5813     const PetscInt cellStart = cells ? cells[cStart] : cStart;
5814 
5815     PetscCall(VecGetDM(locA[2], &dmAux[2]));
5816     PetscCall(DMConvert(dmAux[2], DMPLEX, &plexA));
5817     PetscCall(DMGetSection(dmAux[2], &sectionAux[2]));
5818     PetscCall(DMGetCellDS(dmAux[2], cellStart, &dsAux[2], NULL));
5819     PetscCall(PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]));
5820     {
5821       const PetscInt *cone;
5822       PetscInt        c;
5823 
5824       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
5825       for (c = 0; c < 2; ++c) {
5826         const PetscInt *support;
5827         PetscInt        ssize, s;
5828 
5829         PetscCall(DMPlexGetSupport(dm, cone[c], &support));
5830         PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
5831         PetscCheck(ssize == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " from cell %" PetscInt_FMT " has support size %" PetscInt_FMT " != 2", cone[c], cellStart, ssize);
5832         if (support[0] == cellStart) s = 1;
5833         else if (support[1] == cellStart) s = 0;
5834         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
5835         PetscCall(DMGetAuxiliaryVec(dm, key[c].label, key[c].value, key[c].part, &locA[c]));
5836         if (locA[c]) PetscCall(VecGetDM(locA[c], &dmAux[c]));
5837         else dmAux[c] = dmAux[2];
5838         PetscCall(DMGetCellDS(dmAux[c], support[s], &dsAux[c], NULL));
5839         PetscCall(PetscDSGetTotalDimension(dsAux[c], &totDimAux[c]));
5840       }
5841     }
5842   }
5843   /* Handle mass matrix scaling
5844        The field in key[2] is the field to be scaled, and the scaling field is the first in the dsScale */
5845   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, -key[2].value, key[2].part, &locS[2]));
5846   if (locS[2]) {
5847     const PetscInt cellStart = cells ? cells[cStart] : cStart;
5848     PetscInt       Nb, Nbs;
5849 
5850     PetscCall(VecGetDM(locS[2], &dmScale[2]));
5851     PetscCall(DMGetCellDS(dmScale[2], cells ? cells[cStart] : cStart, &dsScale[2], NULL));
5852     PetscCall(PetscDSGetTotalDimension(dsScale[2], &totDimScale[2]));
5853     // BRAD: This is not set correctly
5854     key[2].field = 2;
5855     PetscCall(PetscDSGetFieldSize(ds, key[2].field, &Nb));
5856     PetscCall(PetscDSGetFieldSize(dsScale[2], 0, &Nbs));
5857     PetscCheck(Nb == Nbs, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Field %" PetscInt_FMT " of size %" PetscInt_FMT " cannot be scaled by field of size %" PetscInt_FMT, key[2].field, Nb, Nbs);
5858     {
5859       const PetscInt *cone;
5860       PetscInt        c;
5861 
5862       locS[1] = locS[0] = locS[2];
5863       dmScale[1] = dmScale[0] = dmScale[2];
5864       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
5865       for (c = 0; c < 2; ++c) {
5866         const PetscInt *support;
5867         PetscInt        ssize, s;
5868 
5869         PetscCall(DMPlexGetSupport(dm, cone[c], &support));
5870         PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
5871         PetscCheck(ssize == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " from cell %" PetscInt_FMT " has support size %" PetscInt_FMT " != 2", cone[c], cellStart, ssize);
5872         if (support[0] == cellStart) s = 1;
5873         else if (support[1] == cellStart) s = 0;
5874         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
5875         PetscCall(DMGetCellDS(dmScale[c], support[s], &dsScale[c], NULL));
5876         PetscCall(PetscDSGetTotalDimension(dsScale[c], &totDimScale[c]));
5877       }
5878     }
5879   }
5880   /* 2: Setup geometric data */
5881   PetscCall(DMGetCoordinateField(dm, &coordField));
5882   PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
5883   if (maxDegree > 1) {
5884     PetscInt f;
5885     PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
5886     for (f = 0; f < Nf; ++f) {
5887       PetscFE fe;
5888 
5889       PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
5890       if (fe) {
5891         PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
5892         PetscCall(PetscObjectReference((PetscObject)quads[f]));
5893       }
5894     }
5895   }
5896   /* Loop over chunks */
5897   cellChunkSize = numCells;
5898   numChunks     = !numCells ? 0 : PetscCeilReal(((PetscReal)numCells) / cellChunkSize);
5899   PetscCall(PetscCalloc1(2 * cellChunkSize, &faces));
5900   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 1 * cellChunkSize, faces, PETSC_USE_POINTER, &chunkIS));
5901   /* Extract field coefficients */
5902   /* NOTE This needs the end cap faces to have identical orientations */
5903   PetscCall(DMPlexGetHybridCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
5904   PetscCall(DMPlexGetHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
5905   PetscCall(DMPlexGetHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_TRUE, s));
5906   PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNeg));
5907   PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPos));
5908   PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCoh));
5909   PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNegP));
5910   PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPosP));
5911   PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCohP));
5912   for (chunk = 0; chunk < numChunks; ++chunk) {
5913     PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
5914 
5915     if (hasBdJac) {
5916       PetscCall(PetscArrayzero(elemMatNeg, cellChunkSize * totDim * totDim));
5917       PetscCall(PetscArrayzero(elemMatPos, cellChunkSize * totDim * totDim));
5918       PetscCall(PetscArrayzero(elemMatCoh, cellChunkSize * totDim * totDim));
5919     }
5920     if (hasBdPrec) {
5921       PetscCall(PetscArrayzero(elemMatNegP, cellChunkSize * totDim * totDim));
5922       PetscCall(PetscArrayzero(elemMatPosP, cellChunkSize * totDim * totDim));
5923       PetscCall(PetscArrayzero(elemMatCohP, cellChunkSize * totDim * totDim));
5924     }
5925     /* Get faces */
5926     for (c = cS; c < cE; ++c) {
5927       const PetscInt  cell = cells ? cells[c] : c;
5928       const PetscInt *cone;
5929       PetscCall(DMPlexGetCone(plex, cell, &cone));
5930       faces[(c - cS) * 2 + 0] = cone[0];
5931       faces[(c - cS) * 2 + 1] = cone[1];
5932     }
5933     PetscCall(ISGeneralSetIndices(chunkIS, 2 * cellChunkSize, faces, PETSC_USE_POINTER));
5934     if (maxDegree <= 1) {
5935       if (!affineQuad) PetscCall(DMFieldCreateDefaultQuadrature(coordField, chunkIS, &affineQuad));
5936       if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, affineQuad, PETSC_TRUE, &affineGeom));
5937     } else {
5938       PetscInt f;
5939       for (f = 0; f < Nf; ++f) {
5940         if (quads[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, quads[f], PETSC_TRUE, &geoms[f]));
5941       }
5942     }
5943 
5944     for (fieldI = 0; fieldI < Nf; ++fieldI) {
5945       PetscFE         feI;
5946       PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[fieldI];
5947       PetscFEGeom    *chunkGeom = NULL, *remGeom = NULL;
5948       PetscQuadrature quad = affineQuad ? affineQuad : quads[fieldI];
5949       PetscInt        numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb;
5950       PetscBool       isCohesiveField;
5951 
5952       PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&feI));
5953       if (!feI) continue;
5954       PetscCall(PetscFEGetTileSizes(feI, NULL, &numBlocks, NULL, &numBatches));
5955       PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
5956       PetscCall(PetscFEGetDimension(feI, &Nb));
5957       blockSize = Nb;
5958       batchSize = numBlocks * blockSize;
5959       PetscCall(PetscFESetTileSizes(feI, blockSize, numBlocks, batchSize, numBatches));
5960       numChunks = numCells / (numBatches * batchSize);
5961       Ne        = numChunks * numBatches * batchSize;
5962       Nr        = numCells % (numBatches * batchSize);
5963       offset    = numCells - Nr;
5964       PetscCall(PetscFEGeomGetChunk(geom, 0, offset * 2, &chunkGeom));
5965       PetscCall(PetscFEGeomGetChunk(geom, offset * 2, numCells * 2, &remGeom));
5966       PetscCall(PetscDSGetCohesive(ds, fieldI, &isCohesiveField));
5967       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5968         PetscFE feJ;
5969 
5970         PetscCall(PetscDSGetDiscretization(ds, fieldJ, (PetscObject *)&feJ));
5971         if (!feJ) continue;
5972         key[0].field = fieldI * Nf + fieldJ;
5973         key[1].field = fieldI * Nf + fieldJ;
5974         key[2].field = fieldI * Nf + fieldJ;
5975         if (hasBdJac) {
5976           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, X_tShift, elemMatNeg));
5977           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[0], 0, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[0], &a[0][offset * totDimAux[0]], t, X_tShift, &elemMatNeg[offset * totDim * totDim]));
5978           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, X_tShift, elemMatPos));
5979           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[1], 1, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[1], &a[1][offset * totDimAux[1]], t, X_tShift, &elemMatPos[offset * totDim * totDim]));
5980         }
5981         if (hasBdPrec) {
5982           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, X_tShift, elemMatNegP));
5983           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[0], 0, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[0], &a[0][offset * totDimAux[0]], t, X_tShift, &elemMatNegP[offset * totDim * totDim]));
5984           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, X_tShift, elemMatPosP));
5985           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[1], 1, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[1], &a[1][offset * totDimAux[1]], t, X_tShift, &elemMatPosP[offset * totDim * totDim]));
5986         }
5987         if (hasBdJac) {
5988           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, X_tShift, elemMatCoh));
5989           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[2], 2, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[2], &a[2][offset * totDimAux[2]], t, X_tShift, &elemMatCoh[offset * totDim * totDim]));
5990         }
5991         if (hasBdPrec) {
5992           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, X_tShift, elemMatCohP));
5993           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[2], 2, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[2], &a[2][offset * totDimAux[2]], t, X_tShift, &elemMatCohP[offset * totDim * totDim]));
5994         }
5995       }
5996       PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &remGeom));
5997       PetscCall(PetscFEGeomRestoreChunk(geom, 0, offset, &chunkGeom));
5998     }
5999     /* Insert values into matrix */
6000     for (c = cS; c < cE; ++c) {
6001       const PetscInt cell = cells ? cells[c] : c;
6002       const PetscInt cind = c - cS, coff = cind * totDim * totDim;
6003       PetscInt       i, j;
6004 
6005       /* Scale element values */
6006       if (locS[0]) {
6007         PetscInt  Nb, soff = cind * totDimScale[0], off = 0;
6008         PetscBool cohesive;
6009 
6010         for (fieldI = 0; fieldI < Nf; ++fieldI) {
6011           PetscCall(PetscDSGetFieldSize(ds, fieldI, &Nb));
6012           PetscCall(PetscDSGetCohesive(ds, fieldI, &cohesive));
6013 
6014           if (fieldI == key[2].field) {
6015             PetscCheck(cohesive, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Scaling should not happen for face fields");
6016             for (i = 0; i < Nb; ++i) {
6017               for (j = 0; j < totDim; ++j) elemMatCoh[coff + (off + i) * totDim + j] += s[0][soff + i] * elemMatNeg[coff + (off + i) * totDim + j] + s[1][soff + i] * elemMatPos[coff + (off + i) * totDim + j];
6018               if (hasBdPrec)
6019                 for (j = 0; j < totDim; ++j) elemMatCohP[coff + (off + i) * totDim + j] += s[0][soff + i] * elemMatNegP[coff + (off + i) * totDim + j] + s[1][soff + i] * elemMatPosP[coff + (off + i) * totDim + j];
6020             }
6021             off += Nb;
6022           } else {
6023             const PetscInt N = cohesive ? Nb : Nb * 2;
6024 
6025             for (i = 0; i < N; ++i) {
6026               for (j = 0; j < totDim; ++j) elemMatCoh[coff + (off + i) * totDim + j] += elemMatNeg[coff + (off + i) * totDim + j] + elemMatPos[coff + (off + i) * totDim + j];
6027               if (hasBdPrec)
6028                 for (j = 0; j < totDim; ++j) elemMatCohP[coff + (off + i) * totDim + j] += elemMatNegP[coff + (off + i) * totDim + j] + elemMatPosP[coff + (off + i) * totDim + j];
6029             }
6030             off += N;
6031           }
6032         }
6033       } else {
6034         for (i = 0; i < totDim * totDim; ++i) elemMatCoh[coff + i] += elemMatNeg[coff + i] + elemMatPos[coff + i];
6035         if (hasBdPrec)
6036           for (i = 0; i < totDim * totDim; ++i) elemMatCohP[coff + i] += elemMatNegP[coff + i] + elemMatPosP[coff + i];
6037       }
6038       if (hasBdPrec) {
6039         if (hasBdJac) {
6040           if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCoh[cind * totDim * totDim]));
6041           PetscCall(DMPlexMatSetClosure(plex, section, globalSection, Jac, cell, &elemMatCoh[cind * totDim * totDim], ADD_VALUES));
6042         }
6043         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCohP[cind * totDim * totDim]));
6044         PetscCall(DMPlexMatSetClosure(plex, section, globalSection, JacP, cell, &elemMatCohP[cind * totDim * totDim], ADD_VALUES));
6045       } else if (hasBdJac) {
6046         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCoh[cind * totDim * totDim]));
6047         PetscCall(DMPlexMatSetClosure(plex, section, globalSection, JacP, cell, &elemMatCoh[cind * totDim * totDim], ADD_VALUES));
6048       }
6049     }
6050   }
6051   PetscCall(DMPlexRestoreCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
6052   PetscCall(DMPlexRestoreHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
6053   PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNeg));
6054   PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPos));
6055   PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCoh));
6056   PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNegP));
6057   PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPosP));
6058   PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCohP));
6059   PetscCall(PetscFree(faces));
6060   PetscCall(ISDestroy(&chunkIS));
6061   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
6062   if (maxDegree <= 1) {
6063     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
6064     PetscCall(PetscQuadratureDestroy(&affineQuad));
6065   } else {
6066     PetscInt f;
6067     for (f = 0; f < Nf; ++f) {
6068       if (geoms) PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
6069       if (quads) PetscCall(PetscQuadratureDestroy(&quads[f]));
6070     }
6071     PetscCall(PetscFree2(quads, geoms));
6072   }
6073   if (dmAux[2]) PetscCall(DMDestroy(&plexA));
6074   PetscCall(DMDestroy(&plex));
6075   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
6076   PetscFunctionReturn(PETSC_SUCCESS);
6077 }
6078 
6079 /*
6080   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.
6081 
6082   Input Parameters:
6083 + dm     - The mesh
6084 . key    - The PetscWeakFormKey indcating where integration should happen
6085 . cellIS - The cells to integrate over
6086 . t      - The time
6087 . X_tShift - The multiplier for the Jacobian with respect to X_t
6088 . X      - Local solution vector
6089 . X_t    - Time-derivative of the local solution vector
6090 . Y      - Local input vector
6091 - user   - the user context
6092 
6093   Output Parameter:
6094 . Z - Local output vector
6095 
6096   Note:
6097   We form the residual one batch of elements at a time. This allows us to offload work onto an accelerator,
6098   like a GPU, or vectorize on a multicore machine.
6099 */
6100 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)
6101 {
6102   DM_Plex        *mesh  = (DM_Plex *)dm->data;
6103   const char     *name  = "Jacobian";
6104   DM              dmAux = NULL, plex, plexAux = NULL;
6105   DMEnclosureType encAux;
6106   Vec             A;
6107   DMField         coordField;
6108   PetscDS         prob, probAux = NULL;
6109   PetscQuadrature quad;
6110   PetscSection    section, globalSection, sectionAux;
6111   PetscScalar    *elemMat, *elemMatD, *u, *u_t, *a = NULL, *y, *z;
6112   const PetscInt *cells;
6113   PetscInt        Nf, fieldI, fieldJ;
6114   PetscInt        totDim, totDimAux = 0, cStart, cEnd, numCells, c;
6115   PetscBool       hasDyn;
6116 
6117   PetscFunctionBegin;
6118   if (!cellIS) PetscFunctionReturn(PETSC_SUCCESS);
6119   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
6120   PetscCall(DMConvert(dm, DMPLEX, &plex));
6121   PetscCall(ISGetLocalSize(cellIS, &numCells));
6122   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
6123   PetscCall(DMGetLocalSection(dm, &section));
6124   PetscCall(DMGetGlobalSection(dm, &globalSection));
6125   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL));
6126   PetscCall(PetscDSGetNumFields(prob, &Nf));
6127   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
6128   PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
6129   hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
6130   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &A));
6131   if (A) {
6132     PetscCall(VecGetDM(A, &dmAux));
6133     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
6134     PetscCall(DMConvert(dmAux, DMPLEX, &plexAux));
6135     PetscCall(DMGetLocalSection(plexAux, &sectionAux));
6136     PetscCall(DMGetDS(dmAux, &probAux));
6137     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
6138   }
6139   PetscCall(VecSet(Z, 0.0));
6140   PetscCall(PetscMalloc6(numCells * totDim, &u, X_t ? numCells * totDim : 0, &u_t, numCells * totDim * totDim, &elemMat, hasDyn ? numCells * totDim * totDim : 0, &elemMatD, numCells * totDim, &y, totDim, &z));
6141   if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
6142   PetscCall(DMGetCoordinateField(dm, &coordField));
6143   for (c = cStart; c < cEnd; ++c) {
6144     const PetscInt cell = cells ? cells[c] : c;
6145     const PetscInt cind = c - cStart;
6146     PetscScalar   *x = NULL, *x_t = NULL;
6147     PetscInt       i;
6148 
6149     PetscCall(DMPlexVecGetClosure(plex, section, X, cell, NULL, &x));
6150     for (i = 0; i < totDim; ++i) u[cind * totDim + i] = x[i];
6151     PetscCall(DMPlexVecRestoreClosure(plex, section, X, cell, NULL, &x));
6152     if (X_t) {
6153       PetscCall(DMPlexVecGetClosure(plex, section, X_t, cell, NULL, &x_t));
6154       for (i = 0; i < totDim; ++i) u_t[cind * totDim + i] = x_t[i];
6155       PetscCall(DMPlexVecRestoreClosure(plex, section, X_t, cell, NULL, &x_t));
6156     }
6157     if (dmAux) {
6158       PetscInt subcell;
6159       PetscCall(DMGetEnclosurePoint(dmAux, dm, encAux, cell, &subcell));
6160       PetscCall(DMPlexVecGetClosure(plexAux, sectionAux, A, subcell, NULL, &x));
6161       for (i = 0; i < totDimAux; ++i) a[cind * totDimAux + i] = x[i];
6162       PetscCall(DMPlexVecRestoreClosure(plexAux, sectionAux, A, subcell, NULL, &x));
6163     }
6164     PetscCall(DMPlexVecGetClosure(plex, section, Y, cell, NULL, &x));
6165     for (i = 0; i < totDim; ++i) y[cind * totDim + i] = x[i];
6166     PetscCall(DMPlexVecRestoreClosure(plex, section, Y, cell, NULL, &x));
6167   }
6168   PetscCall(PetscArrayzero(elemMat, numCells * totDim * totDim));
6169   if (hasDyn) PetscCall(PetscArrayzero(elemMatD, numCells * totDim * totDim));
6170   for (fieldI = 0; fieldI < Nf; ++fieldI) {
6171     PetscFE  fe;
6172     PetscInt Nb;
6173     /* Conforming batches */
6174     PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
6175     /* Remainder */
6176     PetscInt        Nr, offset, Nq;
6177     PetscQuadrature qGeom = NULL;
6178     PetscInt        maxDegree;
6179     PetscFEGeom    *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL;
6180 
6181     PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
6182     PetscCall(PetscFEGetQuadrature(fe, &quad));
6183     PetscCall(PetscFEGetDimension(fe, &Nb));
6184     PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
6185     PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
6186     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
6187     if (!qGeom) {
6188       PetscCall(PetscFEGetQuadrature(fe, &qGeom));
6189       PetscCall(PetscObjectReference((PetscObject)qGeom));
6190     }
6191     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
6192     PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
6193     blockSize = Nb;
6194     batchSize = numBlocks * blockSize;
6195     PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
6196     numChunks = numCells / (numBatches * batchSize);
6197     Ne        = numChunks * numBatches * batchSize;
6198     Nr        = numCells % (numBatches * batchSize);
6199     offset    = numCells - Nr;
6200     PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
6201     PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &remGeom));
6202     for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
6203       key.field = fieldI * Nf + fieldJ;
6204       PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat));
6205       PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Nr, remGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, &a[offset * totDimAux], t, X_tShift, &elemMat[offset * totDim * totDim]));
6206       if (hasDyn) {
6207         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatD));
6208         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Nr, remGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, &a[offset * totDimAux], t, X_tShift, &elemMatD[offset * totDim * totDim]));
6209       }
6210     }
6211     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &remGeom));
6212     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, 0, offset, &chunkGeom));
6213     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
6214     PetscCall(PetscQuadratureDestroy(&qGeom));
6215   }
6216   if (hasDyn) {
6217     for (c = 0; c < numCells * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
6218   }
6219   for (c = cStart; c < cEnd; ++c) {
6220     const PetscInt     cell = cells ? cells[c] : c;
6221     const PetscInt     cind = c - cStart;
6222     const PetscBLASInt M = totDim, one = 1;
6223     const PetscScalar  a = 1.0, b = 0.0;
6224 
6225     PetscCallBLAS("BLASgemv", BLASgemv_("N", &M, &M, &a, &elemMat[cind * totDim * totDim], &M, &y[cind * totDim], &one, &b, z, &one));
6226     if (mesh->printFEM > 1) {
6227       PetscCall(DMPrintCellMatrix(c, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
6228       PetscCall(DMPrintCellVector(c, "Y", totDim, &y[cind * totDim]));
6229       PetscCall(DMPrintCellVector(c, "Z", totDim, z));
6230     }
6231     PetscCall(DMPlexVecSetClosure(dm, section, Z, cell, z, ADD_VALUES));
6232   }
6233   PetscCall(PetscFree6(u, u_t, elemMat, elemMatD, y, z));
6234   if (mesh->printFEM) {
6235     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)Z), "Z:\n"));
6236     PetscCall(VecView(Z, NULL));
6237   }
6238   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
6239   PetscCall(PetscFree(a));
6240   PetscCall(DMDestroy(&plexAux));
6241   PetscCall(DMDestroy(&plex));
6242   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
6243   PetscFunctionReturn(PETSC_SUCCESS);
6244 }
6245