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