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