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