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