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