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