xref: /petsc/src/dm/impls/plex/plexfem.c (revision 217f7fc6b7ccbb041bf230fae922d6b0eefae257)
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(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
4553   if (cStart >= cEnd) PetscFunctionReturn(0);
4554   PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
4555   /* TODO The places where we have to use isFE are probably the member functions for the PetscDisc class */
4556   /* TODO The FVM geometry is over-manipulated. Make the precalc functions return exactly what we need */
4557   /* FEM+FVM */
4558   PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
4559   /* 1: Get sizes from dm and dmAux */
4560   PetscCall(DMGetLocalSection(dm, &section));
4561   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
4562   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds));
4563   PetscCall(PetscDSGetNumFields(ds, &Nf));
4564   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
4565   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &locA));
4566   if (locA) {
4567     PetscInt subcell;
4568     PetscCall(VecGetDM(locA, &dmAux));
4569     PetscCall(DMGetEnclosurePoint(dmAux, dm, DM_ENC_UNKNOWN, cells ? cells[cStart] : cStart, &subcell));
4570     PetscCall(DMGetCellDS(dmAux, subcell, &dsAux));
4571     PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
4572   }
4573   /* 2: Get geometric data */
4574   for (f = 0; f < Nf; ++f) {
4575     PetscObject  obj;
4576     PetscClassId id;
4577     PetscBool    fimp;
4578 
4579     PetscCall(PetscDSGetImplicit(ds, f, &fimp));
4580     if (isImplicit != fimp) continue;
4581     PetscCall(PetscDSGetDiscretization(ds, f, &obj));
4582     PetscCall(PetscObjectGetClassId(obj, &id));
4583     if (id == PETSCFE_CLASSID) useFEM = PETSC_TRUE;
4584     if (id == PETSCFV_CLASSID) {
4585       useFVM = PETSC_TRUE;
4586       fvm    = (PetscFV)obj;
4587     }
4588   }
4589   if (useFEM) {
4590     PetscCall(DMGetCoordinateField(dm, &coordField));
4591     PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
4592     if (maxDegree <= 1) {
4593       PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad));
4594       if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
4595     } else {
4596       PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
4597       for (f = 0; f < Nf; ++f) {
4598         PetscObject  obj;
4599         PetscClassId id;
4600         PetscBool    fimp;
4601 
4602         PetscCall(PetscDSGetImplicit(ds, f, &fimp));
4603         if (isImplicit != fimp) continue;
4604         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
4605         PetscCall(PetscObjectGetClassId(obj, &id));
4606         if (id == PETSCFE_CLASSID) {
4607           PetscFE fe = (PetscFE)obj;
4608 
4609           PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
4610           PetscCall(PetscObjectReference((PetscObject)quads[f]));
4611           PetscCall(DMSNESGetFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
4612         }
4613       }
4614     }
4615   }
4616   if (useFVM) {
4617     PetscCall(DMPlexGetGeometryFVM(dm, &faceGeometryFVM, &cellGeometryFVM, NULL));
4618     PetscCall(VecGetArrayRead(faceGeometryFVM, (const PetscScalar **)&fgeomFVM));
4619     PetscCall(VecGetArrayRead(cellGeometryFVM, (const PetscScalar **)&cgeomFVM));
4620     /* Reconstruct and limit cell gradients */
4621     PetscCall(DMPlexGetGradientDM(dm, fvm, &dmGrad));
4622     if (dmGrad) {
4623       PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
4624       PetscCall(DMGetGlobalVector(dmGrad, &grad));
4625       PetscCall(DMPlexReconstructGradients_Internal(dm, fvm, fStart, fEnd, faceGeometryFVM, cellGeometryFVM, locX, grad));
4626       /* Communicate gradient values */
4627       PetscCall(DMGetLocalVector(dmGrad, &locGrad));
4628       PetscCall(DMGlobalToLocalBegin(dmGrad, grad, INSERT_VALUES, locGrad));
4629       PetscCall(DMGlobalToLocalEnd(dmGrad, grad, INSERT_VALUES, locGrad));
4630       PetscCall(DMRestoreGlobalVector(dmGrad, &grad));
4631     }
4632     /* Handle non-essential (e.g. outflow) boundary values */
4633     PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_FALSE, locX, time, faceGeometryFVM, cellGeometryFVM, locGrad));
4634   }
4635   /* Loop over chunks */
4636   if (useFEM) PetscCall(ISCreate(PETSC_COMM_SELF, &chunkIS));
4637   numCells      = cEnd - cStart;
4638   numChunks     = 1;
4639   cellChunkSize = numCells / numChunks;
4640   faceChunkSize = (fEnd - fStart) / numChunks;
4641   numChunks     = PetscMin(1, numCells);
4642   for (chunk = 0; chunk < numChunks; ++chunk) {
4643     PetscScalar     *elemVec, *fluxL, *fluxR;
4644     PetscReal       *vol;
4645     PetscFVFaceGeom *fgeom;
4646     PetscInt         cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
4647     PetscInt         fS = fStart + chunk * faceChunkSize, fE = PetscMin(fS + faceChunkSize, fEnd), numFaces = 0, face;
4648 
4649     /* Extract field coefficients */
4650     if (useFEM) {
4651       PetscCall(ISGetPointSubrange(chunkIS, cS, cE, cells));
4652       PetscCall(DMPlexGetCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
4653       PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
4654       PetscCall(PetscArrayzero(elemVec, numCells * totDim));
4655     }
4656     if (useFVM) {
4657       PetscCall(DMPlexGetFaceFields(dm, fS, fE, locX, locX_t, faceGeometryFVM, cellGeometryFVM, locGrad, &numFaces, &uL, &uR));
4658       PetscCall(DMPlexGetFaceGeometry(dm, fS, fE, faceGeometryFVM, cellGeometryFVM, &numFaces, &fgeom, &vol));
4659       PetscCall(DMGetWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxL));
4660       PetscCall(DMGetWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxR));
4661       PetscCall(PetscArrayzero(fluxL, numFaces * totDim));
4662       PetscCall(PetscArrayzero(fluxR, numFaces * totDim));
4663     }
4664     /* TODO We will interlace both our field coefficients (u, u_t, uL, uR, etc.) and our output (elemVec, fL, fR). I think this works */
4665     /* Loop over fields */
4666     for (f = 0; f < Nf; ++f) {
4667       PetscObject  obj;
4668       PetscClassId id;
4669       PetscBool    fimp;
4670       PetscInt     numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;
4671 
4672       key.field = f;
4673       PetscCall(PetscDSGetImplicit(ds, f, &fimp));
4674       if (isImplicit != fimp) continue;
4675       PetscCall(PetscDSGetDiscretization(ds, f, &obj));
4676       PetscCall(PetscObjectGetClassId(obj, &id));
4677       if (id == PETSCFE_CLASSID) {
4678         PetscFE         fe        = (PetscFE)obj;
4679         PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[f];
4680         PetscFEGeom    *chunkGeom = NULL;
4681         PetscQuadrature quad      = affineQuad ? affineQuad : quads[f];
4682         PetscInt        Nq, Nb;
4683 
4684         PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
4685         PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
4686         PetscCall(PetscFEGetDimension(fe, &Nb));
4687         blockSize = Nb;
4688         batchSize = numBlocks * blockSize;
4689         PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
4690         numChunks = numCells / (numBatches * batchSize);
4691         Ne        = numChunks * numBatches * batchSize;
4692         Nr        = numCells % (numBatches * batchSize);
4693         offset    = numCells - Nr;
4694         /* Integrate FE residual to get elemVec (need fields at quadrature points) */
4695         /*   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) */
4696         PetscCall(PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom));
4697         PetscCall(PetscFEIntegrateResidual(ds, key, Ne, chunkGeom, u, u_t, dsAux, a, t, elemVec));
4698         PetscCall(PetscFEGeomGetChunk(geom, offset, numCells, &chunkGeom));
4699         PetscCall(PetscFEIntegrateResidual(ds, key, Nr, chunkGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, dsAux, &a[offset * totDimAux], t, &elemVec[offset * totDim]));
4700         PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &chunkGeom));
4701       } else if (id == PETSCFV_CLASSID) {
4702         PetscFV fv = (PetscFV)obj;
4703 
4704         Ne = numFaces;
4705         /* Riemann solve over faces (need fields at face centroids) */
4706         /*   We need to evaluate FE fields at those coordinates */
4707         PetscCall(PetscFVIntegrateRHSFunction(fv, ds, f, Ne, fgeom, vol, uL, uR, fluxL, fluxR));
4708       } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
4709     }
4710     /* Loop over domain */
4711     if (useFEM) {
4712       /* Add elemVec to locX */
4713       for (c = cS; c < cE; ++c) {
4714         const PetscInt cell = cells ? cells[c] : c;
4715         const PetscInt cind = c - cStart;
4716 
4717         if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, name, totDim, &elemVec[cind * totDim]));
4718         if (ghostLabel) {
4719           PetscInt ghostVal;
4720 
4721           PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
4722           if (ghostVal > 0) continue;
4723         }
4724         PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind * totDim], ADD_ALL_VALUES));
4725       }
4726     }
4727     if (useFVM) {
4728       PetscScalar *fa;
4729       PetscInt     iface;
4730 
4731       PetscCall(VecGetArray(locF, &fa));
4732       for (f = 0; f < Nf; ++f) {
4733         PetscFV      fv;
4734         PetscObject  obj;
4735         PetscClassId id;
4736         PetscInt     foff, pdim;
4737 
4738         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
4739         PetscCall(PetscDSGetFieldOffset(ds, f, &foff));
4740         PetscCall(PetscObjectGetClassId(obj, &id));
4741         if (id != PETSCFV_CLASSID) continue;
4742         fv = (PetscFV)obj;
4743         PetscCall(PetscFVGetNumComponents(fv, &pdim));
4744         /* Accumulate fluxes to cells */
4745         for (face = fS, iface = 0; face < fE; ++face) {
4746           const PetscInt *scells;
4747           PetscScalar    *fL = NULL, *fR = NULL;
4748           PetscInt        ghost, d, nsupp, nchild;
4749 
4750           PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
4751           PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
4752           PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
4753           if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
4754           PetscCall(DMPlexGetSupport(dm, face, &scells));
4755           PetscCall(DMLabelGetValue(ghostLabel, scells[0], &ghost));
4756           if (ghost <= 0) PetscCall(DMPlexPointLocalFieldRef(dm, scells[0], f, fa, &fL));
4757           PetscCall(DMLabelGetValue(ghostLabel, scells[1], &ghost));
4758           if (ghost <= 0) PetscCall(DMPlexPointLocalFieldRef(dm, scells[1], f, fa, &fR));
4759           for (d = 0; d < pdim; ++d) {
4760             if (fL) fL[d] -= fluxL[iface * totDim + foff + d];
4761             if (fR) fR[d] += fluxR[iface * totDim + foff + d];
4762           }
4763           ++iface;
4764         }
4765       }
4766       PetscCall(VecRestoreArray(locF, &fa));
4767     }
4768     /* Handle time derivative */
4769     if (locX_t) {
4770       PetscScalar *x_t, *fa;
4771 
4772       PetscCall(VecGetArray(locF, &fa));
4773       PetscCall(VecGetArray(locX_t, &x_t));
4774       for (f = 0; f < Nf; ++f) {
4775         PetscFV      fv;
4776         PetscObject  obj;
4777         PetscClassId id;
4778         PetscInt     pdim, d;
4779 
4780         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
4781         PetscCall(PetscObjectGetClassId(obj, &id));
4782         if (id != PETSCFV_CLASSID) continue;
4783         fv = (PetscFV)obj;
4784         PetscCall(PetscFVGetNumComponents(fv, &pdim));
4785         for (c = cS; c < cE; ++c) {
4786           const PetscInt cell = cells ? cells[c] : c;
4787           PetscScalar   *u_t, *r;
4788 
4789           if (ghostLabel) {
4790             PetscInt ghostVal;
4791 
4792             PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
4793             if (ghostVal > 0) continue;
4794           }
4795           PetscCall(DMPlexPointLocalFieldRead(dm, cell, f, x_t, &u_t));
4796           PetscCall(DMPlexPointLocalFieldRef(dm, cell, f, fa, &r));
4797           for (d = 0; d < pdim; ++d) r[d] += u_t[d];
4798         }
4799       }
4800       PetscCall(VecRestoreArray(locX_t, &x_t));
4801       PetscCall(VecRestoreArray(locF, &fa));
4802     }
4803     if (useFEM) {
4804       PetscCall(DMPlexRestoreCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
4805       PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
4806     }
4807     if (useFVM) {
4808       PetscCall(DMPlexRestoreFaceFields(dm, fS, fE, locX, locX_t, faceGeometryFVM, cellGeometryFVM, locGrad, &numFaces, &uL, &uR));
4809       PetscCall(DMPlexRestoreFaceGeometry(dm, fS, fE, faceGeometryFVM, cellGeometryFVM, &numFaces, &fgeom, &vol));
4810       PetscCall(DMRestoreWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxL));
4811       PetscCall(DMRestoreWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxR));
4812       if (dmGrad) PetscCall(DMRestoreLocalVector(dmGrad, &locGrad));
4813     }
4814   }
4815   if (useFEM) PetscCall(ISDestroy(&chunkIS));
4816   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
4817 
4818   if (useFEM) {
4819     PetscCall(DMPlexComputeBdResidual_Internal(dm, locX, locX_t, t, locF, user));
4820 
4821     if (maxDegree <= 1) {
4822       PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
4823       PetscCall(PetscQuadratureDestroy(&affineQuad));
4824     } else {
4825       for (f = 0; f < Nf; ++f) {
4826         PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
4827         PetscCall(PetscQuadratureDestroy(&quads[f]));
4828       }
4829       PetscCall(PetscFree2(quads, geoms));
4830     }
4831   }
4832 
4833   /* FEM */
4834   /* 1: Get sizes from dm and dmAux */
4835   /* 2: Get geometric data */
4836   /* 3: Handle boundary values */
4837   /* 4: Loop over domain */
4838   /*   Extract coefficients */
4839   /* Loop over fields */
4840   /*   Set tiling for FE*/
4841   /*   Integrate FE residual to get elemVec */
4842   /*     Loop over subdomain */
4843   /*       Loop over quad points */
4844   /*         Transform coords to real space */
4845   /*         Evaluate field and aux fields at point */
4846   /*         Evaluate residual at point */
4847   /*         Transform residual to real space */
4848   /*       Add residual to elemVec */
4849   /* Loop over domain */
4850   /*   Add elemVec to locX */
4851 
4852   /* FVM */
4853   /* Get geometric data */
4854   /* If using gradients */
4855   /*   Compute gradient data */
4856   /*   Loop over domain faces */
4857   /*     Count computational faces */
4858   /*     Reconstruct cell gradient */
4859   /*   Loop over domain cells */
4860   /*     Limit cell gradients */
4861   /* Handle boundary values */
4862   /* Loop over domain faces */
4863   /*   Read out field, centroid, normal, volume for each side of face */
4864   /* Riemann solve over faces */
4865   /* Loop over domain faces */
4866   /*   Accumulate fluxes to cells */
4867   /* TODO Change printFEM to printDisc here */
4868   if (mesh->printFEM) {
4869     Vec          locFbc;
4870     PetscInt     pStart, pEnd, p, maxDof;
4871     PetscScalar *zeroes;
4872 
4873     PetscCall(VecDuplicate(locF, &locFbc));
4874     PetscCall(VecCopy(locF, locFbc));
4875     PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
4876     PetscCall(PetscSectionGetMaxDof(section, &maxDof));
4877     PetscCall(PetscCalloc1(maxDof, &zeroes));
4878     for (p = pStart; p < pEnd; p++) PetscCall(VecSetValuesSection(locFbc, section, p, zeroes, INSERT_BC_VALUES));
4879     PetscCall(PetscFree(zeroes));
4880     PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc));
4881     PetscCall(VecDestroy(&locFbc));
4882   }
4883   PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
4884   PetscFunctionReturn(0);
4885 }
4886 
4887 /*
4888   1) Allow multiple kernels for BdResidual for hybrid DS
4889 
4890   DONE 2) Get out dsAux for either side at the same time as cohesive cell dsAux
4891 
4892   DONE 3) Change DMGetCellFields() to get different aux data a[] for each side
4893      - I think I just need to replace a[] with the closure from each face
4894 
4895   4) Run both kernels for each non-hybrid field with correct dsAux, and then hybrid field as before
4896 */
4897 PetscErrorCode DMPlexComputeResidual_Hybrid_Internal(DM dm, PetscFormKey key[], IS cellIS, PetscReal time, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
4898 {
4899   DM_Plex        *mesh       = (DM_Plex *)dm->data;
4900   const char     *name       = "Hybrid Residual";
4901   DM              dmAux[3]   = {NULL, NULL, NULL};
4902   DMLabel         ghostLabel = NULL;
4903   PetscDS         ds         = NULL;
4904   PetscDS         dsAux[3]   = {NULL, NULL, NULL};
4905   Vec             locA[3]    = {NULL, NULL, NULL};
4906   PetscScalar    *a[3]       = {NULL, NULL, NULL};
4907   PetscSection    section    = NULL;
4908   DMField         coordField = NULL;
4909   PetscScalar    *u          = NULL, *u_t;
4910   PetscScalar    *elemVec;
4911   IS              chunkIS;
4912   const PetscInt *cells;
4913   PetscInt       *faces;
4914   PetscInt        cStart, cEnd, numCells;
4915   PetscInt        Nf, f, totDim, totDimAux[3], numChunks, cellChunkSize, chunk;
4916   PetscInt        maxDegree  = PETSC_MAX_INT;
4917   PetscQuadrature affineQuad = NULL, *quads = NULL;
4918   PetscFEGeom    *affineGeom = NULL, **geoms = NULL;
4919 
4920   PetscFunctionBegin;
4921   if (!cellIS) PetscFunctionReturn(0);
4922   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
4923   PetscCall(ISGetLocalSize(cellIS, &numCells));
4924   if (cStart >= cEnd) PetscFunctionReturn(0);
4925   if ((key[0].label == key[1].label) && (key[0].value == key[1].value) && (key[0].part == key[1].part)) {
4926     const char *name;
4927     PetscCall(PetscObjectGetName((PetscObject)key[0].label, &name));
4928     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);
4929   }
4930   PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
4931   /* TODO The places where we have to use isFE are probably the member functions for the PetscDisc class */
4932   /* FEM */
4933   /* 1: Get sizes from dm and dmAux */
4934   PetscCall(DMGetSection(dm, &section));
4935   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
4936   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds));
4937   PetscCall(PetscDSGetNumFields(ds, &Nf));
4938   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
4939   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, key[2].value, key[2].part, &locA[2]));
4940   if (locA[2]) {
4941     const PetscInt cellStart = cells ? cells[cStart] : cStart;
4942 
4943     PetscCall(VecGetDM(locA[2], &dmAux[2]));
4944     PetscCall(DMGetCellDS(dmAux[2], cellStart, &dsAux[2]));
4945     PetscCall(PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]));
4946     {
4947       const PetscInt *cone;
4948       PetscInt        c;
4949 
4950       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
4951       for (c = 0; c < 2; ++c) {
4952         const PetscInt *support;
4953         PetscInt        ssize, s;
4954 
4955         PetscCall(DMPlexGetSupport(dm, cone[c], &support));
4956         PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
4957         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);
4958         if (support[0] == cellStart) s = 1;
4959         else if (support[1] == cellStart) s = 0;
4960         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
4961         PetscCall(DMGetAuxiliaryVec(dm, key[c].label, key[c].value, key[c].part, &locA[c]));
4962         PetscCheck(locA[c], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Must have auxiliary vector for (%p, %" PetscInt_FMT ", %" PetscInt_FMT ")", (void *)key[c].label, key[c].value, key[c].part);
4963         if (locA[c]) PetscCall(VecGetDM(locA[c], &dmAux[c]));
4964         else dmAux[c] = dmAux[2];
4965         PetscCall(DMGetCellDS(dmAux[c], support[s], &dsAux[c]));
4966         PetscCall(PetscDSGetTotalDimension(dsAux[c], &totDimAux[c]));
4967       }
4968     }
4969   }
4970   /* 2: Setup geometric data */
4971   PetscCall(DMGetCoordinateField(dm, &coordField));
4972   PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
4973   if (maxDegree > 1) {
4974     PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
4975     for (f = 0; f < Nf; ++f) {
4976       PetscFE fe;
4977 
4978       PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
4979       if (fe) {
4980         PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
4981         PetscCall(PetscObjectReference((PetscObject)quads[f]));
4982       }
4983     }
4984   }
4985   /* Loop over chunks */
4986   cellChunkSize = numCells;
4987   numChunks     = !numCells ? 0 : PetscCeilReal(((PetscReal)numCells) / cellChunkSize);
4988   PetscCall(PetscCalloc1(1 * cellChunkSize, &faces));
4989   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 1 * cellChunkSize, faces, PETSC_USE_POINTER, &chunkIS));
4990   /* Extract field coefficients */
4991   /* NOTE This needs the end cap faces to have identical orientations */
4992   PetscCall(DMPlexGetCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
4993   PetscCall(DMPlexGetHybridAuxFields(dm, dmAux, dsAux, cellIS, locA, a));
4994   PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVec));
4995   for (chunk = 0; chunk < numChunks; ++chunk) {
4996     PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
4997 
4998     PetscCall(PetscMemzero(elemVec, cellChunkSize * totDim * sizeof(PetscScalar)));
4999     /* Get faces */
5000     for (c = cS; c < cE; ++c) {
5001       const PetscInt  cell = cells ? cells[c] : c;
5002       const PetscInt *cone;
5003       PetscCall(DMPlexGetCone(dm, cell, &cone));
5004       faces[0 * cellChunkSize + (c - cS)] = cone[0];
5005       /*faces[1*cellChunkSize+(c-cS)] = cone[1];*/
5006     }
5007     PetscCall(ISGeneralSetIndices(chunkIS, 1 * cellChunkSize, faces, PETSC_USE_POINTER));
5008     /* Get geometric data */
5009     if (maxDegree <= 1) {
5010       if (!affineQuad) PetscCall(DMFieldCreateDefaultQuadrature(coordField, chunkIS, &affineQuad));
5011       if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, affineQuad, PETSC_TRUE, &affineGeom));
5012     } else {
5013       for (f = 0; f < Nf; ++f) {
5014         if (quads[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, quads[f], PETSC_TRUE, &geoms[f]));
5015       }
5016     }
5017     /* Loop over fields */
5018     for (f = 0; f < Nf; ++f) {
5019       PetscFE         fe;
5020       PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[f];
5021       PetscFEGeom    *chunkGeom = NULL, *remGeom = NULL;
5022       PetscQuadrature quad = affineQuad ? affineQuad : quads[f];
5023       PetscInt        numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb;
5024       PetscBool       isCohesiveField;
5025 
5026       PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
5027       if (!fe) continue;
5028       PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5029       PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
5030       PetscCall(PetscFEGetDimension(fe, &Nb));
5031       blockSize = Nb;
5032       batchSize = numBlocks * blockSize;
5033       PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5034       numChunks = numCells / (numBatches * batchSize);
5035       Ne        = numChunks * numBatches * batchSize;
5036       Nr        = numCells % (numBatches * batchSize);
5037       offset    = numCells - Nr;
5038       PetscCall(PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom));
5039       PetscCall(PetscFEGeomGetChunk(geom, offset, numCells, &remGeom));
5040       PetscCall(PetscDSGetCohesive(ds, f, &isCohesiveField));
5041       chunkGeom->isCohesive = remGeom->isCohesive = PETSC_TRUE;
5042       key[0].field                                = f;
5043       key[1].field                                = f;
5044       key[2].field                                = f;
5045       PetscCall(PetscFEIntegrateHybridResidual(ds, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, elemVec));
5046       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]));
5047       PetscCall(PetscFEIntegrateHybridResidual(ds, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, elemVec));
5048       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]));
5049       PetscCall(PetscFEIntegrateHybridResidual(ds, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, elemVec));
5050       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]));
5051       PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &remGeom));
5052       PetscCall(PetscFEGeomRestoreChunk(geom, 0, offset, &chunkGeom));
5053     }
5054     /* Add elemVec to locX */
5055     for (c = cS; c < cE; ++c) {
5056       const PetscInt cell = cells ? cells[c] : c;
5057       const PetscInt cind = c - cStart;
5058 
5059       if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, name, totDim, &elemVec[cind * totDim]));
5060       if (ghostLabel) {
5061         PetscInt ghostVal;
5062 
5063         PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
5064         if (ghostVal > 0) continue;
5065       }
5066       PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind * totDim], ADD_ALL_VALUES));
5067     }
5068   }
5069   PetscCall(DMPlexRestoreCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
5070   PetscCall(DMPlexRestoreHybridAuxFields(dmAux, dsAux, cellIS, locA, a));
5071   PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
5072   PetscCall(PetscFree(faces));
5073   PetscCall(ISDestroy(&chunkIS));
5074   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
5075   if (maxDegree <= 1) {
5076     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
5077     PetscCall(PetscQuadratureDestroy(&affineQuad));
5078   } else {
5079     for (f = 0; f < Nf; ++f) {
5080       if (geoms) PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
5081       if (quads) PetscCall(PetscQuadratureDestroy(&quads[f]));
5082     }
5083     PetscCall(PetscFree2(quads, geoms));
5084   }
5085   PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
5086   PetscFunctionReturn(0);
5087 }
5088 
5089 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)
5090 {
5091   DM_Plex        *mesh = (DM_Plex *)dm->data;
5092   DM              plex = NULL, plexA = NULL, tdm;
5093   DMEnclosureType encAux;
5094   PetscDS         prob, probAux       = NULL;
5095   PetscSection    section, sectionAux = NULL;
5096   PetscSection    globalSection;
5097   Vec             locA = NULL, tv;
5098   PetscScalar    *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL;
5099   PetscInt        v;
5100   PetscInt        Nf, totDim, totDimAux = 0;
5101   PetscBool       transform;
5102 
5103   PetscFunctionBegin;
5104   PetscCall(DMConvert(dm, DMPLEX, &plex));
5105   PetscCall(DMHasBasisTransform(dm, &transform));
5106   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
5107   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
5108   PetscCall(DMGetLocalSection(dm, &section));
5109   PetscCall(DMGetDS(dm, &prob));
5110   PetscCall(PetscDSGetNumFields(prob, &Nf));
5111   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
5112   PetscCall(DMGetAuxiliaryVec(dm, label, values[0], 0, &locA));
5113   if (locA) {
5114     DM dmAux;
5115 
5116     PetscCall(VecGetDM(locA, &dmAux));
5117     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
5118     PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
5119     PetscCall(DMGetDS(plexA, &probAux));
5120     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
5121     PetscCall(DMGetLocalSection(plexA, &sectionAux));
5122   }
5123 
5124   PetscCall(DMGetGlobalSection(dm, &globalSection));
5125   for (v = 0; v < numValues; ++v) {
5126     PetscFEGeom    *fgeom;
5127     PetscInt        maxDegree;
5128     PetscQuadrature qGeom = NULL;
5129     IS              pointIS;
5130     const PetscInt *points;
5131     PetscFormKey    key;
5132     PetscInt        numFaces, face, Nq;
5133 
5134     key.label = label;
5135     key.value = values[v];
5136     key.part  = 0;
5137     PetscCall(DMLabelGetStratumIS(label, values[v], &pointIS));
5138     if (!pointIS) continue; /* No points with that id on this process */
5139     {
5140       IS isectIS;
5141 
5142       /* TODO: Special cases of ISIntersect where it is quick to check a prior if one is a superset of the other */
5143       PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS));
5144       PetscCall(ISDestroy(&pointIS));
5145       pointIS = isectIS;
5146     }
5147     PetscCall(ISGetLocalSize(pointIS, &numFaces));
5148     PetscCall(ISGetIndices(pointIS, &points));
5149     PetscCall(PetscMalloc4(numFaces * totDim, &u, locX_t ? numFaces * totDim : 0, &u_t, numFaces * totDim * totDim, &elemMat, locA ? numFaces * totDimAux : 0, &a));
5150     PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
5151     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom));
5152     if (!qGeom) {
5153       PetscFE fe;
5154 
5155       PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
5156       PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
5157       PetscCall(PetscObjectReference((PetscObject)qGeom));
5158     }
5159     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
5160     PetscCall(DMSNESGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
5161     for (face = 0; face < numFaces; ++face) {
5162       const PetscInt point = points[face], *support;
5163       PetscScalar   *x     = NULL;
5164       PetscInt       i;
5165 
5166       PetscCall(DMPlexGetSupport(dm, point, &support));
5167       PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
5168       for (i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
5169       PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
5170       if (locX_t) {
5171         PetscCall(DMPlexVecGetClosure(plex, section, locX_t, support[0], NULL, &x));
5172         for (i = 0; i < totDim; ++i) u_t[face * totDim + i] = x[i];
5173         PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, support[0], NULL, &x));
5174       }
5175       if (locA) {
5176         PetscInt subp;
5177         PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
5178         PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
5179         for (i = 0; i < totDimAux; ++i) a[face * totDimAux + i] = x[i];
5180         PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
5181       }
5182     }
5183     PetscCall(PetscArrayzero(elemMat, numFaces * totDim * totDim));
5184     {
5185       PetscFE  fe;
5186       PetscInt Nb;
5187       /* Conforming batches */
5188       PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
5189       /* Remainder */
5190       PetscFEGeom *chunkGeom = NULL;
5191       PetscInt     fieldJ, Nr, offset;
5192 
5193       PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
5194       PetscCall(PetscFEGetDimension(fe, &Nb));
5195       PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5196       blockSize = Nb;
5197       batchSize = numBlocks * blockSize;
5198       PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5199       numChunks = numFaces / (numBatches * batchSize);
5200       Ne        = numChunks * numBatches * batchSize;
5201       Nr        = numFaces % (numBatches * batchSize);
5202       offset    = numFaces - Nr;
5203       PetscCall(PetscFEGeomGetChunk(fgeom, 0, offset, &chunkGeom));
5204       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5205         key.field = fieldI * Nf + fieldJ;
5206         PetscCall(PetscFEIntegrateBdJacobian(prob, wf, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat));
5207       }
5208       PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom));
5209       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5210         key.field = fieldI * Nf + fieldJ;
5211         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]));
5212       }
5213       PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom));
5214     }
5215     for (face = 0; face < numFaces; ++face) {
5216       const PetscInt point = points[face], *support;
5217 
5218       /* Transform to global basis before insertion in Jacobian */
5219       PetscCall(DMPlexGetSupport(plex, point, &support));
5220       if (transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, support[0], PETSC_TRUE, totDim, &elemMat[face * totDim * totDim]));
5221       if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(point, "BdJacobian", totDim, totDim, &elemMat[face * totDim * totDim]));
5222       PetscCall(DMPlexMatSetClosure(plex, section, globalSection, JacP, support[0], &elemMat[face * totDim * totDim], ADD_VALUES));
5223     }
5224     PetscCall(DMSNESRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
5225     PetscCall(PetscQuadratureDestroy(&qGeom));
5226     PetscCall(ISRestoreIndices(pointIS, &points));
5227     PetscCall(ISDestroy(&pointIS));
5228     PetscCall(PetscFree4(u, u_t, elemMat, a));
5229   }
5230   if (plex) PetscCall(DMDestroy(&plex));
5231   if (plexA) PetscCall(DMDestroy(&plexA));
5232   PetscFunctionReturn(0);
5233 }
5234 
5235 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)
5236 {
5237   DMField  coordField;
5238   DMLabel  depthLabel;
5239   IS       facetIS;
5240   PetscInt dim;
5241 
5242   PetscFunctionBegin;
5243   PetscCall(DMGetDimension(dm, &dim));
5244   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5245   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
5246   PetscCall(DMGetCoordinateField(dm, &coordField));
5247   PetscCall(DMPlexComputeBdJacobian_Single_Internal(dm, t, wf, label, numValues, values, field, locX, locX_t, X_tShift, Jac, JacP, coordField, facetIS));
5248   PetscCall(ISDestroy(&facetIS));
5249   PetscFunctionReturn(0);
5250 }
5251 
5252 PetscErrorCode DMPlexComputeBdJacobian_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, PetscReal X_tShift, Mat Jac, Mat JacP, void *user)
5253 {
5254   PetscDS  prob;
5255   PetscInt dim, numBd, bd;
5256   DMLabel  depthLabel;
5257   DMField  coordField = NULL;
5258   IS       facetIS;
5259 
5260   PetscFunctionBegin;
5261   PetscCall(DMGetDS(dm, &prob));
5262   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5263   PetscCall(DMGetDimension(dm, &dim));
5264   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
5265   PetscCall(PetscDSGetNumBoundary(prob, &numBd));
5266   PetscCall(DMGetCoordinateField(dm, &coordField));
5267   for (bd = 0; bd < numBd; ++bd) {
5268     PetscWeakForm           wf;
5269     DMBoundaryConditionType type;
5270     DMLabel                 label;
5271     const PetscInt         *values;
5272     PetscInt                fieldI, numValues;
5273     PetscObject             obj;
5274     PetscClassId            id;
5275 
5276     PetscCall(PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &fieldI, NULL, NULL, NULL, NULL, NULL));
5277     if (type & DM_BC_ESSENTIAL) continue;
5278     PetscCall(PetscDSGetDiscretization(prob, fieldI, &obj));
5279     PetscCall(PetscObjectGetClassId(obj, &id));
5280     if (id != PETSCFE_CLASSID) continue;
5281     PetscCall(DMPlexComputeBdJacobian_Single_Internal(dm, t, wf, label, numValues, values, fieldI, locX, locX_t, X_tShift, Jac, JacP, coordField, facetIS));
5282   }
5283   PetscCall(ISDestroy(&facetIS));
5284   PetscFunctionReturn(0);
5285 }
5286 
5287 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)
5288 {
5289   DM_Plex        *mesh  = (DM_Plex *)dm->data;
5290   const char     *name  = "Jacobian";
5291   DM              dmAux = NULL, plex, tdm;
5292   DMEnclosureType encAux;
5293   Vec             A, tv;
5294   DMField         coordField;
5295   PetscDS         prob, probAux = NULL;
5296   PetscSection    section, globalSection, sectionAux;
5297   PetscScalar    *elemMat, *elemMatP, *elemMatD, *u, *u_t, *a = NULL;
5298   const PetscInt *cells;
5299   PetscInt        Nf, fieldI, fieldJ;
5300   PetscInt        totDim, totDimAux = 0, cStart, cEnd, numCells, c;
5301   PetscBool       hasJac = PETSC_FALSE, hasPrec = PETSC_FALSE, hasDyn, hasFV = PETSC_FALSE, transform;
5302 
5303   PetscFunctionBegin;
5304   if (!cellIS) goto end;
5305   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
5306   PetscCall(ISGetLocalSize(cellIS, &numCells));
5307   if (cStart >= cEnd) goto end;
5308   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
5309   PetscCall(DMHasBasisTransform(dm, &transform));
5310   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
5311   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
5312   PetscCall(DMGetLocalSection(dm, &section));
5313   PetscCall(DMGetGlobalSection(dm, &globalSection));
5314   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob));
5315   PetscCall(PetscDSGetNumFields(prob, &Nf));
5316   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
5317   PetscCall(PetscDSHasJacobian(prob, &hasJac));
5318   PetscCall(PetscDSHasJacobianPreconditioner(prob, &hasPrec));
5319   /* user passed in the same matrix, avoid double contributions and
5320      only assemble the Jacobian */
5321   if (hasJac && Jac == JacP) hasPrec = PETSC_FALSE;
5322   PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
5323   hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
5324   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &A));
5325   if (A) {
5326     PetscCall(VecGetDM(A, &dmAux));
5327     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
5328     PetscCall(DMConvert(dmAux, DMPLEX, &plex));
5329     PetscCall(DMGetLocalSection(plex, &sectionAux));
5330     PetscCall(DMGetDS(dmAux, &probAux));
5331     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
5332   }
5333   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));
5334   if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
5335   PetscCall(DMGetCoordinateField(dm, &coordField));
5336   for (c = cStart; c < cEnd; ++c) {
5337     const PetscInt cell = cells ? cells[c] : c;
5338     const PetscInt cind = c - cStart;
5339     PetscScalar   *x = NULL, *x_t = NULL;
5340     PetscInt       i;
5341 
5342     PetscCall(DMPlexVecGetClosure(dm, section, X, cell, NULL, &x));
5343     for (i = 0; i < totDim; ++i) u[cind * totDim + i] = x[i];
5344     PetscCall(DMPlexVecRestoreClosure(dm, section, X, cell, NULL, &x));
5345     if (X_t) {
5346       PetscCall(DMPlexVecGetClosure(dm, section, X_t, cell, NULL, &x_t));
5347       for (i = 0; i < totDim; ++i) u_t[cind * totDim + i] = x_t[i];
5348       PetscCall(DMPlexVecRestoreClosure(dm, section, X_t, cell, NULL, &x_t));
5349     }
5350     if (dmAux) {
5351       PetscInt subcell;
5352       PetscCall(DMGetEnclosurePoint(dmAux, dm, encAux, cell, &subcell));
5353       PetscCall(DMPlexVecGetClosure(plex, sectionAux, A, subcell, NULL, &x));
5354       for (i = 0; i < totDimAux; ++i) a[cind * totDimAux + i] = x[i];
5355       PetscCall(DMPlexVecRestoreClosure(plex, sectionAux, A, subcell, NULL, &x));
5356     }
5357   }
5358   if (hasJac) PetscCall(PetscArrayzero(elemMat, numCells * totDim * totDim));
5359   if (hasPrec) PetscCall(PetscArrayzero(elemMatP, numCells * totDim * totDim));
5360   if (hasDyn) PetscCall(PetscArrayzero(elemMatD, numCells * totDim * totDim));
5361   for (fieldI = 0; fieldI < Nf; ++fieldI) {
5362     PetscClassId    id;
5363     PetscFE         fe;
5364     PetscQuadrature qGeom = NULL;
5365     PetscInt        Nb;
5366     /* Conforming batches */
5367     PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
5368     /* Remainder */
5369     PetscInt     Nr, offset, Nq;
5370     PetscInt     maxDegree;
5371     PetscFEGeom *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL;
5372 
5373     PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
5374     PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
5375     if (id == PETSCFV_CLASSID) {
5376       hasFV = PETSC_TRUE;
5377       continue;
5378     }
5379     PetscCall(PetscFEGetDimension(fe, &Nb));
5380     PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5381     PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
5382     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
5383     if (!qGeom) {
5384       PetscCall(PetscFEGetQuadrature(fe, &qGeom));
5385       PetscCall(PetscObjectReference((PetscObject)qGeom));
5386     }
5387     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
5388     PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
5389     blockSize = Nb;
5390     batchSize = numBlocks * blockSize;
5391     PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5392     numChunks = numCells / (numBatches * batchSize);
5393     Ne        = numChunks * numBatches * batchSize;
5394     Nr        = numCells % (numBatches * batchSize);
5395     offset    = numCells - Nr;
5396     PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
5397     PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &remGeom));
5398     for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5399       key.field = fieldI * Nf + fieldJ;
5400       if (hasJac) {
5401         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat));
5402         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]));
5403       }
5404       if (hasPrec) {
5405         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatP));
5406         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]));
5407       }
5408       if (hasDyn) {
5409         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatD));
5410         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]));
5411       }
5412     }
5413     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &remGeom));
5414     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, 0, offset, &chunkGeom));
5415     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
5416     PetscCall(PetscQuadratureDestroy(&qGeom));
5417   }
5418   /*   Add contribution from X_t */
5419   if (hasDyn) {
5420     for (c = 0; c < numCells * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
5421   }
5422   if (hasFV) {
5423     PetscClassId id;
5424     PetscFV      fv;
5425     PetscInt     offsetI, NcI, NbI = 1, fc, f;
5426 
5427     for (fieldI = 0; fieldI < Nf; ++fieldI) {
5428       PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fv));
5429       PetscCall(PetscDSGetFieldOffset(prob, fieldI, &offsetI));
5430       PetscCall(PetscObjectGetClassId((PetscObject)fv, &id));
5431       if (id != PETSCFV_CLASSID) continue;
5432       /* Put in the identity */
5433       PetscCall(PetscFVGetNumComponents(fv, &NcI));
5434       for (c = cStart; c < cEnd; ++c) {
5435         const PetscInt cind    = c - cStart;
5436         const PetscInt eOffset = cind * totDim * totDim;
5437         for (fc = 0; fc < NcI; ++fc) {
5438           for (f = 0; f < NbI; ++f) {
5439             const PetscInt i = offsetI + f * NcI + fc;
5440             if (hasPrec) {
5441               if (hasJac) elemMat[eOffset + i * totDim + i] = 1.0;
5442               elemMatP[eOffset + i * totDim + i] = 1.0;
5443             } else {
5444               elemMat[eOffset + i * totDim + i] = 1.0;
5445             }
5446           }
5447         }
5448       }
5449     }
5450     /* No allocated space for FV stuff, so ignore the zero entries */
5451     PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE));
5452   }
5453   /* Insert values into matrix */
5454   for (c = cStart; c < cEnd; ++c) {
5455     const PetscInt cell = cells ? cells[c] : c;
5456     const PetscInt cind = c - cStart;
5457 
5458     /* Transform to global basis before insertion in Jacobian */
5459     if (transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, cell, PETSC_TRUE, totDim, &elemMat[cind * totDim * totDim]));
5460     if (hasPrec) {
5461       if (hasJac) {
5462         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
5463         PetscCall(DMPlexMatSetClosure(dm, section, globalSection, Jac, cell, &elemMat[cind * totDim * totDim], ADD_VALUES));
5464       }
5465       if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatP[cind * totDim * totDim]));
5466       PetscCall(DMPlexMatSetClosure(dm, section, globalSection, JacP, cell, &elemMatP[cind * totDim * totDim], ADD_VALUES));
5467     } else {
5468       if (hasJac) {
5469         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
5470         PetscCall(DMPlexMatSetClosure(dm, section, globalSection, JacP, cell, &elemMat[cind * totDim * totDim], ADD_VALUES));
5471       }
5472     }
5473   }
5474   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
5475   if (hasFV) PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE));
5476   PetscCall(PetscFree5(u, u_t, elemMat, elemMatP, elemMatD));
5477   if (dmAux) {
5478     PetscCall(PetscFree(a));
5479     PetscCall(DMDestroy(&plex));
5480   }
5481   /* Compute boundary integrals */
5482   PetscCall(DMPlexComputeBdJacobian_Internal(dm, X, X_t, t, X_tShift, Jac, JacP, user));
5483   /* Assemble matrix */
5484 end : {
5485   PetscBool assOp = hasJac && hasPrec ? PETSC_TRUE : PETSC_FALSE, gassOp;
5486 
5487   PetscCallMPI(MPI_Allreduce(&assOp, &gassOp, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
5488   if (hasJac && hasPrec) {
5489     PetscCall(MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY));
5490     PetscCall(MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY));
5491   }
5492 }
5493   PetscCall(MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY));
5494   PetscCall(MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY));
5495   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
5496   PetscFunctionReturn(0);
5497 }
5498 
5499 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)
5500 {
5501   DM_Plex        *mesh          = (DM_Plex *)dm->data;
5502   const char     *name          = "Hybrid Jacobian";
5503   DM              dmAux[3]      = {NULL, NULL, NULL};
5504   DMLabel         ghostLabel    = NULL;
5505   DM              plex          = NULL;
5506   DM              plexA         = NULL;
5507   PetscDS         ds            = NULL;
5508   PetscDS         dsAux[3]      = {NULL, NULL, NULL};
5509   Vec             locA[3]       = {NULL, NULL, NULL};
5510   PetscSection    section       = NULL;
5511   PetscSection    sectionAux[3] = {NULL, NULL, NULL};
5512   DMField         coordField    = NULL;
5513   PetscScalar    *u             = NULL, *u_t, *a[3];
5514   PetscScalar    *elemMat, *elemMatP;
5515   PetscSection    globalSection;
5516   IS              chunkIS;
5517   const PetscInt *cells;
5518   PetscInt       *faces;
5519   PetscInt        cStart, cEnd, numCells;
5520   PetscInt        Nf, fieldI, fieldJ, totDim, totDimAux[3], numChunks, cellChunkSize, chunk;
5521   PetscInt        maxDegree  = PETSC_MAX_INT;
5522   PetscQuadrature affineQuad = NULL, *quads = NULL;
5523   PetscFEGeom    *affineGeom = NULL, **geoms = NULL;
5524   PetscBool       hasBdJac, hasBdPrec;
5525 
5526   PetscFunctionBegin;
5527   if (!cellIS) PetscFunctionReturn(0);
5528   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
5529   PetscCall(ISGetLocalSize(cellIS, &numCells));
5530   if (cStart >= cEnd) PetscFunctionReturn(0);
5531   if ((key[0].label == key[1].label) && (key[0].value == key[1].value) && (key[0].part == key[1].part)) {
5532     const char *name;
5533     PetscCall(PetscObjectGetName((PetscObject)key[0].label, &name));
5534     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);
5535   }
5536   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
5537   PetscCall(DMConvert(dm, DMPLEX, &plex));
5538   PetscCall(DMGetSection(dm, &section));
5539   PetscCall(DMGetGlobalSection(dm, &globalSection));
5540   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
5541   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds));
5542   PetscCall(PetscDSGetNumFields(ds, &Nf));
5543   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
5544   PetscCall(PetscDSHasBdJacobian(ds, &hasBdJac));
5545   PetscCall(PetscDSHasBdJacobianPreconditioner(ds, &hasBdPrec));
5546   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, key[2].value, key[2].part, &locA[2]));
5547   if (locA[2]) {
5548     const PetscInt cellStart = cells ? cells[cStart] : cStart;
5549 
5550     PetscCall(VecGetDM(locA[2], &dmAux[2]));
5551     PetscCall(DMConvert(dmAux[2], DMPLEX, &plexA));
5552     PetscCall(DMGetSection(dmAux[2], &sectionAux[2]));
5553     PetscCall(DMGetCellDS(dmAux[2], cellStart, &dsAux[2]));
5554     PetscCall(PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]));
5555     {
5556       const PetscInt *cone;
5557       PetscInt        c;
5558 
5559       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
5560       for (c = 0; c < 2; ++c) {
5561         const PetscInt *support;
5562         PetscInt        ssize, s;
5563 
5564         PetscCall(DMPlexGetSupport(dm, cone[c], &support));
5565         PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
5566         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);
5567         if (support[0] == cellStart) s = 1;
5568         else if (support[1] == cellStart) s = 0;
5569         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
5570         PetscCall(DMGetAuxiliaryVec(dm, key[c].label, key[c].value, key[c].part, &locA[c]));
5571         if (locA[c]) PetscCall(VecGetDM(locA[c], &dmAux[c]));
5572         else dmAux[c] = dmAux[2];
5573         PetscCall(DMGetCellDS(dmAux[c], support[s], &dsAux[c]));
5574         PetscCall(PetscDSGetTotalDimension(dsAux[c], &totDimAux[c]));
5575       }
5576     }
5577   }
5578   PetscCall(DMGetCoordinateField(dm, &coordField));
5579   PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
5580   if (maxDegree > 1) {
5581     PetscInt f;
5582     PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
5583     for (f = 0; f < Nf; ++f) {
5584       PetscFE fe;
5585 
5586       PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
5587       if (fe) {
5588         PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
5589         PetscCall(PetscObjectReference((PetscObject)quads[f]));
5590       }
5591     }
5592   }
5593   cellChunkSize = numCells;
5594   numChunks     = !numCells ? 0 : PetscCeilReal(((PetscReal)numCells) / cellChunkSize);
5595   PetscCall(PetscCalloc1(1 * cellChunkSize, &faces));
5596   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 1 * cellChunkSize, faces, PETSC_USE_POINTER, &chunkIS));
5597   PetscCall(DMPlexGetCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
5598   PetscCall(DMPlexGetHybridAuxFields(dm, dmAux, dsAux, cellIS, locA, a));
5599   PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMat));
5600   PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatP));
5601   for (chunk = 0; chunk < numChunks; ++chunk) {
5602     PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
5603 
5604     if (hasBdJac) PetscCall(PetscMemzero(elemMat, numCells * totDim * totDim * sizeof(PetscScalar)));
5605     if (hasBdPrec) PetscCall(PetscMemzero(elemMatP, numCells * totDim * totDim * sizeof(PetscScalar)));
5606     /* Get faces */
5607     for (c = cS; c < cE; ++c) {
5608       const PetscInt  cell = cells ? cells[c] : c;
5609       const PetscInt *cone;
5610       PetscCall(DMPlexGetCone(plex, cell, &cone));
5611       faces[0 * cellChunkSize + (c - cS)] = cone[0];
5612       /*faces[2*cellChunkSize+(c-cS)] = cone[1];*/
5613     }
5614     PetscCall(ISGeneralSetIndices(chunkIS, 1 * cellChunkSize, faces, PETSC_USE_POINTER));
5615     if (maxDegree <= 1) {
5616       if (!affineQuad) PetscCall(DMFieldCreateDefaultQuadrature(coordField, chunkIS, &affineQuad));
5617       if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, affineQuad, PETSC_TRUE, &affineGeom));
5618     } else {
5619       PetscInt f;
5620       for (f = 0; f < Nf; ++f) {
5621         if (quads[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, quads[f], PETSC_TRUE, &geoms[f]));
5622       }
5623     }
5624 
5625     for (fieldI = 0; fieldI < Nf; ++fieldI) {
5626       PetscFE         feI;
5627       PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[fieldI];
5628       PetscFEGeom    *chunkGeom = NULL, *remGeom = NULL;
5629       PetscQuadrature quad = affineQuad ? affineQuad : quads[fieldI];
5630       PetscInt        numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb;
5631       PetscBool       isCohesiveField;
5632 
5633       PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&feI));
5634       if (!feI) continue;
5635       PetscCall(PetscFEGetTileSizes(feI, NULL, &numBlocks, NULL, &numBatches));
5636       PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
5637       PetscCall(PetscFEGetDimension(feI, &Nb));
5638       blockSize = Nb;
5639       batchSize = numBlocks * blockSize;
5640       PetscCall(PetscFESetTileSizes(feI, blockSize, numBlocks, batchSize, numBatches));
5641       numChunks = numCells / (numBatches * batchSize);
5642       Ne        = numChunks * numBatches * batchSize;
5643       Nr        = numCells % (numBatches * batchSize);
5644       offset    = numCells - Nr;
5645       PetscCall(PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom));
5646       PetscCall(PetscFEGeomGetChunk(geom, offset, numCells, &remGeom));
5647       PetscCall(PetscDSGetCohesive(ds, fieldI, &isCohesiveField));
5648       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5649         PetscFE feJ;
5650 
5651         PetscCall(PetscDSGetDiscretization(ds, fieldJ, (PetscObject *)&feJ));
5652         if (!feJ) continue;
5653         key[0].field = fieldI * Nf + fieldJ;
5654         key[1].field = fieldI * Nf + fieldJ;
5655         key[2].field = fieldI * Nf + fieldJ;
5656         if (hasBdJac) {
5657           PetscCall(PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, X_tShift, elemMat));
5658           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]));
5659           PetscCall(PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, X_tShift, elemMat));
5660           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]));
5661         }
5662         if (hasBdPrec) {
5663           PetscCall(PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN_PRE, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, X_tShift, elemMatP));
5664           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]));
5665           PetscCall(PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN_PRE, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, X_tShift, elemMatP));
5666           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]));
5667         }
5668         if (hasBdJac) {
5669           PetscCall(PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, X_tShift, elemMat));
5670           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]));
5671         }
5672         if (hasBdPrec) {
5673           PetscCall(PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN_PRE, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, X_tShift, elemMatP));
5674           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]));
5675         }
5676       }
5677       PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &remGeom));
5678       PetscCall(PetscFEGeomRestoreChunk(geom, 0, offset, &chunkGeom));
5679     }
5680     /* Insert values into matrix */
5681     for (c = cS; c < cE; ++c) {
5682       const PetscInt cell = cells ? cells[c] : c;
5683       const PetscInt cind = c - cS;
5684 
5685       if (hasBdPrec) {
5686         if (hasBdJac) {
5687           if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
5688           PetscCall(DMPlexMatSetClosure(plex, section, globalSection, Jac, cell, &elemMat[cind * totDim * totDim], ADD_VALUES));
5689         }
5690         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatP[cind * totDim * totDim]));
5691         PetscCall(DMPlexMatSetClosure(plex, section, globalSection, JacP, cell, &elemMatP[cind * totDim * totDim], ADD_VALUES));
5692       } else if (hasBdJac) {
5693         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
5694         PetscCall(DMPlexMatSetClosure(plex, section, globalSection, JacP, cell, &elemMat[cind * totDim * totDim], ADD_VALUES));
5695       }
5696     }
5697   }
5698   PetscCall(DMPlexRestoreCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
5699   PetscCall(DMPlexRestoreHybridAuxFields(dmAux, dsAux, cellIS, locA, a));
5700   PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMat));
5701   PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatP));
5702   PetscCall(PetscFree(faces));
5703   PetscCall(ISDestroy(&chunkIS));
5704   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
5705   if (maxDegree <= 1) {
5706     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
5707     PetscCall(PetscQuadratureDestroy(&affineQuad));
5708   } else {
5709     PetscInt f;
5710     for (f = 0; f < Nf; ++f) {
5711       if (geoms) PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
5712       if (quads) PetscCall(PetscQuadratureDestroy(&quads[f]));
5713     }
5714     PetscCall(PetscFree2(quads, geoms));
5715   }
5716   if (dmAux[2]) PetscCall(DMDestroy(&plexA));
5717   PetscCall(DMDestroy(&plex));
5718   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
5719   PetscFunctionReturn(0);
5720 }
5721 
5722 /*
5723   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.
5724 
5725   Input Parameters:
5726 + dm     - The mesh
5727 . key    - The PetscWeakFormKey indcating where integration should happen
5728 . cellIS - The cells to integrate over
5729 . t      - The time
5730 . X_tShift - The multiplier for the Jacobian with repsect to X_t
5731 . X      - Local solution vector
5732 . X_t    - Time-derivative of the local solution vector
5733 . Y      - Local input vector
5734 - user   - the user context
5735 
5736   Output Parameter:
5737 . Z - Local output vector
5738 
5739   Note:
5740   We form the residual one batch of elements at a time. This allows us to offload work onto an accelerator,
5741   like a GPU, or vectorize on a multicore machine.
5742 */
5743 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)
5744 {
5745   DM_Plex        *mesh  = (DM_Plex *)dm->data;
5746   const char     *name  = "Jacobian";
5747   DM              dmAux = NULL, plex, plexAux = NULL;
5748   DMEnclosureType encAux;
5749   Vec             A;
5750   DMField         coordField;
5751   PetscDS         prob, probAux = NULL;
5752   PetscQuadrature quad;
5753   PetscSection    section, globalSection, sectionAux;
5754   PetscScalar    *elemMat, *elemMatD, *u, *u_t, *a = NULL, *y, *z;
5755   const PetscInt *cells;
5756   PetscInt        Nf, fieldI, fieldJ;
5757   PetscInt        totDim, totDimAux = 0, cStart, cEnd, numCells, c;
5758   PetscBool       hasDyn;
5759 
5760   PetscFunctionBegin;
5761   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
5762   PetscCall(DMConvert(dm, DMPLEX, &plex));
5763   if (!cellIS) {
5764     PetscInt depth;
5765 
5766     PetscCall(DMPlexGetDepth(plex, &depth));
5767     PetscCall(DMGetStratumIS(plex, "dim", depth, &cellIS));
5768     if (!cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, &cellIS));
5769   } else {
5770     PetscCall(PetscObjectReference((PetscObject)cellIS));
5771   }
5772   PetscCall(ISGetLocalSize(cellIS, &numCells));
5773   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
5774   PetscCall(DMGetLocalSection(dm, &section));
5775   PetscCall(DMGetGlobalSection(dm, &globalSection));
5776   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob));
5777   PetscCall(PetscDSGetNumFields(prob, &Nf));
5778   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
5779   PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
5780   hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
5781   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &A));
5782   if (A) {
5783     PetscCall(VecGetDM(A, &dmAux));
5784     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
5785     PetscCall(DMConvert(dmAux, DMPLEX, &plexAux));
5786     PetscCall(DMGetLocalSection(plexAux, &sectionAux));
5787     PetscCall(DMGetDS(dmAux, &probAux));
5788     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
5789   }
5790   PetscCall(VecSet(Z, 0.0));
5791   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));
5792   if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
5793   PetscCall(DMGetCoordinateField(dm, &coordField));
5794   for (c = cStart; c < cEnd; ++c) {
5795     const PetscInt cell = cells ? cells[c] : c;
5796     const PetscInt cind = c - cStart;
5797     PetscScalar   *x = NULL, *x_t = NULL;
5798     PetscInt       i;
5799 
5800     PetscCall(DMPlexVecGetClosure(plex, section, X, cell, NULL, &x));
5801     for (i = 0; i < totDim; ++i) u[cind * totDim + i] = x[i];
5802     PetscCall(DMPlexVecRestoreClosure(plex, section, X, cell, NULL, &x));
5803     if (X_t) {
5804       PetscCall(DMPlexVecGetClosure(plex, section, X_t, cell, NULL, &x_t));
5805       for (i = 0; i < totDim; ++i) u_t[cind * totDim + i] = x_t[i];
5806       PetscCall(DMPlexVecRestoreClosure(plex, section, X_t, cell, NULL, &x_t));
5807     }
5808     if (dmAux) {
5809       PetscInt subcell;
5810       PetscCall(DMGetEnclosurePoint(dmAux, dm, encAux, cell, &subcell));
5811       PetscCall(DMPlexVecGetClosure(plexAux, sectionAux, A, subcell, NULL, &x));
5812       for (i = 0; i < totDimAux; ++i) a[cind * totDimAux + i] = x[i];
5813       PetscCall(DMPlexVecRestoreClosure(plexAux, sectionAux, A, subcell, NULL, &x));
5814     }
5815     PetscCall(DMPlexVecGetClosure(plex, section, Y, cell, NULL, &x));
5816     for (i = 0; i < totDim; ++i) y[cind * totDim + i] = x[i];
5817     PetscCall(DMPlexVecRestoreClosure(plex, section, Y, cell, NULL, &x));
5818   }
5819   PetscCall(PetscArrayzero(elemMat, numCells * totDim * totDim));
5820   if (hasDyn) PetscCall(PetscArrayzero(elemMatD, numCells * totDim * totDim));
5821   for (fieldI = 0; fieldI < Nf; ++fieldI) {
5822     PetscFE  fe;
5823     PetscInt Nb;
5824     /* Conforming batches */
5825     PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
5826     /* Remainder */
5827     PetscInt        Nr, offset, Nq;
5828     PetscQuadrature qGeom = NULL;
5829     PetscInt        maxDegree;
5830     PetscFEGeom    *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL;
5831 
5832     PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
5833     PetscCall(PetscFEGetQuadrature(fe, &quad));
5834     PetscCall(PetscFEGetDimension(fe, &Nb));
5835     PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5836     PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
5837     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
5838     if (!qGeom) {
5839       PetscCall(PetscFEGetQuadrature(fe, &qGeom));
5840       PetscCall(PetscObjectReference((PetscObject)qGeom));
5841     }
5842     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
5843     PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
5844     blockSize = Nb;
5845     batchSize = numBlocks * blockSize;
5846     PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5847     numChunks = numCells / (numBatches * batchSize);
5848     Ne        = numChunks * numBatches * batchSize;
5849     Nr        = numCells % (numBatches * batchSize);
5850     offset    = numCells - Nr;
5851     PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
5852     PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &remGeom));
5853     for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5854       key.field = fieldI * Nf + fieldJ;
5855       PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat));
5856       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]));
5857       if (hasDyn) {
5858         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatD));
5859         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]));
5860       }
5861     }
5862     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &remGeom));
5863     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, 0, offset, &chunkGeom));
5864     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
5865     PetscCall(PetscQuadratureDestroy(&qGeom));
5866   }
5867   if (hasDyn) {
5868     for (c = 0; c < numCells * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
5869   }
5870   for (c = cStart; c < cEnd; ++c) {
5871     const PetscInt     cell = cells ? cells[c] : c;
5872     const PetscInt     cind = c - cStart;
5873     const PetscBLASInt M = totDim, one = 1;
5874     const PetscScalar  a = 1.0, b = 0.0;
5875 
5876     PetscCallBLAS("BLASgemv", BLASgemv_("N", &M, &M, &a, &elemMat[cind * totDim * totDim], &M, &y[cind * totDim], &one, &b, z, &one));
5877     if (mesh->printFEM > 1) {
5878       PetscCall(DMPrintCellMatrix(c, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
5879       PetscCall(DMPrintCellVector(c, "Y", totDim, &y[cind * totDim]));
5880       PetscCall(DMPrintCellVector(c, "Z", totDim, z));
5881     }
5882     PetscCall(DMPlexVecSetClosure(dm, section, Z, cell, z, ADD_VALUES));
5883   }
5884   PetscCall(PetscFree6(u, u_t, elemMat, elemMatD, y, z));
5885   if (mesh->printFEM) {
5886     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)Z), "Z:\n"));
5887     PetscCall(VecView(Z, NULL));
5888   }
5889   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
5890   PetscCall(PetscFree(a));
5891   PetscCall(ISDestroy(&cellIS));
5892   PetscCall(DMDestroy(&plexAux));
5893   PetscCall(DMDestroy(&plex));
5894   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
5895   PetscFunctionReturn(0);
5896 }
5897