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