xref: /petsc/src/dm/impls/plex/plexfem.c (revision 0318f8a0986eeef85110e2c009122da1f76c5cfa)
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;
2436   PetscScalar *cintegral, *af;
2437   PetscInt     Nf, f, cellHeight, cStart, cEnd, cell;
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(VecGetDM(F, &dmF));
2460   PetscCall(DMGetLocalSection(dmF, &sectionF));
2461   PetscCall(VecGetArray(F, &af));
2462   printFEM = ((DM_Plex *)dm->data)->printFEM;
2463   for (cell = cStart; cell < cEnd; ++cell) {
2464     const PetscInt c = cell - cStart;
2465     PetscInt       dof, off;
2466 
2467     if (printFEM > 1) PetscCall(DMPrintCellVector(cell, "Cell Integral", Nf, &cintegral[c * Nf]));
2468     PetscCall(PetscSectionGetDof(sectionF, cell, &dof));
2469     PetscCall(PetscSectionGetOffset(sectionF, cell, &off));
2470     PetscCheck(dof == Nf, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "The number of cell dofs %" PetscInt_FMT " != %" PetscInt_FMT, dof, Nf);
2471     for (f = 0; f < Nf; ++f) af[off + f] = cintegral[c * Nf + f];
2472   }
2473   PetscCall(VecRestoreArray(F, &af));
2474   PetscCall(PetscFree(cintegral));
2475   PetscCall(PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2476   PetscCall(DMDestroy(&dm));
2477   PetscFunctionReturn(PETSC_SUCCESS);
2478 }
2479 
2480 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)
2481 {
2482   DM                 plex = NULL, plexA = NULL;
2483   DMEnclosureType    encAux;
2484   PetscDS            prob, probAux       = NULL;
2485   PetscSection       section, sectionAux = NULL;
2486   Vec                locA = NULL;
2487   DMField            coordField;
2488   PetscInt           Nf, totDim, *uOff, *uOff_x;
2489   PetscInt           NfAux = 0, totDimAux = 0, *aOff = NULL;
2490   PetscScalar       *u, *a = NULL;
2491   const PetscScalar *constants;
2492   PetscInt           numConstants, f;
2493 
2494   PetscFunctionBegin;
2495   PetscCall(DMGetCoordinateField(dm, &coordField));
2496   PetscCall(DMConvert(dm, DMPLEX, &plex));
2497   PetscCall(DMGetDS(dm, &prob));
2498   PetscCall(DMGetLocalSection(dm, &section));
2499   PetscCall(PetscSectionGetNumFields(section, &Nf));
2500   /* Determine which discretizations we have */
2501   for (f = 0; f < Nf; ++f) {
2502     PetscObject  obj;
2503     PetscClassId id;
2504 
2505     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
2506     PetscCall(PetscObjectGetClassId(obj, &id));
2507     PetscCheck(id != PETSCFV_CLASSID, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Not supported for FVM (field %" PetscInt_FMT ")", f);
2508   }
2509   /* Read DS information */
2510   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
2511   PetscCall(PetscDSGetComponentOffsets(prob, &uOff));
2512   PetscCall(PetscDSGetComponentDerivativeOffsets(prob, &uOff_x));
2513   PetscCall(PetscDSGetConstants(prob, &numConstants, &constants));
2514   /* Read Auxiliary DS information */
2515   PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA));
2516   if (locA) {
2517     DM dmAux;
2518 
2519     PetscCall(VecGetDM(locA, &dmAux));
2520     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
2521     PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
2522     PetscCall(DMGetDS(dmAux, &probAux));
2523     PetscCall(PetscDSGetNumFields(probAux, &NfAux));
2524     PetscCall(DMGetLocalSection(dmAux, &sectionAux));
2525     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
2526     PetscCall(PetscDSGetComponentOffsets(probAux, &aOff));
2527   }
2528   /* Integrate over points */
2529   {
2530     PetscFEGeom    *fgeom, *chunkGeom = NULL;
2531     PetscInt        maxDegree;
2532     PetscQuadrature qGeom = NULL;
2533     const PetscInt *points;
2534     PetscInt        numFaces, face, Nq, field;
2535     PetscInt        numChunks, chunkSize, chunk, Nr, offset;
2536 
2537     PetscCall(ISGetLocalSize(pointIS, &numFaces));
2538     PetscCall(ISGetIndices(pointIS, &points));
2539     PetscCall(PetscCalloc2(numFaces * totDim, &u, locA ? numFaces * totDimAux : 0, &a));
2540     PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
2541     for (field = 0; field < Nf; ++field) {
2542       PetscFE fe;
2543 
2544       PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fe));
2545       if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom));
2546       if (!qGeom) {
2547         PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
2548         PetscCall(PetscObjectReference((PetscObject)qGeom));
2549       }
2550       PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
2551       PetscCall(DMPlexGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
2552       for (face = 0; face < numFaces; ++face) {
2553         const PetscInt point = points[face], *support;
2554         PetscScalar   *x     = NULL;
2555         PetscInt       i;
2556 
2557         PetscCall(DMPlexGetSupport(dm, point, &support));
2558         PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
2559         for (i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
2560         PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
2561         if (locA) {
2562           PetscInt subp;
2563           PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
2564           PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
2565           for (i = 0; i < totDimAux; ++i) a[f * totDimAux + i] = x[i];
2566           PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
2567         }
2568       }
2569       /* Get blocking */
2570       {
2571         PetscQuadrature q;
2572         PetscInt        numBatches, batchSize, numBlocks, blockSize;
2573         PetscInt        Nq, Nb;
2574 
2575         PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
2576         PetscCall(PetscFEGetQuadrature(fe, &q));
2577         PetscCall(PetscQuadratureGetData(q, NULL, NULL, &Nq, NULL, NULL));
2578         PetscCall(PetscFEGetDimension(fe, &Nb));
2579         blockSize = Nb * Nq;
2580         batchSize = numBlocks * blockSize;
2581         chunkSize = numBatches * batchSize;
2582         PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
2583         numChunks = numFaces / chunkSize;
2584         Nr        = numFaces % chunkSize;
2585         offset    = numFaces - Nr;
2586       }
2587       /* Do integration for each field */
2588       for (chunk = 0; chunk < numChunks; ++chunk) {
2589         PetscCall(PetscFEGeomGetChunk(fgeom, chunk * chunkSize, (chunk + 1) * chunkSize, &chunkGeom));
2590         PetscCall(PetscFEIntegrateBd(prob, field, func, chunkSize, chunkGeom, u, probAux, a, fintegral));
2591         PetscCall(PetscFEGeomRestoreChunk(fgeom, 0, offset, &chunkGeom));
2592       }
2593       PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom));
2594       PetscCall(PetscFEIntegrateBd(prob, field, func, Nr, chunkGeom, &u[offset * totDim], probAux, PetscSafePointerPlusOffset(a, offset * totDimAux), &fintegral[offset * Nf]));
2595       PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom));
2596       /* Cleanup data arrays */
2597       PetscCall(DMPlexRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
2598       PetscCall(PetscQuadratureDestroy(&qGeom));
2599       PetscCall(PetscFree2(u, a));
2600       PetscCall(ISRestoreIndices(pointIS, &points));
2601     }
2602   }
2603   if (plex) PetscCall(DMDestroy(&plex));
2604   if (plexA) PetscCall(DMDestroy(&plexA));
2605   PetscFunctionReturn(PETSC_SUCCESS);
2606 }
2607 
2608 /*@C
2609   DMPlexComputeBdIntegral - Form the integral over the specified boundary from the global input X using pointwise functions specified by the user
2610 
2611   Input Parameters:
2612 + dm      - The mesh
2613 . X       - Global input vector
2614 . label   - The boundary `DMLabel`
2615 . numVals - The number of label values to use, or `PETSC_DETERMINE` for all values
2616 . vals    - The label values to use, or NULL for all values
2617 . func    - The function to integrate along the boundary
2618 - user    - The user context
2619 
2620   Output Parameter:
2621 . integral - Integral for each field
2622 
2623   Level: developer
2624 
2625 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeIntegralFEM()`, `DMPlexComputeBdResidualFEM()`
2626 @*/
2627 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)
2628 {
2629   Vec          locX;
2630   PetscSection section;
2631   DMLabel      depthLabel;
2632   IS           facetIS;
2633   PetscInt     dim, Nf, f, v;
2634 
2635   PetscFunctionBegin;
2636   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2637   PetscValidHeaderSpecific(X, VEC_CLASSID, 2);
2638   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 3);
2639   if (vals) PetscAssertPointer(vals, 5);
2640   PetscAssertPointer(integral, 7);
2641   PetscCall(PetscLogEventBegin(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2642   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
2643   PetscCall(DMGetDimension(dm, &dim));
2644   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
2645   PetscCall(DMGetLocalSection(dm, &section));
2646   PetscCall(PetscSectionGetNumFields(section, &Nf));
2647   /* Get local solution with boundary values */
2648   PetscCall(DMGetLocalVector(dm, &locX));
2649   PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL));
2650   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX));
2651   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX));
2652   /* Loop over label values */
2653   PetscCall(PetscArrayzero(integral, Nf));
2654   for (v = 0; v < numVals; ++v) {
2655     IS           pointIS;
2656     PetscInt     numFaces, face;
2657     PetscScalar *fintegral;
2658 
2659     PetscCall(DMLabelGetStratumIS(label, vals[v], &pointIS));
2660     if (!pointIS) continue; /* No points with that id on this process */
2661     {
2662       IS isectIS;
2663 
2664       /* TODO: Special cases of ISIntersect where it is quick to check a priori if one is a superset of the other */
2665       PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS));
2666       PetscCall(ISDestroy(&pointIS));
2667       pointIS = isectIS;
2668     }
2669     PetscCall(ISGetLocalSize(pointIS, &numFaces));
2670     PetscCall(PetscCalloc1(numFaces * Nf, &fintegral));
2671     PetscCall(DMPlexComputeBdIntegral_Internal(dm, locX, pointIS, func, fintegral, user));
2672     /* Sum point contributions into integral */
2673     for (f = 0; f < Nf; ++f)
2674       for (face = 0; face < numFaces; ++face) integral[f] += fintegral[face * Nf + f];
2675     PetscCall(PetscFree(fintegral));
2676     PetscCall(ISDestroy(&pointIS));
2677   }
2678   PetscCall(DMRestoreLocalVector(dm, &locX));
2679   PetscCall(ISDestroy(&facetIS));
2680   PetscCall(PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2681   PetscFunctionReturn(PETSC_SUCCESS);
2682 }
2683 
2684 /*@
2685   DMPlexComputeInterpolatorNested - Form the local portion of the interpolation matrix from the coarse `DM` to a uniformly refined `DM`.
2686 
2687   Input Parameters:
2688 + dmc       - The coarse mesh
2689 . dmf       - The fine mesh
2690 . isRefined - Flag indicating regular refinement, rather than the same topology
2691 - user      - The user context
2692 
2693   Output Parameter:
2694 . In - The interpolation matrix
2695 
2696   Level: developer
2697 
2698 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorGeneral()`, `DMPlexComputeJacobianFEM()`
2699 @*/
2700 PetscErrorCode DMPlexComputeInterpolatorNested(DM dmc, DM dmf, PetscBool isRefined, Mat In, void *user)
2701 {
2702   DM_Plex     *mesh = (DM_Plex *)dmc->data;
2703   const char  *name = "Interpolator";
2704   PetscFE     *feRef;
2705   PetscFV     *fvRef;
2706   PetscSection fsection, fglobalSection;
2707   PetscSection csection, cglobalSection;
2708   PetscScalar *elemMat;
2709   PetscInt     dim, Nf, f, fieldI, fieldJ, offsetI, offsetJ, cStart, cEnd, c;
2710   PetscInt     cTotDim = 0, rTotDim = 0;
2711 
2712   PetscFunctionBegin;
2713   PetscCall(PetscLogEventBegin(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
2714   PetscCall(DMGetDimension(dmf, &dim));
2715   PetscCall(DMGetLocalSection(dmf, &fsection));
2716   PetscCall(DMGetGlobalSection(dmf, &fglobalSection));
2717   PetscCall(DMGetLocalSection(dmc, &csection));
2718   PetscCall(DMGetGlobalSection(dmc, &cglobalSection));
2719   PetscCall(PetscSectionGetNumFields(fsection, &Nf));
2720   PetscCall(DMPlexGetSimplexOrBoxCells(dmc, 0, &cStart, &cEnd));
2721   PetscCall(PetscCalloc2(Nf, &feRef, Nf, &fvRef));
2722   for (f = 0; f < Nf; ++f) {
2723     PetscObject  obj, objc;
2724     PetscClassId id, idc;
2725     PetscInt     rNb = 0, Nc = 0, cNb = 0;
2726 
2727     PetscCall(DMGetField(dmf, f, NULL, &obj));
2728     PetscCall(PetscObjectGetClassId(obj, &id));
2729     if (id == PETSCFE_CLASSID) {
2730       PetscFE fe = (PetscFE)obj;
2731 
2732       if (isRefined) {
2733         PetscCall(PetscFERefine(fe, &feRef[f]));
2734       } else {
2735         PetscCall(PetscObjectReference((PetscObject)fe));
2736         feRef[f] = fe;
2737       }
2738       PetscCall(PetscFEGetDimension(feRef[f], &rNb));
2739       PetscCall(PetscFEGetNumComponents(fe, &Nc));
2740     } else if (id == PETSCFV_CLASSID) {
2741       PetscFV        fv = (PetscFV)obj;
2742       PetscDualSpace Q;
2743 
2744       if (isRefined) {
2745         PetscCall(PetscFVRefine(fv, &fvRef[f]));
2746       } else {
2747         PetscCall(PetscObjectReference((PetscObject)fv));
2748         fvRef[f] = fv;
2749       }
2750       PetscCall(PetscFVGetDualSpace(fvRef[f], &Q));
2751       PetscCall(PetscDualSpaceGetDimension(Q, &rNb));
2752       PetscCall(PetscFVGetDualSpace(fv, &Q));
2753       PetscCall(PetscFVGetNumComponents(fv, &Nc));
2754     }
2755     PetscCall(DMGetField(dmc, f, NULL, &objc));
2756     PetscCall(PetscObjectGetClassId(objc, &idc));
2757     if (idc == PETSCFE_CLASSID) {
2758       PetscFE fe = (PetscFE)objc;
2759 
2760       PetscCall(PetscFEGetDimension(fe, &cNb));
2761     } else if (id == PETSCFV_CLASSID) {
2762       PetscFV        fv = (PetscFV)obj;
2763       PetscDualSpace Q;
2764 
2765       PetscCall(PetscFVGetDualSpace(fv, &Q));
2766       PetscCall(PetscDualSpaceGetDimension(Q, &cNb));
2767     }
2768     rTotDim += rNb;
2769     cTotDim += cNb;
2770   }
2771   PetscCall(PetscMalloc1(rTotDim * cTotDim, &elemMat));
2772   PetscCall(PetscArrayzero(elemMat, rTotDim * cTotDim));
2773   for (fieldI = 0, offsetI = 0; fieldI < Nf; ++fieldI) {
2774     PetscDualSpace   Qref;
2775     PetscQuadrature  f;
2776     const PetscReal *qpoints, *qweights;
2777     PetscReal       *points;
2778     PetscInt         npoints = 0, Nc, Np, fpdim, i, k, p, d;
2779 
2780     /* Compose points from all dual basis functionals */
2781     if (feRef[fieldI]) {
2782       PetscCall(PetscFEGetDualSpace(feRef[fieldI], &Qref));
2783       PetscCall(PetscFEGetNumComponents(feRef[fieldI], &Nc));
2784     } else {
2785       PetscCall(PetscFVGetDualSpace(fvRef[fieldI], &Qref));
2786       PetscCall(PetscFVGetNumComponents(fvRef[fieldI], &Nc));
2787     }
2788     PetscCall(PetscDualSpaceGetDimension(Qref, &fpdim));
2789     for (i = 0; i < fpdim; ++i) {
2790       PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
2791       PetscCall(PetscQuadratureGetData(f, NULL, NULL, &Np, NULL, NULL));
2792       npoints += Np;
2793     }
2794     PetscCall(PetscMalloc1(npoints * dim, &points));
2795     for (i = 0, k = 0; i < fpdim; ++i) {
2796       PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
2797       PetscCall(PetscQuadratureGetData(f, NULL, NULL, &Np, &qpoints, NULL));
2798       for (p = 0; p < Np; ++p, ++k)
2799         for (d = 0; d < dim; ++d) points[k * dim + d] = qpoints[p * dim + d];
2800     }
2801 
2802     for (fieldJ = 0, offsetJ = 0; fieldJ < Nf; ++fieldJ) {
2803       PetscObject  obj;
2804       PetscClassId id;
2805       PetscInt     NcJ = 0, cpdim = 0, j, qNc;
2806 
2807       PetscCall(DMGetField(dmc, fieldJ, NULL, &obj));
2808       PetscCall(PetscObjectGetClassId(obj, &id));
2809       if (id == PETSCFE_CLASSID) {
2810         PetscFE         fe = (PetscFE)obj;
2811         PetscTabulation T  = NULL;
2812 
2813         /* Evaluate basis at points */
2814         PetscCall(PetscFEGetNumComponents(fe, &NcJ));
2815         PetscCall(PetscFEGetDimension(fe, &cpdim));
2816         /* For now, fields only interpolate themselves */
2817         if (fieldI == fieldJ) {
2818           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);
2819           PetscCall(PetscFECreateTabulation(fe, 1, npoints, points, 0, &T));
2820           for (i = 0, k = 0; i < fpdim; ++i) {
2821             PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
2822             PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, NULL, &qweights));
2823             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);
2824             for (p = 0; p < Np; ++p, ++k) {
2825               for (j = 0; j < cpdim; ++j) {
2826                 /*
2827                    cTotDim:            Total columns in element interpolation matrix, sum of number of dual basis functionals in each field
2828                    offsetI, offsetJ:   Offsets into the larger element interpolation matrix for different fields
2829                    fpdim, i, cpdim, j: Dofs for fine and coarse grids, correspond to dual space basis functionals
2830                    qNC, Nc, Ncj, c:    Number of components in this field
2831                    Np, p:              Number of quad points in the fine grid functional i
2832                    k:                  i*Np + p, overall point number for the interpolation
2833                 */
2834                 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];
2835               }
2836             }
2837           }
2838           PetscCall(PetscTabulationDestroy(&T));
2839         }
2840       } else if (id == PETSCFV_CLASSID) {
2841         PetscFV fv = (PetscFV)obj;
2842 
2843         /* Evaluate constant function at points */
2844         PetscCall(PetscFVGetNumComponents(fv, &NcJ));
2845         cpdim = 1;
2846         /* For now, fields only interpolate themselves */
2847         if (fieldI == fieldJ) {
2848           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);
2849           for (i = 0, k = 0; i < fpdim; ++i) {
2850             PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
2851             PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, NULL, &qweights));
2852             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);
2853             for (p = 0; p < Np; ++p, ++k) {
2854               for (j = 0; j < cpdim; ++j) {
2855                 for (c = 0; c < Nc; ++c) elemMat[(offsetI + i) * cTotDim + offsetJ + j] += 1.0 * qweights[p * qNc + c];
2856               }
2857             }
2858           }
2859         }
2860       }
2861       offsetJ += cpdim;
2862     }
2863     offsetI += fpdim;
2864     PetscCall(PetscFree(points));
2865   }
2866   if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(0, name, rTotDim, cTotDim, elemMat));
2867   /* Preallocate matrix */
2868   {
2869     Mat          preallocator;
2870     PetscScalar *vals;
2871     PetscInt    *cellCIndices, *cellFIndices;
2872     PetscInt     locRows, locCols, cell;
2873 
2874     PetscCall(MatGetLocalSize(In, &locRows, &locCols));
2875     PetscCall(MatCreate(PetscObjectComm((PetscObject)In), &preallocator));
2876     PetscCall(MatSetType(preallocator, MATPREALLOCATOR));
2877     PetscCall(MatSetSizes(preallocator, locRows, locCols, PETSC_DETERMINE, PETSC_DETERMINE));
2878     PetscCall(MatSetUp(preallocator));
2879     PetscCall(PetscCalloc3(rTotDim * cTotDim, &vals, cTotDim, &cellCIndices, rTotDim, &cellFIndices));
2880     for (cell = cStart; cell < cEnd; ++cell) {
2881       if (isRefined) {
2882         PetscCall(DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, cell, cellCIndices, cellFIndices));
2883         PetscCall(MatSetValues(preallocator, rTotDim, cellFIndices, cTotDim, cellCIndices, vals, INSERT_VALUES));
2884       } else {
2885         PetscCall(DMPlexMatSetClosureGeneral(dmf, fsection, fglobalSection, PETSC_FALSE, dmc, csection, cglobalSection, PETSC_FALSE, preallocator, cell, vals, INSERT_VALUES));
2886       }
2887     }
2888     PetscCall(PetscFree3(vals, cellCIndices, cellFIndices));
2889     PetscCall(MatAssemblyBegin(preallocator, MAT_FINAL_ASSEMBLY));
2890     PetscCall(MatAssemblyEnd(preallocator, MAT_FINAL_ASSEMBLY));
2891     PetscCall(MatPreallocatorPreallocate(preallocator, PETSC_TRUE, In));
2892     PetscCall(MatDestroy(&preallocator));
2893   }
2894   /* Fill matrix */
2895   PetscCall(MatZeroEntries(In));
2896   for (c = cStart; c < cEnd; ++c) {
2897     if (isRefined) {
2898       PetscCall(DMPlexMatSetClosureRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, In, c, elemMat, INSERT_VALUES));
2899     } else {
2900       PetscCall(DMPlexMatSetClosureGeneral(dmf, fsection, fglobalSection, PETSC_FALSE, dmc, csection, cglobalSection, PETSC_FALSE, In, c, elemMat, INSERT_VALUES));
2901     }
2902   }
2903   for (f = 0; f < Nf; ++f) PetscCall(PetscFEDestroy(&feRef[f]));
2904   PetscCall(PetscFree2(feRef, fvRef));
2905   PetscCall(PetscFree(elemMat));
2906   PetscCall(MatAssemblyBegin(In, MAT_FINAL_ASSEMBLY));
2907   PetscCall(MatAssemblyEnd(In, MAT_FINAL_ASSEMBLY));
2908   if (mesh->printFEM > 1) {
2909     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)In), "%s:\n", name));
2910     PetscCall(MatFilter(In, 1.0e-10, PETSC_FALSE, PETSC_FALSE));
2911     PetscCall(MatView(In, NULL));
2912   }
2913   PetscCall(PetscLogEventEnd(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
2914   PetscFunctionReturn(PETSC_SUCCESS);
2915 }
2916 
2917 PetscErrorCode DMPlexComputeMassMatrixNested(DM dmc, DM dmf, Mat mass, void *user)
2918 {
2919   SETERRQ(PetscObjectComm((PetscObject)dmc), PETSC_ERR_SUP, "Laziness");
2920 }
2921 
2922 /*@
2923   DMPlexComputeInterpolatorGeneral - Form the local portion of the interpolation matrix from the coarse `DM` to a non-nested fine `DM`.
2924 
2925   Input Parameters:
2926 + dmf  - The fine mesh
2927 . dmc  - The coarse mesh
2928 - user - The user context
2929 
2930   Output Parameter:
2931 . In - The interpolation matrix
2932 
2933   Level: developer
2934 
2935 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorNested()`, `DMPlexComputeJacobianFEM()`
2936 @*/
2937 PetscErrorCode DMPlexComputeInterpolatorGeneral(DM dmc, DM dmf, Mat In, void *user)
2938 {
2939   DM_Plex     *mesh = (DM_Plex *)dmf->data;
2940   const char  *name = "Interpolator";
2941   PetscDS      prob;
2942   Mat          interp;
2943   PetscSection fsection, globalFSection;
2944   PetscSection csection, globalCSection;
2945   PetscInt     locRows, locCols;
2946   PetscReal   *x, *v0, *J, *invJ, detJ;
2947   PetscReal   *v0c, *Jc, *invJc, detJc;
2948   PetscScalar *elemMat;
2949   PetscInt     dim, Nf, field, totDim, cStart, cEnd, cell, ccell, s;
2950 
2951   PetscFunctionBegin;
2952   PetscCall(PetscLogEventBegin(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
2953   PetscCall(DMGetCoordinateDim(dmc, &dim));
2954   PetscCall(DMGetDS(dmc, &prob));
2955   PetscCall(PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL));
2956   PetscCall(PetscDSGetNumFields(prob, &Nf));
2957   PetscCall(PetscMalloc3(dim, &v0, dim * dim, &J, dim * dim, &invJ));
2958   PetscCall(PetscMalloc3(dim, &v0c, dim * dim, &Jc, dim * dim, &invJc));
2959   PetscCall(DMGetLocalSection(dmf, &fsection));
2960   PetscCall(DMGetGlobalSection(dmf, &globalFSection));
2961   PetscCall(DMGetLocalSection(dmc, &csection));
2962   PetscCall(DMGetGlobalSection(dmc, &globalCSection));
2963   PetscCall(DMPlexGetSimplexOrBoxCells(dmf, 0, &cStart, &cEnd));
2964   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
2965   PetscCall(PetscMalloc1(totDim, &elemMat));
2966 
2967   PetscCall(MatGetLocalSize(In, &locRows, &locCols));
2968   PetscCall(MatCreate(PetscObjectComm((PetscObject)In), &interp));
2969   PetscCall(MatSetType(interp, MATPREALLOCATOR));
2970   PetscCall(MatSetSizes(interp, locRows, locCols, PETSC_DETERMINE, PETSC_DETERMINE));
2971   PetscCall(MatSetUp(interp));
2972   for (s = 0; s < 2; ++s) {
2973     for (field = 0; field < Nf; ++field) {
2974       PetscObject      obj;
2975       PetscClassId     id;
2976       PetscDualSpace   Q = NULL;
2977       PetscTabulation  T = NULL;
2978       PetscQuadrature  f;
2979       const PetscReal *qpoints, *qweights;
2980       PetscInt         Nc, qNc, Np, fpdim, off, i, d;
2981 
2982       PetscCall(PetscDSGetFieldOffset(prob, field, &off));
2983       PetscCall(PetscDSGetDiscretization(prob, field, &obj));
2984       PetscCall(PetscObjectGetClassId(obj, &id));
2985       if (id == PETSCFE_CLASSID) {
2986         PetscFE fe = (PetscFE)obj;
2987 
2988         PetscCall(PetscFEGetDualSpace(fe, &Q));
2989         PetscCall(PetscFEGetNumComponents(fe, &Nc));
2990         if (s) PetscCall(PetscFECreateTabulation(fe, 1, 1, x, 0, &T));
2991       } else if (id == PETSCFV_CLASSID) {
2992         PetscFV fv = (PetscFV)obj;
2993 
2994         PetscCall(PetscFVGetDualSpace(fv, &Q));
2995         Nc = 1;
2996       } else SETERRQ(PetscObjectComm((PetscObject)dmc), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
2997       PetscCall(PetscDualSpaceGetDimension(Q, &fpdim));
2998       /* For each fine grid cell */
2999       for (cell = cStart; cell < cEnd; ++cell) {
3000         PetscInt *findices, *cindices;
3001         PetscInt  numFIndices, numCIndices;
3002 
3003         PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3004         PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ));
3005         PetscCheck(numFIndices == totDim, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fine indices %" PetscInt_FMT " != %" PetscInt_FMT " dual basis vecs", numFIndices, totDim);
3006         for (i = 0; i < fpdim; ++i) {
3007           Vec                pointVec;
3008           PetscScalar       *pV;
3009           PetscSF            coarseCellSF = NULL;
3010           const PetscSFNode *coarseCells;
3011           PetscInt           numCoarseCells, cpdim, row = findices[i + off], q, c, j;
3012 
3013           /* Get points from the dual basis functional quadrature */
3014           PetscCall(PetscDualSpaceGetFunctional(Q, i, &f));
3015           PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, &qpoints, &qweights));
3016           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);
3017           PetscCall(VecCreateSeq(PETSC_COMM_SELF, Np * dim, &pointVec));
3018           PetscCall(VecSetBlockSize(pointVec, dim));
3019           PetscCall(VecGetArray(pointVec, &pV));
3020           for (q = 0; q < Np; ++q) {
3021             const PetscReal xi0[3] = {-1., -1., -1.};
3022 
3023             /* Transform point to real space */
3024             CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x);
3025             for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d];
3026           }
3027           PetscCall(VecRestoreArray(pointVec, &pV));
3028           /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
3029           /* OPT: Read this out from preallocation information */
3030           PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF));
3031           /* Update preallocation info */
3032           PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells));
3033           PetscCheck(numCoarseCells == Np, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located");
3034           PetscCall(VecGetArray(pointVec, &pV));
3035           for (ccell = 0; ccell < numCoarseCells; ++ccell) {
3036             PetscReal       pVReal[3];
3037             const PetscReal xi0[3] = {-1., -1., -1.};
3038 
3039             PetscCall(DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3040             if (id == PETSCFE_CLASSID) PetscCall(PetscFEGetDimension((PetscFE)obj, &cpdim));
3041             else cpdim = 1;
3042 
3043             if (s) {
3044               /* Transform points from real space to coarse reference space */
3045               PetscCall(DMPlexComputeCellGeometryFEM(dmc, coarseCells[ccell].index, NULL, v0c, Jc, invJc, &detJc));
3046               for (d = 0; d < dim; ++d) pVReal[d] = PetscRealPart(pV[ccell * dim + d]);
3047               CoordinatesRealToRef(dim, dim, xi0, v0c, invJc, pVReal, x);
3048 
3049               if (id == PETSCFE_CLASSID) {
3050                 /* Evaluate coarse basis on contained point */
3051                 PetscCall(PetscFEComputeTabulation((PetscFE)obj, 1, x, 0, T));
3052                 PetscCall(PetscArrayzero(elemMat, cpdim));
3053                 /* Get elemMat entries by multiplying by weight */
3054                 for (j = 0; j < cpdim; ++j) {
3055                   for (c = 0; c < Nc; ++c) elemMat[j] += T->T[0][j * Nc + c] * qweights[ccell * qNc + c];
3056                 }
3057               } else {
3058                 for (j = 0; j < cpdim; ++j) {
3059                   for (c = 0; c < Nc; ++c) elemMat[j] += 1.0 * qweights[ccell * qNc + c];
3060                 }
3061               }
3062               if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat));
3063             }
3064             /* Update interpolator */
3065             PetscCheck(numCIndices == totDim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, totDim);
3066             PetscCall(MatSetValues(interp, 1, &row, cpdim, &cindices[off], elemMat, INSERT_VALUES));
3067             PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3068           }
3069           PetscCall(VecRestoreArray(pointVec, &pV));
3070           PetscCall(PetscSFDestroy(&coarseCellSF));
3071           PetscCall(VecDestroy(&pointVec));
3072         }
3073         PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3074       }
3075       if (s && id == PETSCFE_CLASSID) PetscCall(PetscTabulationDestroy(&T));
3076     }
3077     if (!s) {
3078       PetscCall(MatAssemblyBegin(interp, MAT_FINAL_ASSEMBLY));
3079       PetscCall(MatAssemblyEnd(interp, MAT_FINAL_ASSEMBLY));
3080       PetscCall(MatPreallocatorPreallocate(interp, PETSC_TRUE, In));
3081       PetscCall(MatDestroy(&interp));
3082       interp = In;
3083     }
3084   }
3085   PetscCall(PetscFree3(v0, J, invJ));
3086   PetscCall(PetscFree3(v0c, Jc, invJc));
3087   PetscCall(PetscFree(elemMat));
3088   PetscCall(MatAssemblyBegin(In, MAT_FINAL_ASSEMBLY));
3089   PetscCall(MatAssemblyEnd(In, MAT_FINAL_ASSEMBLY));
3090   PetscCall(PetscLogEventEnd(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
3091   PetscFunctionReturn(PETSC_SUCCESS);
3092 }
3093 
3094 /*@
3095   DMPlexComputeMassMatrixGeneral - Form the local portion of the mass matrix from the coarse `DM` to a non-nested fine `DM`.
3096 
3097   Input Parameters:
3098 + dmf  - The fine mesh
3099 . dmc  - The coarse mesh
3100 - user - The user context
3101 
3102   Output Parameter:
3103 . mass - The mass matrix
3104 
3105   Level: developer
3106 
3107 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeMassMatrixNested()`, `DMPlexComputeInterpolatorNested()`, `DMPlexComputeInterpolatorGeneral()`, `DMPlexComputeJacobianFEM()`
3108 @*/
3109 PetscErrorCode DMPlexComputeMassMatrixGeneral(DM dmc, DM dmf, Mat mass, void *user)
3110 {
3111   DM_Plex     *mesh = (DM_Plex *)dmf->data;
3112   const char  *name = "Mass Matrix";
3113   PetscDS      prob;
3114   PetscSection fsection, csection, globalFSection, globalCSection;
3115   PetscHSetIJ  ht;
3116   PetscLayout  rLayout;
3117   PetscInt    *dnz, *onz;
3118   PetscInt     locRows, rStart, rEnd;
3119   PetscReal   *x, *v0, *J, *invJ, detJ;
3120   PetscReal   *v0c, *Jc, *invJc, detJc;
3121   PetscScalar *elemMat;
3122   PetscInt     dim, Nf, field, totDim, cStart, cEnd, cell, ccell;
3123 
3124   PetscFunctionBegin;
3125   PetscCall(DMGetCoordinateDim(dmc, &dim));
3126   PetscCall(DMGetDS(dmc, &prob));
3127   PetscCall(PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL));
3128   PetscCall(PetscDSGetNumFields(prob, &Nf));
3129   PetscCall(PetscMalloc3(dim, &v0, dim * dim, &J, dim * dim, &invJ));
3130   PetscCall(PetscMalloc3(dim, &v0c, dim * dim, &Jc, dim * dim, &invJc));
3131   PetscCall(DMGetLocalSection(dmf, &fsection));
3132   PetscCall(DMGetGlobalSection(dmf, &globalFSection));
3133   PetscCall(DMGetLocalSection(dmc, &csection));
3134   PetscCall(DMGetGlobalSection(dmc, &globalCSection));
3135   PetscCall(DMPlexGetHeightStratum(dmf, 0, &cStart, &cEnd));
3136   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
3137   PetscCall(PetscMalloc1(totDim, &elemMat));
3138 
3139   PetscCall(MatGetLocalSize(mass, &locRows, NULL));
3140   PetscCall(PetscLayoutCreate(PetscObjectComm((PetscObject)mass), &rLayout));
3141   PetscCall(PetscLayoutSetLocalSize(rLayout, locRows));
3142   PetscCall(PetscLayoutSetBlockSize(rLayout, 1));
3143   PetscCall(PetscLayoutSetUp(rLayout));
3144   PetscCall(PetscLayoutGetRange(rLayout, &rStart, &rEnd));
3145   PetscCall(PetscLayoutDestroy(&rLayout));
3146   PetscCall(PetscCalloc2(locRows, &dnz, locRows, &onz));
3147   PetscCall(PetscHSetIJCreate(&ht));
3148   for (field = 0; field < Nf; ++field) {
3149     PetscObject      obj;
3150     PetscClassId     id;
3151     PetscQuadrature  quad;
3152     const PetscReal *qpoints;
3153     PetscInt         Nq, Nc, i, d;
3154 
3155     PetscCall(PetscDSGetDiscretization(prob, field, &obj));
3156     PetscCall(PetscObjectGetClassId(obj, &id));
3157     if (id == PETSCFE_CLASSID) PetscCall(PetscFEGetQuadrature((PetscFE)obj, &quad));
3158     else PetscCall(PetscFVGetQuadrature((PetscFV)obj, &quad));
3159     PetscCall(PetscQuadratureGetData(quad, NULL, &Nc, &Nq, &qpoints, NULL));
3160     /* For each fine grid cell */
3161     for (cell = cStart; cell < cEnd; ++cell) {
3162       Vec                pointVec;
3163       PetscScalar       *pV;
3164       PetscSF            coarseCellSF = NULL;
3165       const PetscSFNode *coarseCells;
3166       PetscInt           numCoarseCells, q, c;
3167       PetscInt          *findices, *cindices;
3168       PetscInt           numFIndices, numCIndices;
3169 
3170       PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3171       PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ));
3172       /* Get points from the quadrature */
3173       PetscCall(VecCreateSeq(PETSC_COMM_SELF, Nq * dim, &pointVec));
3174       PetscCall(VecSetBlockSize(pointVec, dim));
3175       PetscCall(VecGetArray(pointVec, &pV));
3176       for (q = 0; q < Nq; ++q) {
3177         const PetscReal xi0[3] = {-1., -1., -1.};
3178 
3179         /* Transform point to real space */
3180         CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x);
3181         for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d];
3182       }
3183       PetscCall(VecRestoreArray(pointVec, &pV));
3184       /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
3185       PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF));
3186       PetscCall(PetscSFViewFromOptions(coarseCellSF, NULL, "-interp_sf_view"));
3187       /* Update preallocation info */
3188       PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells));
3189       PetscCheck(numCoarseCells == Nq, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located");
3190       {
3191         PetscHashIJKey key;
3192         PetscBool      missing;
3193 
3194         for (i = 0; i < numFIndices; ++i) {
3195           key.i = findices[i];
3196           if (key.i >= 0) {
3197             /* Get indices for coarse elements */
3198             for (ccell = 0; ccell < numCoarseCells; ++ccell) {
3199               PetscCall(DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3200               for (c = 0; c < numCIndices; ++c) {
3201                 key.j = cindices[c];
3202                 if (key.j < 0) continue;
3203                 PetscCall(PetscHSetIJQueryAdd(ht, key, &missing));
3204                 if (missing) {
3205                   if ((key.j >= rStart) && (key.j < rEnd)) ++dnz[key.i - rStart];
3206                   else ++onz[key.i - rStart];
3207                 }
3208               }
3209               PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3210             }
3211           }
3212         }
3213       }
3214       PetscCall(PetscSFDestroy(&coarseCellSF));
3215       PetscCall(VecDestroy(&pointVec));
3216       PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3217     }
3218   }
3219   PetscCall(PetscHSetIJDestroy(&ht));
3220   PetscCall(MatXAIJSetPreallocation(mass, 1, dnz, onz, NULL, NULL));
3221   PetscCall(MatSetOption(mass, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_TRUE));
3222   PetscCall(PetscFree2(dnz, onz));
3223   for (field = 0; field < Nf; ++field) {
3224     PetscObject      obj;
3225     PetscClassId     id;
3226     PetscTabulation  T, Tfine;
3227     PetscQuadrature  quad;
3228     const PetscReal *qpoints, *qweights;
3229     PetscInt         Nq, Nc, i, d;
3230 
3231     PetscCall(PetscDSGetDiscretization(prob, field, &obj));
3232     PetscCall(PetscObjectGetClassId(obj, &id));
3233     if (id == PETSCFE_CLASSID) {
3234       PetscCall(PetscFEGetQuadrature((PetscFE)obj, &quad));
3235       PetscCall(PetscFEGetCellTabulation((PetscFE)obj, 1, &Tfine));
3236       PetscCall(PetscFECreateTabulation((PetscFE)obj, 1, 1, x, 0, &T));
3237     } else {
3238       PetscCall(PetscFVGetQuadrature((PetscFV)obj, &quad));
3239     }
3240     PetscCall(PetscQuadratureGetData(quad, NULL, &Nc, &Nq, &qpoints, &qweights));
3241     /* For each fine grid cell */
3242     for (cell = cStart; cell < cEnd; ++cell) {
3243       Vec                pointVec;
3244       PetscScalar       *pV;
3245       PetscSF            coarseCellSF = NULL;
3246       const PetscSFNode *coarseCells;
3247       PetscInt           numCoarseCells, cpdim, q, c, j;
3248       PetscInt          *findices, *cindices;
3249       PetscInt           numFIndices, numCIndices;
3250 
3251       PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3252       PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ));
3253       /* Get points from the quadrature */
3254       PetscCall(VecCreateSeq(PETSC_COMM_SELF, Nq * dim, &pointVec));
3255       PetscCall(VecSetBlockSize(pointVec, dim));
3256       PetscCall(VecGetArray(pointVec, &pV));
3257       for (q = 0; q < Nq; ++q) {
3258         const PetscReal xi0[3] = {-1., -1., -1.};
3259 
3260         /* Transform point to real space */
3261         CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x);
3262         for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d];
3263       }
3264       PetscCall(VecRestoreArray(pointVec, &pV));
3265       /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
3266       PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF));
3267       /* Update matrix */
3268       PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells));
3269       PetscCheck(numCoarseCells == Nq, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located");
3270       PetscCall(VecGetArray(pointVec, &pV));
3271       for (ccell = 0; ccell < numCoarseCells; ++ccell) {
3272         PetscReal       pVReal[3];
3273         const PetscReal xi0[3] = {-1., -1., -1.};
3274 
3275         PetscCall(DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3276         /* Transform points from real space to coarse reference space */
3277         PetscCall(DMPlexComputeCellGeometryFEM(dmc, coarseCells[ccell].index, NULL, v0c, Jc, invJc, &detJc));
3278         for (d = 0; d < dim; ++d) pVReal[d] = PetscRealPart(pV[ccell * dim + d]);
3279         CoordinatesRealToRef(dim, dim, xi0, v0c, invJc, pVReal, x);
3280 
3281         if (id == PETSCFE_CLASSID) {
3282           PetscFE fe = (PetscFE)obj;
3283 
3284           /* Evaluate coarse basis on contained point */
3285           PetscCall(PetscFEGetDimension(fe, &cpdim));
3286           PetscCall(PetscFEComputeTabulation(fe, 1, x, 0, T));
3287           /* Get elemMat entries by multiplying by weight */
3288           for (i = 0; i < numFIndices; ++i) {
3289             PetscCall(PetscArrayzero(elemMat, cpdim));
3290             for (j = 0; j < cpdim; ++j) {
3291               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;
3292             }
3293             /* Update interpolator */
3294             if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat));
3295             PetscCheck(numCIndices == cpdim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, cpdim);
3296             PetscCall(MatSetValues(mass, 1, &findices[i], numCIndices, cindices, elemMat, ADD_VALUES));
3297           }
3298         } else {
3299           cpdim = 1;
3300           for (i = 0; i < numFIndices; ++i) {
3301             PetscCall(PetscArrayzero(elemMat, cpdim));
3302             for (j = 0; j < cpdim; ++j) {
3303               for (c = 0; c < Nc; ++c) elemMat[j] += 1.0 * 1.0 * qweights[ccell * Nc + c] * detJ;
3304             }
3305             /* Update interpolator */
3306             if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat));
3307             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));
3308             PetscCheck(numCIndices == cpdim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, cpdim);
3309             PetscCall(MatSetValues(mass, 1, &findices[i], numCIndices, cindices, elemMat, ADD_VALUES));
3310           }
3311         }
3312         PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3313       }
3314       PetscCall(VecRestoreArray(pointVec, &pV));
3315       PetscCall(PetscSFDestroy(&coarseCellSF));
3316       PetscCall(VecDestroy(&pointVec));
3317       PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3318     }
3319     if (id == PETSCFE_CLASSID) PetscCall(PetscTabulationDestroy(&T));
3320   }
3321   PetscCall(PetscFree3(v0, J, invJ));
3322   PetscCall(PetscFree3(v0c, Jc, invJc));
3323   PetscCall(PetscFree(elemMat));
3324   PetscCall(MatAssemblyBegin(mass, MAT_FINAL_ASSEMBLY));
3325   PetscCall(MatAssemblyEnd(mass, MAT_FINAL_ASSEMBLY));
3326   PetscFunctionReturn(PETSC_SUCCESS);
3327 }
3328 
3329 /*@
3330   DMPlexComputeInjectorFEM - Compute a mapping from coarse unknowns to fine unknowns
3331 
3332   Input Parameters:
3333 + dmc  - The coarse mesh
3334 . dmf  - The fine mesh
3335 - user - The user context
3336 
3337   Output Parameter:
3338 . sc - The mapping
3339 
3340   Level: developer
3341 
3342 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorNested()`, `DMPlexComputeJacobianFEM()`
3343 @*/
3344 PetscErrorCode DMPlexComputeInjectorFEM(DM dmc, DM dmf, VecScatter *sc, void *user)
3345 {
3346   PetscDS      prob;
3347   PetscFE     *feRef;
3348   PetscFV     *fvRef;
3349   Vec          fv, cv;
3350   IS           fis, cis;
3351   PetscSection fsection, fglobalSection, csection, cglobalSection;
3352   PetscInt    *cmap, *cellCIndices, *cellFIndices, *cindices, *findices;
3353   PetscInt     cTotDim, fTotDim = 0, Nf, f, field, cStart, cEnd, c, dim, d, startC, endC, offsetC, offsetF, m;
3354   PetscBool   *needAvg;
3355 
3356   PetscFunctionBegin;
3357   PetscCall(PetscLogEventBegin(DMPLEX_InjectorFEM, dmc, dmf, 0, 0));
3358   PetscCall(DMGetDimension(dmf, &dim));
3359   PetscCall(DMGetLocalSection(dmf, &fsection));
3360   PetscCall(DMGetGlobalSection(dmf, &fglobalSection));
3361   PetscCall(DMGetLocalSection(dmc, &csection));
3362   PetscCall(DMGetGlobalSection(dmc, &cglobalSection));
3363   PetscCall(PetscSectionGetNumFields(fsection, &Nf));
3364   PetscCall(DMPlexGetSimplexOrBoxCells(dmc, 0, &cStart, &cEnd));
3365   PetscCall(DMGetDS(dmc, &prob));
3366   PetscCall(PetscCalloc3(Nf, &feRef, Nf, &fvRef, Nf, &needAvg));
3367   for (f = 0; f < Nf; ++f) {
3368     PetscObject  obj;
3369     PetscClassId id;
3370     PetscInt     fNb = 0, Nc = 0;
3371 
3372     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
3373     PetscCall(PetscObjectGetClassId(obj, &id));
3374     if (id == PETSCFE_CLASSID) {
3375       PetscFE    fe = (PetscFE)obj;
3376       PetscSpace sp;
3377       PetscInt   maxDegree;
3378 
3379       PetscCall(PetscFERefine(fe, &feRef[f]));
3380       PetscCall(PetscFEGetDimension(feRef[f], &fNb));
3381       PetscCall(PetscFEGetNumComponents(fe, &Nc));
3382       PetscCall(PetscFEGetBasisSpace(fe, &sp));
3383       PetscCall(PetscSpaceGetDegree(sp, NULL, &maxDegree));
3384       if (!maxDegree) needAvg[f] = PETSC_TRUE;
3385     } else if (id == PETSCFV_CLASSID) {
3386       PetscFV        fv = (PetscFV)obj;
3387       PetscDualSpace Q;
3388 
3389       PetscCall(PetscFVRefine(fv, &fvRef[f]));
3390       PetscCall(PetscFVGetDualSpace(fvRef[f], &Q));
3391       PetscCall(PetscDualSpaceGetDimension(Q, &fNb));
3392       PetscCall(PetscFVGetNumComponents(fv, &Nc));
3393       needAvg[f] = PETSC_TRUE;
3394     }
3395     fTotDim += fNb;
3396   }
3397   PetscCall(PetscDSGetTotalDimension(prob, &cTotDim));
3398   PetscCall(PetscMalloc1(cTotDim, &cmap));
3399   for (field = 0, offsetC = 0, offsetF = 0; field < Nf; ++field) {
3400     PetscFE        feC;
3401     PetscFV        fvC;
3402     PetscDualSpace QF, QC;
3403     PetscInt       order = -1, NcF, NcC, fpdim, cpdim;
3404 
3405     if (feRef[field]) {
3406       PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&feC));
3407       PetscCall(PetscFEGetNumComponents(feC, &NcC));
3408       PetscCall(PetscFEGetNumComponents(feRef[field], &NcF));
3409       PetscCall(PetscFEGetDualSpace(feRef[field], &QF));
3410       PetscCall(PetscDualSpaceGetOrder(QF, &order));
3411       PetscCall(PetscDualSpaceGetDimension(QF, &fpdim));
3412       PetscCall(PetscFEGetDualSpace(feC, &QC));
3413       PetscCall(PetscDualSpaceGetDimension(QC, &cpdim));
3414     } else {
3415       PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fvC));
3416       PetscCall(PetscFVGetNumComponents(fvC, &NcC));
3417       PetscCall(PetscFVGetNumComponents(fvRef[field], &NcF));
3418       PetscCall(PetscFVGetDualSpace(fvRef[field], &QF));
3419       PetscCall(PetscDualSpaceGetDimension(QF, &fpdim));
3420       PetscCall(PetscFVGetDualSpace(fvC, &QC));
3421       PetscCall(PetscDualSpaceGetDimension(QC, &cpdim));
3422     }
3423     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);
3424     for (c = 0; c < cpdim; ++c) {
3425       PetscQuadrature  cfunc;
3426       const PetscReal *cqpoints, *cqweights;
3427       PetscInt         NqcC, NpC;
3428       PetscBool        found = PETSC_FALSE;
3429 
3430       PetscCall(PetscDualSpaceGetFunctional(QC, c, &cfunc));
3431       PetscCall(PetscQuadratureGetData(cfunc, NULL, &NqcC, &NpC, &cqpoints, &cqweights));
3432       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);
3433       PetscCheck(NpC == 1 || !feRef[field], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Do not know how to do injection for moments");
3434       for (f = 0; f < fpdim; ++f) {
3435         PetscQuadrature  ffunc;
3436         const PetscReal *fqpoints, *fqweights;
3437         PetscReal        sum = 0.0;
3438         PetscInt         NqcF, NpF;
3439 
3440         PetscCall(PetscDualSpaceGetFunctional(QF, f, &ffunc));
3441         PetscCall(PetscQuadratureGetData(ffunc, NULL, &NqcF, &NpF, &fqpoints, &fqweights));
3442         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);
3443         if (NpC != NpF) continue;
3444         for (d = 0; d < dim; ++d) sum += PetscAbsReal(cqpoints[d] - fqpoints[d]);
3445         if (sum > 1.0e-9) continue;
3446         for (d = 0; d < NcC; ++d) sum += PetscAbsReal(cqweights[d] * fqweights[d]);
3447         if (sum < 1.0e-9) continue;
3448         cmap[offsetC + c] = offsetF + f;
3449         found             = PETSC_TRUE;
3450         break;
3451       }
3452       if (!found) {
3453         /* TODO We really want the average here, but some asshole put VecScatter in the interface */
3454         if (fvRef[field] || (feRef[field] && order == 0)) {
3455           cmap[offsetC + c] = offsetF + 0;
3456         } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Could not locate matching functional for injection");
3457       }
3458     }
3459     offsetC += cpdim;
3460     offsetF += fpdim;
3461   }
3462   for (f = 0; f < Nf; ++f) {
3463     PetscCall(PetscFEDestroy(&feRef[f]));
3464     PetscCall(PetscFVDestroy(&fvRef[f]));
3465   }
3466   PetscCall(PetscFree3(feRef, fvRef, needAvg));
3467 
3468   PetscCall(DMGetGlobalVector(dmf, &fv));
3469   PetscCall(DMGetGlobalVector(dmc, &cv));
3470   PetscCall(VecGetOwnershipRange(cv, &startC, &endC));
3471   PetscCall(PetscSectionGetConstrainedStorageSize(cglobalSection, &m));
3472   PetscCall(PetscMalloc2(cTotDim, &cellCIndices, fTotDim, &cellFIndices));
3473   PetscCall(PetscMalloc1(m, &cindices));
3474   PetscCall(PetscMalloc1(m, &findices));
3475   for (d = 0; d < m; ++d) cindices[d] = findices[d] = -1;
3476   for (c = cStart; c < cEnd; ++c) {
3477     PetscCall(DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, c, cellCIndices, cellFIndices));
3478     for (d = 0; d < cTotDim; ++d) {
3479       if ((cellCIndices[d] < startC) || (cellCIndices[d] >= endC)) continue;
3480       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]]);
3481       cindices[cellCIndices[d] - startC] = cellCIndices[d];
3482       findices[cellCIndices[d] - startC] = cellFIndices[cmap[d]];
3483     }
3484   }
3485   PetscCall(PetscFree(cmap));
3486   PetscCall(PetscFree2(cellCIndices, cellFIndices));
3487 
3488   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, m, cindices, PETSC_OWN_POINTER, &cis));
3489   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, m, findices, PETSC_OWN_POINTER, &fis));
3490   PetscCall(VecScatterCreate(cv, cis, fv, fis, sc));
3491   PetscCall(ISDestroy(&cis));
3492   PetscCall(ISDestroy(&fis));
3493   PetscCall(DMRestoreGlobalVector(dmf, &fv));
3494   PetscCall(DMRestoreGlobalVector(dmc, &cv));
3495   PetscCall(PetscLogEventEnd(DMPLEX_InjectorFEM, dmc, dmf, 0, 0));
3496   PetscFunctionReturn(PETSC_SUCCESS);
3497 }
3498 
3499 /*@C
3500   DMPlexGetCellFields - Retrieve the field values values for a chunk of cells
3501 
3502   Input Parameters:
3503 + dm     - The `DM`
3504 . cellIS - The cells to include
3505 . locX   - A local vector with the solution fields
3506 . locX_t - A local vector with solution field time derivatives, or NULL
3507 - locA   - A local vector with auxiliary fields, or NULL
3508 
3509   Output Parameters:
3510 + u   - The field coefficients
3511 . u_t - The fields derivative coefficients
3512 - a   - The auxiliary field coefficients
3513 
3514   Level: developer
3515 
3516 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
3517 @*/
3518 PetscErrorCode DMPlexGetCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
3519 {
3520   DM              plex, plexA = NULL;
3521   DMEnclosureType encAux;
3522   PetscSection    section, sectionAux;
3523   PetscDS         prob;
3524   const PetscInt *cells;
3525   PetscInt        cStart, cEnd, numCells, totDim, totDimAux, c;
3526 
3527   PetscFunctionBegin;
3528   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3529   PetscValidHeaderSpecific(locX, VEC_CLASSID, 3);
3530   if (locX_t) PetscValidHeaderSpecific(locX_t, VEC_CLASSID, 4);
3531   if (locA) PetscValidHeaderSpecific(locA, VEC_CLASSID, 5);
3532   PetscAssertPointer(u, 6);
3533   PetscAssertPointer(u_t, 7);
3534   PetscAssertPointer(a, 8);
3535   PetscCall(DMPlexConvertPlex(dm, &plex, PETSC_FALSE));
3536   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
3537   PetscCall(DMGetLocalSection(dm, &section));
3538   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL));
3539   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
3540   if (locA) {
3541     DM      dmAux;
3542     PetscDS probAux;
3543 
3544     PetscCall(VecGetDM(locA, &dmAux));
3545     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
3546     PetscCall(DMPlexConvertPlex(dmAux, &plexA, PETSC_FALSE));
3547     PetscCall(DMGetLocalSection(dmAux, &sectionAux));
3548     PetscCall(DMGetDS(dmAux, &probAux));
3549     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
3550   }
3551   numCells = cEnd - cStart;
3552   PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u));
3553   if (locX_t) PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u_t));
3554   else *u_t = NULL;
3555   if (locA) PetscCall(DMGetWorkArray(dm, numCells * totDimAux, MPIU_SCALAR, a));
3556   else *a = NULL;
3557   for (c = cStart; c < cEnd; ++c) {
3558     const PetscInt cell = cells ? cells[c] : c;
3559     const PetscInt cind = c - cStart;
3560     PetscScalar   *x = NULL, *x_t = NULL, *ul = *u, *ul_t = *u_t, *al = *a;
3561     PetscInt       i;
3562 
3563     PetscCall(DMPlexVecGetClosure(plex, section, locX, cell, NULL, &x));
3564     for (i = 0; i < totDim; ++i) ul[cind * totDim + i] = x[i];
3565     PetscCall(DMPlexVecRestoreClosure(plex, section, locX, cell, NULL, &x));
3566     if (locX_t) {
3567       PetscCall(DMPlexVecGetClosure(plex, section, locX_t, cell, NULL, &x_t));
3568       for (i = 0; i < totDim; ++i) ul_t[cind * totDim + i] = x_t[i];
3569       PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, cell, NULL, &x_t));
3570     }
3571     if (locA) {
3572       PetscInt subcell;
3573       PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, cell, &subcell));
3574       PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subcell, NULL, &x));
3575       for (i = 0; i < totDimAux; ++i) al[cind * totDimAux + i] = x[i];
3576       PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subcell, NULL, &x));
3577     }
3578   }
3579   PetscCall(DMDestroy(&plex));
3580   if (locA) PetscCall(DMDestroy(&plexA));
3581   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
3582   PetscFunctionReturn(PETSC_SUCCESS);
3583 }
3584 
3585 /*@C
3586   DMPlexRestoreCellFields - Restore the field values values for a chunk of cells
3587 
3588   Input Parameters:
3589 + dm     - The `DM`
3590 . cellIS - The cells to include
3591 . locX   - A local vector with the solution fields
3592 . locX_t - A local vector with solution field time derivatives, or NULL
3593 - locA   - A local vector with auxiliary fields, or NULL
3594 
3595   Output Parameters:
3596 + u   - The field coefficients
3597 . u_t - The fields derivative coefficients
3598 - a   - The auxiliary field coefficients
3599 
3600   Level: developer
3601 
3602 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
3603 @*/
3604 PetscErrorCode DMPlexRestoreCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
3605 {
3606   PetscFunctionBegin;
3607   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u));
3608   if (locX_t) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u_t));
3609   if (locA) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, a));
3610   PetscFunctionReturn(PETSC_SUCCESS);
3611 }
3612 
3613 static PetscErrorCode DMPlexGetHybridCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
3614 {
3615   DM              plex, plexA = NULL;
3616   DMEnclosureType encAux;
3617   PetscSection    section, sectionAux;
3618   PetscDS         ds, dsIn;
3619   const PetscInt *cells;
3620   PetscInt        cStart, cEnd, numCells, c, totDim, totDimAux, Nf, f;
3621 
3622   PetscFunctionBegin;
3623   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3624   PetscValidHeaderSpecific(cellIS, IS_CLASSID, 2);
3625   PetscValidHeaderSpecific(locX, VEC_CLASSID, 3);
3626   if (locX_t) { PetscValidHeaderSpecific(locX_t, VEC_CLASSID, 4); }
3627   if (locA) { PetscValidHeaderSpecific(locA, VEC_CLASSID, 5); }
3628   PetscAssertPointer(u, 6);
3629   PetscAssertPointer(u_t, 7);
3630   PetscAssertPointer(a, 8);
3631   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
3632   numCells = cEnd - cStart;
3633   PetscCall(DMPlexConvertPlex(dm, &plex, PETSC_FALSE));
3634   PetscCall(DMGetLocalSection(dm, &section));
3635   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn));
3636   PetscCall(PetscDSGetNumFields(dsIn, &Nf));
3637   PetscCall(PetscDSGetTotalDimension(dsIn, &totDim));
3638   if (locA) {
3639     DM      dmAux;
3640     PetscDS probAux;
3641 
3642     PetscCall(VecGetDM(locA, &dmAux));
3643     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
3644     PetscCall(DMPlexConvertPlex(dmAux, &plexA, PETSC_FALSE));
3645     PetscCall(DMGetLocalSection(dmAux, &sectionAux));
3646     PetscCall(DMGetDS(dmAux, &probAux));
3647     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
3648   }
3649   PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u));
3650   if (locX_t) PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u_t));
3651   else {
3652     *u_t = NULL;
3653   }
3654   if (locA) PetscCall(DMGetWorkArray(dm, numCells * totDimAux, MPIU_SCALAR, a));
3655   else {
3656     *a = NULL;
3657   }
3658   // Loop over cohesive cells
3659   for (c = cStart; c < cEnd; ++c) {
3660     const PetscInt  cell = cells ? cells[c] : c;
3661     const PetscInt  cind = c - cStart;
3662     PetscScalar    *xf = NULL, *xc = NULL, *x = NULL, *xf_t = NULL, *xc_t = NULL;
3663     PetscScalar    *ul = &(*u)[cind * totDim], *ul_t = PetscSafePointerPlusOffset(*u_t, cind * totDim);
3664     const PetscInt *cone, *ornt;
3665     PetscInt        Nx = 0, Nxf, s;
3666 
3667     PetscCall(DMPlexGetCone(dm, cell, &cone));
3668     PetscCall(DMPlexGetConeOrientation(dm, cell, &ornt));
3669     // Put in cohesive unknowns
3670     PetscCall(DMPlexVecGetClosure(plex, section, locX, cell, &Nxf, &xf));
3671     if (locX_t) PetscCall(DMPlexVecGetClosure(plex, section, locX_t, cell, NULL, &xf_t));
3672     for (f = 0; f < Nf; ++f) {
3673       PetscInt  fdofIn, foff, foffIn;
3674       PetscBool cohesive;
3675 
3676       PetscCall(PetscDSGetCohesive(dsIn, f, &cohesive));
3677       if (!cohesive) continue;
3678       PetscCall(PetscDSGetFieldSize(dsIn, f, &fdofIn));
3679       PetscCall(PetscDSGetFieldOffsetCohesive(ds, f, &foff));
3680       PetscCall(PetscDSGetFieldOffsetCohesive(dsIn, f, &foffIn));
3681       for (PetscInt i = 0; i < fdofIn; ++i) ul[foffIn + i] = xf[foff + i];
3682       if (locX_t)
3683         for (PetscInt i = 0; i < fdofIn; ++i) ul_t[foffIn + i] = xf_t[foff + i];
3684       Nx += fdofIn;
3685     }
3686     PetscCall(DMPlexVecRestoreClosure(plex, section, locX, cell, &Nxf, &xf));
3687     if (locX_t) PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, cell, NULL, &xf_t));
3688     // Loop over sides of surface
3689     for (s = 0; s < 2; ++s) {
3690       const PetscInt *support;
3691       const PetscInt  face = cone[s];
3692       PetscInt        ssize, ncell, Nxc;
3693 
3694       // I don't think I need the face to have 0 orientation in the hybrid cell
3695       //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]);
3696       PetscCall(DMPlexGetSupport(dm, face, &support));
3697       PetscCall(DMPlexGetSupportSize(dm, face, &ssize));
3698       if (support[0] == cell) ncell = support[1];
3699       else if (support[1] == cell) ncell = support[0];
3700       else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", face, cell);
3701       // Get closure of both face and cell, stick in cell for normal fields and face for cohesive fields
3702       PetscCall(DMPlexVecGetClosure(plex, section, locX, ncell, &Nxc, &xc));
3703       if (locX_t) PetscCall(DMPlexVecGetClosure(plex, section, locX_t, ncell, NULL, &xc_t));
3704       for (f = 0; f < Nf; ++f) {
3705         PetscInt  fdofIn, foffIn;
3706         PetscBool cohesive;
3707 
3708         PetscCall(PetscDSGetCohesive(dsIn, f, &cohesive));
3709         if (cohesive) continue;
3710         PetscCall(PetscDSGetFieldSize(dsIn, f, &fdofIn));
3711         PetscCall(PetscDSGetFieldOffsetCohesive(dsIn, f, &foffIn));
3712         for (PetscInt i = 0; i < fdofIn; ++i) ul[foffIn + s * fdofIn + i] = xc[foffIn + i];
3713         if (locX_t)
3714           for (PetscInt i = 0; i < fdofIn; ++i) ul_t[foffIn + s * fdofIn + i] = xc_t[foffIn + i];
3715         Nx += fdofIn;
3716       }
3717       PetscCall(DMPlexVecRestoreClosure(plex, section, locX, ncell, &Nxc, &xc));
3718       if (locX_t) PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, ncell, NULL, &xc_t));
3719     }
3720     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);
3721 
3722     if (locA) {
3723       PetscScalar *al = &(*a)[cind * totDimAux];
3724       PetscInt     subcell;
3725 
3726       PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, cell, &subcell));
3727       PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subcell, &Nx, &x));
3728       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);
3729       for (PetscInt i = 0; i < totDimAux; ++i) al[i] = x[i];
3730       PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subcell, &Nx, &x));
3731     }
3732   }
3733   PetscCall(DMDestroy(&plex));
3734   PetscCall(DMDestroy(&plexA));
3735   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
3736   PetscFunctionReturn(PETSC_SUCCESS);
3737 }
3738 
3739 /*
3740   DMPlexGetHybridFields - Get the field values for the negative side (s = 0) and positive side (s = 1) of the interface
3741 
3742   Input Parameters:
3743 + dm      - The full domain DM
3744 . dmX     - An array of DM for the field, say an auxiliary DM, indexed by s
3745 . dsX     - An array of PetscDS for the field, indexed by s
3746 . cellIS  - The interface cells for which we want values
3747 . locX    - An array of local vectors with the field values, indexed by s
3748 - useCell - Flag to have values come from neighboring cell rather than endcap face
3749 
3750   Output Parameter:
3751 . x       - An array of field values, indexed by s
3752 
3753   Note:
3754   The arrays in `x` will be allocated using `DMGetWorkArray()`, and must be returned using `DMPlexRestoreHybridFields()`.
3755 
3756   Level: advanced
3757 
3758 .seealso: `DMPlexRestoreHybridFields()`, `DMGetWorkArray()`
3759 */
3760 static PetscErrorCode DMPlexGetHybridFields(DM dm, DM dmX[], PetscDS dsX[], IS cellIS, Vec locX[], PetscBool useCell, PetscScalar *x[])
3761 {
3762   DM              plexX[2];
3763   DMEnclosureType encX[2];
3764   PetscSection    sectionX[2];
3765   const PetscInt *cells;
3766   PetscInt        cStart, cEnd, numCells, c, s, totDimX[2];
3767 
3768   PetscFunctionBegin;
3769   PetscAssertPointer(locX, 5);
3770   if (!locX[0] || !locX[1]) PetscFunctionReturn(PETSC_SUCCESS);
3771   PetscAssertPointer(dmX, 2);
3772   PetscAssertPointer(dsX, 3);
3773   PetscValidHeaderSpecific(cellIS, IS_CLASSID, 4);
3774   PetscAssertPointer(x, 7);
3775   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
3776   numCells = cEnd - cStart;
3777   for (s = 0; s < 2; ++s) {
3778     PetscValidHeaderSpecific(dmX[s], DM_CLASSID, 2);
3779     PetscValidHeaderSpecific(dsX[s], PETSCDS_CLASSID, 3);
3780     PetscValidHeaderSpecific(locX[s], VEC_CLASSID, 5);
3781     PetscCall(DMPlexConvertPlex(dmX[s], &plexX[s], PETSC_FALSE));
3782     PetscCall(DMGetEnclosureRelation(dmX[s], dm, &encX[s]));
3783     PetscCall(DMGetLocalSection(dmX[s], &sectionX[s]));
3784     PetscCall(PetscDSGetTotalDimension(dsX[s], &totDimX[s]));
3785     PetscCall(DMGetWorkArray(dmX[s], numCells * totDimX[s], MPIU_SCALAR, &x[s]));
3786   }
3787   for (c = cStart; c < cEnd; ++c) {
3788     const PetscInt  cell = cells ? cells[c] : c;
3789     const PetscInt  cind = c - cStart;
3790     const PetscInt *cone, *ornt;
3791 
3792     PetscCall(DMPlexGetCone(dm, cell, &cone));
3793     PetscCall(DMPlexGetConeOrientation(dm, cell, &ornt));
3794     //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]);
3795     for (s = 0; s < 2; ++s) {
3796       const PetscInt tdX     = totDimX[s];
3797       PetscScalar   *closure = NULL, *xl = &x[s][cind * tdX];
3798       PetscInt       face = cone[s], point = face, subpoint, Nx, i;
3799 
3800       if (useCell) {
3801         const PetscInt *support;
3802         PetscInt        ssize;
3803 
3804         PetscCall(DMPlexGetSupport(dm, face, &support));
3805         PetscCall(DMPlexGetSupportSize(dm, face, &ssize));
3806         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);
3807         if (support[0] == cell) point = support[1];
3808         else if (support[1] == cell) point = support[0];
3809         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", face, cell);
3810       }
3811       PetscCall(DMGetEnclosurePoint(plexX[s], dm, encX[s], point, &subpoint));
3812       PetscCall(DMPlexVecGetOrientedClosure_Internal(plexX[s], sectionX[s], PETSC_FALSE, locX[s], subpoint, ornt[s], &Nx, &closure));
3813       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);
3814       for (i = 0; i < Nx; ++i) xl[i] = closure[i];
3815       PetscCall(DMPlexVecRestoreClosure(plexX[s], sectionX[s], locX[s], subpoint, &Nx, &closure));
3816     }
3817   }
3818   for (s = 0; s < 2; ++s) PetscCall(DMDestroy(&plexX[s]));
3819   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
3820   PetscFunctionReturn(PETSC_SUCCESS);
3821 }
3822 
3823 static PetscErrorCode DMPlexRestoreHybridFields(DM dm, DM dmX[], PetscDS dsX[], IS cellIS, Vec locX[], PetscBool useCell, PetscScalar *x[])
3824 {
3825   PetscFunctionBegin;
3826   if (!locX[0] || !locX[1]) PetscFunctionReturn(PETSC_SUCCESS);
3827   PetscCall(DMRestoreWorkArray(dmX[0], 0, MPIU_SCALAR, &x[0]));
3828   PetscCall(DMRestoreWorkArray(dmX[1], 0, MPIU_SCALAR, &x[1]));
3829   PetscFunctionReturn(PETSC_SUCCESS);
3830 }
3831 
3832 /*@C
3833   DMPlexGetFaceFields - Retrieve the field values values for a chunk of faces
3834 
3835   Input Parameters:
3836 + dm           - The `DM`
3837 . fStart       - The first face to include
3838 . fEnd         - The first face to exclude
3839 . locX         - A local vector with the solution fields
3840 . locX_t       - A local vector with solution field time derivatives, or NULL
3841 . faceGeometry - A local vector with face geometry
3842 . cellGeometry - A local vector with cell geometry
3843 - locGrad      - A local vector with field gradients, or NULL
3844 
3845   Output Parameters:
3846 + Nface - The number of faces with field values
3847 . uL    - The field values at the left side of the face
3848 - uR    - The field values at the right side of the face
3849 
3850   Level: developer
3851 
3852 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellFields()`
3853 @*/
3854 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)
3855 {
3856   DM                 dmFace, dmCell, dmGrad = NULL;
3857   PetscSection       section;
3858   PetscDS            prob;
3859   DMLabel            ghostLabel;
3860   const PetscScalar *facegeom, *cellgeom, *x, *lgrad;
3861   PetscBool         *isFE;
3862   PetscInt           dim, Nf, f, Nc, numFaces = fEnd - fStart, iface, face;
3863 
3864   PetscFunctionBegin;
3865   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3866   PetscValidHeaderSpecific(locX, VEC_CLASSID, 4);
3867   if (locX_t) PetscValidHeaderSpecific(locX_t, VEC_CLASSID, 5);
3868   PetscValidHeaderSpecific(faceGeometry, VEC_CLASSID, 6);
3869   PetscValidHeaderSpecific(cellGeometry, VEC_CLASSID, 7);
3870   if (locGrad) PetscValidHeaderSpecific(locGrad, VEC_CLASSID, 8);
3871   PetscAssertPointer(uL, 10);
3872   PetscAssertPointer(uR, 11);
3873   PetscCall(DMGetDimension(dm, &dim));
3874   PetscCall(DMGetDS(dm, &prob));
3875   PetscCall(DMGetLocalSection(dm, &section));
3876   PetscCall(PetscDSGetNumFields(prob, &Nf));
3877   PetscCall(PetscDSGetTotalComponents(prob, &Nc));
3878   PetscCall(PetscMalloc1(Nf, &isFE));
3879   for (f = 0; f < Nf; ++f) {
3880     PetscObject  obj;
3881     PetscClassId id;
3882 
3883     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
3884     PetscCall(PetscObjectGetClassId(obj, &id));
3885     if (id == PETSCFE_CLASSID) {
3886       isFE[f] = PETSC_TRUE;
3887     } else if (id == PETSCFV_CLASSID) {
3888       isFE[f] = PETSC_FALSE;
3889     } else {
3890       isFE[f] = PETSC_FALSE;
3891     }
3892   }
3893   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
3894   PetscCall(VecGetArrayRead(locX, &x));
3895   PetscCall(VecGetDM(faceGeometry, &dmFace));
3896   PetscCall(VecGetArrayRead(faceGeometry, &facegeom));
3897   PetscCall(VecGetDM(cellGeometry, &dmCell));
3898   PetscCall(VecGetArrayRead(cellGeometry, &cellgeom));
3899   if (locGrad) {
3900     PetscCall(VecGetDM(locGrad, &dmGrad));
3901     PetscCall(VecGetArrayRead(locGrad, &lgrad));
3902   }
3903   PetscCall(DMGetWorkArray(dm, numFaces * Nc, MPIU_SCALAR, uL));
3904   PetscCall(DMGetWorkArray(dm, numFaces * Nc, MPIU_SCALAR, uR));
3905   /* Right now just eat the extra work for FE (could make a cell loop) */
3906   for (face = fStart, iface = 0; face < fEnd; ++face) {
3907     const PetscInt  *cells;
3908     PetscFVFaceGeom *fg;
3909     PetscFVCellGeom *cgL, *cgR;
3910     PetscScalar     *xL, *xR, *gL, *gR;
3911     PetscScalar     *uLl = *uL, *uRl = *uR;
3912     PetscInt         ghost, nsupp, nchild;
3913 
3914     PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
3915     PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
3916     PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
3917     if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
3918     PetscCall(DMPlexPointLocalRead(dmFace, face, facegeom, &fg));
3919     PetscCall(DMPlexGetSupport(dm, face, &cells));
3920     PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL));
3921     PetscCall(DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR));
3922     for (f = 0; f < Nf; ++f) {
3923       PetscInt off;
3924 
3925       PetscCall(PetscDSGetComponentOffset(prob, f, &off));
3926       if (isFE[f]) {
3927         const PetscInt *cone;
3928         PetscInt        comp, coneSizeL, coneSizeR, faceLocL, faceLocR, ldof, rdof, d;
3929 
3930         xL = xR = NULL;
3931         PetscCall(PetscSectionGetFieldComponents(section, f, &comp));
3932         PetscCall(DMPlexVecGetClosure(dm, section, locX, cells[0], &ldof, (PetscScalar **)&xL));
3933         PetscCall(DMPlexVecGetClosure(dm, section, locX, cells[1], &rdof, (PetscScalar **)&xR));
3934         PetscCall(DMPlexGetCone(dm, cells[0], &cone));
3935         PetscCall(DMPlexGetConeSize(dm, cells[0], &coneSizeL));
3936         for (faceLocL = 0; faceLocL < coneSizeL; ++faceLocL)
3937           if (cone[faceLocL] == face) break;
3938         PetscCall(DMPlexGetCone(dm, cells[1], &cone));
3939         PetscCall(DMPlexGetConeSize(dm, cells[1], &coneSizeR));
3940         for (faceLocR = 0; faceLocR < coneSizeR; ++faceLocR)
3941           if (cone[faceLocR] == face) break;
3942         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]);
3943         /* Check that FEM field has values in the right cell (sometimes its an FV ghost cell) */
3944         /* TODO: this is a hack that might not be right for nonconforming */
3945         if (faceLocL < coneSizeL) {
3946           PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocL, xL, &uLl[iface * Nc + off]));
3947           if (rdof == ldof && faceLocR < coneSizeR) PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface * Nc + off]));
3948           else {
3949             for (d = 0; d < comp; ++d) uRl[iface * Nc + off + d] = uLl[iface * Nc + off + d];
3950           }
3951         } else {
3952           PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface * Nc + off]));
3953           PetscCall(PetscSectionGetFieldComponents(section, f, &comp));
3954           for (d = 0; d < comp; ++d) uLl[iface * Nc + off + d] = uRl[iface * Nc + off + d];
3955         }
3956         PetscCall(DMPlexVecRestoreClosure(dm, section, locX, cells[0], &ldof, (PetscScalar **)&xL));
3957         PetscCall(DMPlexVecRestoreClosure(dm, section, locX, cells[1], &rdof, (PetscScalar **)&xR));
3958       } else {
3959         PetscFV  fv;
3960         PetscInt numComp, c;
3961 
3962         PetscCall(PetscDSGetDiscretization(prob, f, (PetscObject *)&fv));
3963         PetscCall(PetscFVGetNumComponents(fv, &numComp));
3964         PetscCall(DMPlexPointLocalFieldRead(dm, cells[0], f, x, &xL));
3965         PetscCall(DMPlexPointLocalFieldRead(dm, cells[1], f, x, &xR));
3966         if (dmGrad) {
3967           PetscReal dxL[3], dxR[3];
3968 
3969           PetscCall(DMPlexPointLocalRead(dmGrad, cells[0], lgrad, &gL));
3970           PetscCall(DMPlexPointLocalRead(dmGrad, cells[1], lgrad, &gR));
3971           DMPlex_WaxpyD_Internal(dim, -1, cgL->centroid, fg->centroid, dxL);
3972           DMPlex_WaxpyD_Internal(dim, -1, cgR->centroid, fg->centroid, dxR);
3973           for (c = 0; c < numComp; ++c) {
3974             uLl[iface * Nc + off + c] = xL[c] + DMPlex_DotD_Internal(dim, &gL[c * dim], dxL);
3975             uRl[iface * Nc + off + c] = xR[c] + DMPlex_DotD_Internal(dim, &gR[c * dim], dxR);
3976           }
3977         } else {
3978           for (c = 0; c < numComp; ++c) {
3979             uLl[iface * Nc + off + c] = xL[c];
3980             uRl[iface * Nc + off + c] = xR[c];
3981           }
3982         }
3983       }
3984     }
3985     ++iface;
3986   }
3987   *Nface = iface;
3988   PetscCall(VecRestoreArrayRead(locX, &x));
3989   PetscCall(VecRestoreArrayRead(faceGeometry, &facegeom));
3990   PetscCall(VecRestoreArrayRead(cellGeometry, &cellgeom));
3991   if (locGrad) PetscCall(VecRestoreArrayRead(locGrad, &lgrad));
3992   PetscCall(PetscFree(isFE));
3993   PetscFunctionReturn(PETSC_SUCCESS);
3994 }
3995 
3996 /*@C
3997   DMPlexRestoreFaceFields - Restore the field values values for a chunk of faces
3998 
3999   Input Parameters:
4000 + dm           - The `DM`
4001 . fStart       - The first face to include
4002 . fEnd         - The first face to exclude
4003 . locX         - A local vector with the solution fields
4004 . locX_t       - A local vector with solution field time derivatives, or NULL
4005 . faceGeometry - A local vector with face geometry
4006 . cellGeometry - A local vector with cell geometry
4007 - locGrad      - A local vector with field gradients, or NULL
4008 
4009   Output Parameters:
4010 + Nface - The number of faces with field values
4011 . uL    - The field values at the left side of the face
4012 - uR    - The field values at the right side of the face
4013 
4014   Level: developer
4015 
4016 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
4017 @*/
4018 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)
4019 {
4020   PetscFunctionBegin;
4021   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uL));
4022   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uR));
4023   PetscFunctionReturn(PETSC_SUCCESS);
4024 }
4025 
4026 /*@C
4027   DMPlexGetFaceGeometry - Retrieve the geometric values for a chunk of faces
4028 
4029   Input Parameters:
4030 + dm           - The `DM`
4031 . fStart       - The first face to include
4032 . fEnd         - The first face to exclude
4033 . faceGeometry - A local vector with face geometry
4034 - cellGeometry - A local vector with cell geometry
4035 
4036   Output Parameters:
4037 + Nface - The number of faces with field values
4038 . fgeom - The extract the face centroid and normal
4039 - vol   - The cell volume
4040 
4041   Level: developer
4042 
4043 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellFields()`
4044 @*/
4045 PetscErrorCode DMPlexGetFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom **fgeom, PetscReal **vol)
4046 {
4047   DM                 dmFace, dmCell;
4048   DMLabel            ghostLabel;
4049   const PetscScalar *facegeom, *cellgeom;
4050   PetscInt           dim, numFaces = fEnd - fStart, iface, face;
4051 
4052   PetscFunctionBegin;
4053   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4054   PetscValidHeaderSpecific(faceGeometry, VEC_CLASSID, 4);
4055   PetscValidHeaderSpecific(cellGeometry, VEC_CLASSID, 5);
4056   PetscAssertPointer(fgeom, 7);
4057   PetscAssertPointer(vol, 8);
4058   PetscCall(DMGetDimension(dm, &dim));
4059   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
4060   PetscCall(VecGetDM(faceGeometry, &dmFace));
4061   PetscCall(VecGetArrayRead(faceGeometry, &facegeom));
4062   PetscCall(VecGetDM(cellGeometry, &dmCell));
4063   PetscCall(VecGetArrayRead(cellGeometry, &cellgeom));
4064   PetscCall(PetscMalloc1(numFaces, fgeom));
4065   PetscCall(DMGetWorkArray(dm, numFaces * 2, MPIU_SCALAR, vol));
4066   for (face = fStart, iface = 0; face < fEnd; ++face) {
4067     const PetscInt  *cells;
4068     PetscFVFaceGeom *fg;
4069     PetscFVCellGeom *cgL, *cgR;
4070     PetscFVFaceGeom *fgeoml = *fgeom;
4071     PetscReal       *voll   = *vol;
4072     PetscInt         ghost, d, nchild, nsupp;
4073 
4074     PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
4075     PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
4076     PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
4077     if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
4078     PetscCall(DMPlexPointLocalRead(dmFace, face, facegeom, &fg));
4079     PetscCall(DMPlexGetSupport(dm, face, &cells));
4080     PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL));
4081     PetscCall(DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR));
4082     for (d = 0; d < dim; ++d) {
4083       fgeoml[iface].centroid[d] = fg->centroid[d];
4084       fgeoml[iface].normal[d]   = fg->normal[d];
4085     }
4086     voll[iface * 2 + 0] = cgL->volume;
4087     voll[iface * 2 + 1] = cgR->volume;
4088     ++iface;
4089   }
4090   *Nface = iface;
4091   PetscCall(VecRestoreArrayRead(faceGeometry, &facegeom));
4092   PetscCall(VecRestoreArrayRead(cellGeometry, &cellgeom));
4093   PetscFunctionReturn(PETSC_SUCCESS);
4094 }
4095 
4096 /*@C
4097   DMPlexRestoreFaceGeometry - Restore the field values values for a chunk of faces
4098 
4099   Input Parameters:
4100 + dm           - The `DM`
4101 . fStart       - The first face to include
4102 . fEnd         - The first face to exclude
4103 . faceGeometry - A local vector with face geometry
4104 - cellGeometry - A local vector with cell geometry
4105 
4106   Output Parameters:
4107 + Nface - The number of faces with field values
4108 . fgeom - The extract the face centroid and normal
4109 - vol   - The cell volume
4110 
4111   Level: developer
4112 
4113 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
4114 @*/
4115 PetscErrorCode DMPlexRestoreFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom **fgeom, PetscReal **vol)
4116 {
4117   PetscFunctionBegin;
4118   PetscCall(PetscFree(*fgeom));
4119   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_REAL, vol));
4120   PetscFunctionReturn(PETSC_SUCCESS);
4121 }
4122 
4123 PetscErrorCode DMSNESGetFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
4124 {
4125   char           composeStr[33] = {0};
4126   PetscObjectId  id;
4127   PetscContainer container;
4128 
4129   PetscFunctionBegin;
4130   PetscCall(PetscObjectGetId((PetscObject)quad, &id));
4131   PetscCall(PetscSNPrintf(composeStr, 32, "DMSNESGetFEGeom_%" PetscInt64_FMT "\n", id));
4132   PetscCall(PetscObjectQuery((PetscObject)pointIS, composeStr, (PetscObject *)&container));
4133   if (container) {
4134     PetscCall(PetscContainerGetPointer(container, (void **)geom));
4135   } else {
4136     PetscCall(DMFieldCreateFEGeom(coordField, pointIS, quad, faceData, geom));
4137     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &container));
4138     PetscCall(PetscContainerSetPointer(container, (void *)*geom));
4139     PetscCall(PetscContainerSetUserDestroy(container, PetscContainerUserDestroy_PetscFEGeom));
4140     PetscCall(PetscObjectCompose((PetscObject)pointIS, composeStr, (PetscObject)container));
4141     PetscCall(PetscContainerDestroy(&container));
4142   }
4143   PetscFunctionReturn(PETSC_SUCCESS);
4144 }
4145 
4146 PetscErrorCode DMSNESRestoreFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
4147 {
4148   PetscFunctionBegin;
4149   *geom = NULL;
4150   PetscFunctionReturn(PETSC_SUCCESS);
4151 }
4152 
4153 PetscErrorCode DMPlexComputeResidual_Patch_Internal(DM dm, PetscSection section, IS cellIS, PetscReal t, Vec locX, Vec locX_t, Vec locF, void *user)
4154 {
4155   DM_Plex        *mesh       = (DM_Plex *)dm->data;
4156   const char     *name       = "Residual";
4157   DM              dmAux      = NULL;
4158   DMLabel         ghostLabel = NULL;
4159   PetscDS         prob       = NULL;
4160   PetscDS         probAux    = NULL;
4161   PetscBool       useFEM     = PETSC_FALSE;
4162   PetscBool       isImplicit = (locX_t || t == PETSC_MIN_REAL) ? PETSC_TRUE : PETSC_FALSE;
4163   DMField         coordField = NULL;
4164   Vec             locA;
4165   PetscScalar    *u = NULL, *u_t, *a, *uL = NULL, *uR = NULL;
4166   IS              chunkIS;
4167   const PetscInt *cells;
4168   PetscInt        cStart, cEnd, numCells;
4169   PetscInt        Nf, f, totDim, totDimAux, numChunks, cellChunkSize, chunk, fStart, fEnd;
4170   PetscInt        maxDegree = PETSC_MAX_INT;
4171   PetscFormKey    key;
4172   PetscQuadrature affineQuad = NULL, *quads = NULL;
4173   PetscFEGeom    *affineGeom = NULL, **geoms = NULL;
4174 
4175   PetscFunctionBegin;
4176   PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
4177   /* FEM+FVM */
4178   /* 1: Get sizes from dm and dmAux */
4179   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
4180   PetscCall(DMGetDS(dm, &prob));
4181   PetscCall(PetscDSGetNumFields(prob, &Nf));
4182   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
4183   PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA));
4184   if (locA) {
4185     PetscCall(VecGetDM(locA, &dmAux));
4186     PetscCall(DMGetDS(dmAux, &probAux));
4187     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
4188   }
4189   /* 2: Get geometric data */
4190   for (f = 0; f < Nf; ++f) {
4191     PetscObject  obj;
4192     PetscClassId id;
4193     PetscBool    fimp;
4194 
4195     PetscCall(PetscDSGetImplicit(prob, f, &fimp));
4196     if (isImplicit != fimp) continue;
4197     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4198     PetscCall(PetscObjectGetClassId(obj, &id));
4199     if (id == PETSCFE_CLASSID) useFEM = PETSC_TRUE;
4200     PetscCheck(id != PETSCFV_CLASSID, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Use of FVM with PCPATCH not yet implemented");
4201   }
4202   if (useFEM) {
4203     PetscCall(DMGetCoordinateField(dm, &coordField));
4204     PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
4205     if (maxDegree <= 1) {
4206       PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad));
4207       if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
4208     } else {
4209       PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
4210       for (f = 0; f < Nf; ++f) {
4211         PetscObject  obj;
4212         PetscClassId id;
4213         PetscBool    fimp;
4214 
4215         PetscCall(PetscDSGetImplicit(prob, f, &fimp));
4216         if (isImplicit != fimp) continue;
4217         PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4218         PetscCall(PetscObjectGetClassId(obj, &id));
4219         if (id == PETSCFE_CLASSID) {
4220           PetscFE fe = (PetscFE)obj;
4221 
4222           PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
4223           PetscCall(PetscObjectReference((PetscObject)quads[f]));
4224           PetscCall(DMSNESGetFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
4225         }
4226       }
4227     }
4228   }
4229   /* Loop over chunks */
4230   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
4231   PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
4232   if (useFEM) PetscCall(ISCreate(PETSC_COMM_SELF, &chunkIS));
4233   numCells      = cEnd - cStart;
4234   numChunks     = 1;
4235   cellChunkSize = numCells / numChunks;
4236   numChunks     = PetscMin(1, numCells);
4237   key.label     = NULL;
4238   key.value     = 0;
4239   key.part      = 0;
4240   for (chunk = 0; chunk < numChunks; ++chunk) {
4241     PetscScalar     *elemVec, *fluxL = NULL, *fluxR = NULL;
4242     PetscReal       *vol   = NULL;
4243     PetscFVFaceGeom *fgeom = NULL;
4244     PetscInt         cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
4245     PetscInt         numFaces = 0;
4246 
4247     /* Extract field coefficients */
4248     if (useFEM) {
4249       PetscCall(ISGetPointSubrange(chunkIS, cS, cE, cells));
4250       PetscCall(DMPlexGetCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
4251       PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
4252       PetscCall(PetscArrayzero(elemVec, numCells * totDim));
4253     }
4254     /* TODO We will interlace both our field coefficients (u, u_t, uL, uR, etc.) and our output (elemVec, fL, fR). I think this works */
4255     /* Loop over fields */
4256     for (f = 0; f < Nf; ++f) {
4257       PetscObject  obj;
4258       PetscClassId id;
4259       PetscBool    fimp;
4260       PetscInt     numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;
4261 
4262       key.field = f;
4263       PetscCall(PetscDSGetImplicit(prob, f, &fimp));
4264       if (isImplicit != fimp) continue;
4265       PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4266       PetscCall(PetscObjectGetClassId(obj, &id));
4267       if (id == PETSCFE_CLASSID) {
4268         PetscFE         fe        = (PetscFE)obj;
4269         PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[f];
4270         PetscFEGeom    *chunkGeom = NULL;
4271         PetscQuadrature quad      = affineQuad ? affineQuad : quads[f];
4272         PetscInt        Nq, Nb;
4273 
4274         PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
4275         PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
4276         PetscCall(PetscFEGetDimension(fe, &Nb));
4277         blockSize = Nb;
4278         batchSize = numBlocks * blockSize;
4279         PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
4280         numChunks = numCells / (numBatches * batchSize);
4281         Ne        = numChunks * numBatches * batchSize;
4282         Nr        = numCells % (numBatches * batchSize);
4283         offset    = numCells - Nr;
4284         /* Integrate FE residual to get elemVec (need fields at quadrature points) */
4285         /*   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) */
4286         PetscCall(PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom));
4287         PetscCall(PetscFEIntegrateResidual(prob, key, Ne, chunkGeom, u, u_t, probAux, a, t, elemVec));
4288         PetscCall(PetscFEGeomGetChunk(geom, offset, numCells, &chunkGeom));
4289         PetscCall(PetscFEIntegrateResidual(prob, key, Nr, chunkGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), probAux, &a[offset * totDimAux], t, &elemVec[offset * totDim]));
4290         PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &chunkGeom));
4291       } else if (id == PETSCFV_CLASSID) {
4292         PetscFV fv = (PetscFV)obj;
4293 
4294         Ne = numFaces;
4295         /* Riemann solve over faces (need fields at face centroids) */
4296         /*   We need to evaluate FE fields at those coordinates */
4297         PetscCall(PetscFVIntegrateRHSFunction(fv, prob, f, Ne, fgeom, vol, uL, uR, fluxL, fluxR));
4298       } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
4299     }
4300     /* Loop over domain */
4301     if (useFEM) {
4302       /* Add elemVec to locX */
4303       for (c = cS; c < cE; ++c) {
4304         const PetscInt cell = cells ? cells[c] : c;
4305         const PetscInt cind = c - cStart;
4306 
4307         if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, name, totDim, &elemVec[cind * totDim]));
4308         if (ghostLabel) {
4309           PetscInt ghostVal;
4310 
4311           PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
4312           if (ghostVal > 0) continue;
4313         }
4314         PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind * totDim], ADD_ALL_VALUES));
4315       }
4316     }
4317     /* Handle time derivative */
4318     if (locX_t) {
4319       PetscScalar *x_t, *fa;
4320 
4321       PetscCall(VecGetArray(locF, &fa));
4322       PetscCall(VecGetArray(locX_t, &x_t));
4323       for (f = 0; f < Nf; ++f) {
4324         PetscFV      fv;
4325         PetscObject  obj;
4326         PetscClassId id;
4327         PetscInt     pdim, d;
4328 
4329         PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4330         PetscCall(PetscObjectGetClassId(obj, &id));
4331         if (id != PETSCFV_CLASSID) continue;
4332         fv = (PetscFV)obj;
4333         PetscCall(PetscFVGetNumComponents(fv, &pdim));
4334         for (c = cS; c < cE; ++c) {
4335           const PetscInt cell = cells ? cells[c] : c;
4336           PetscScalar   *u_t, *r;
4337 
4338           if (ghostLabel) {
4339             PetscInt ghostVal;
4340 
4341             PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
4342             if (ghostVal > 0) continue;
4343           }
4344           PetscCall(DMPlexPointLocalFieldRead(dm, cell, f, x_t, &u_t));
4345           PetscCall(DMPlexPointLocalFieldRef(dm, cell, f, fa, &r));
4346           for (d = 0; d < pdim; ++d) r[d] += u_t[d];
4347         }
4348       }
4349       PetscCall(VecRestoreArray(locX_t, &x_t));
4350       PetscCall(VecRestoreArray(locF, &fa));
4351     }
4352     if (useFEM) {
4353       PetscCall(DMPlexRestoreCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
4354       PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
4355     }
4356   }
4357   if (useFEM) PetscCall(ISDestroy(&chunkIS));
4358   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
4359   /* TODO Could include boundary residual here (see DMPlexComputeResidual_Internal) */
4360   if (useFEM) {
4361     if (maxDegree <= 1) {
4362       PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
4363       PetscCall(PetscQuadratureDestroy(&affineQuad));
4364     } else {
4365       for (f = 0; f < Nf; ++f) {
4366         PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
4367         PetscCall(PetscQuadratureDestroy(&quads[f]));
4368       }
4369       PetscCall(PetscFree2(quads, geoms));
4370     }
4371   }
4372   PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
4373   PetscFunctionReturn(PETSC_SUCCESS);
4374 }
4375 
4376 /*
4377   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
4378 
4379   X   - The local solution vector
4380   X_t - The local solution time derivative vector, or NULL
4381 */
4382 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)
4383 {
4384   DM_Plex        *mesh = (DM_Plex *)dm->data;
4385   const char     *name = "Jacobian", *nameP = "JacobianPre";
4386   DM              dmAux = NULL;
4387   PetscDS         prob, probAux = NULL;
4388   PetscSection    sectionAux = NULL;
4389   Vec             A;
4390   DMField         coordField;
4391   PetscFEGeom    *cgeomFEM;
4392   PetscQuadrature qGeom = NULL;
4393   Mat             J = Jac, JP = JacP;
4394   PetscScalar    *work, *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL, *elemMatP = NULL, *elemMatD = NULL;
4395   PetscBool       hasJac, hasPrec, hasDyn, assembleJac, *isFE, hasFV = PETSC_FALSE;
4396   const PetscInt *cells;
4397   PetscFormKey    key;
4398   PetscInt        Nf, fieldI, fieldJ, maxDegree, numCells, cStart, cEnd, numChunks, chunkSize, chunk, totDim, totDimAux = 0, sz, wsz, off = 0, offCell = 0;
4399 
4400   PetscFunctionBegin;
4401   PetscCall(ISGetLocalSize(cellIS, &numCells));
4402   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
4403   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
4404   PetscCall(DMGetDS(dm, &prob));
4405   PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &A));
4406   if (A) {
4407     PetscCall(VecGetDM(A, &dmAux));
4408     PetscCall(DMGetLocalSection(dmAux, &sectionAux));
4409     PetscCall(DMGetDS(dmAux, &probAux));
4410   }
4411   /* Get flags */
4412   PetscCall(PetscDSGetNumFields(prob, &Nf));
4413   PetscCall(DMGetWorkArray(dm, Nf, MPIU_BOOL, &isFE));
4414   for (fieldI = 0; fieldI < Nf; ++fieldI) {
4415     PetscObject  disc;
4416     PetscClassId id;
4417     PetscCall(PetscDSGetDiscretization(prob, fieldI, &disc));
4418     PetscCall(PetscObjectGetClassId(disc, &id));
4419     if (id == PETSCFE_CLASSID) {
4420       isFE[fieldI] = PETSC_TRUE;
4421     } else if (id == PETSCFV_CLASSID) {
4422       hasFV        = PETSC_TRUE;
4423       isFE[fieldI] = PETSC_FALSE;
4424     }
4425   }
4426   PetscCall(PetscDSHasJacobian(prob, &hasJac));
4427   PetscCall(PetscDSHasJacobianPreconditioner(prob, &hasPrec));
4428   PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
4429   assembleJac = hasJac && hasPrec && (Jac != JacP) ? PETSC_TRUE : PETSC_FALSE;
4430   hasDyn      = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
4431   if (hasFV) PetscCall(MatSetOption(JP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE)); /* No allocated space for FV stuff, so ignore the zero entries */
4432   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
4433   if (probAux) PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
4434   /* Compute batch sizes */
4435   if (isFE[0]) {
4436     PetscFE         fe;
4437     PetscQuadrature q;
4438     PetscInt        numQuadPoints, numBatches, batchSize, numBlocks, blockSize, Nb;
4439 
4440     PetscCall(PetscDSGetDiscretization(prob, 0, (PetscObject *)&fe));
4441     PetscCall(PetscFEGetQuadrature(fe, &q));
4442     PetscCall(PetscQuadratureGetData(q, NULL, NULL, &numQuadPoints, NULL, NULL));
4443     PetscCall(PetscFEGetDimension(fe, &Nb));
4444     PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
4445     blockSize = Nb * numQuadPoints;
4446     batchSize = numBlocks * blockSize;
4447     chunkSize = numBatches * batchSize;
4448     numChunks = numCells / chunkSize + numCells % chunkSize;
4449     PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
4450   } else {
4451     chunkSize = numCells;
4452     numChunks = 1;
4453   }
4454   /* Get work space */
4455   wsz = (((X ? 1 : 0) + (X_t ? 1 : 0) + (dmAux ? 1 : 0)) * totDim + ((hasJac ? 1 : 0) + (hasPrec ? 1 : 0) + (hasDyn ? 1 : 0)) * totDim * totDim) * chunkSize;
4456   PetscCall(DMGetWorkArray(dm, wsz, MPIU_SCALAR, &work));
4457   PetscCall(PetscArrayzero(work, wsz));
4458   off      = 0;
4459   u        = X ? (sz = chunkSize * totDim, off += sz, work + off - sz) : NULL;
4460   u_t      = X_t ? (sz = chunkSize * totDim, off += sz, work + off - sz) : NULL;
4461   a        = dmAux ? (sz = chunkSize * totDimAux, off += sz, work + off - sz) : NULL;
4462   elemMat  = hasJac ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL;
4463   elemMatP = hasPrec ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL;
4464   elemMatD = hasDyn ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL;
4465   PetscCheck(off == wsz, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Error is workspace size %" PetscInt_FMT " should be %" PetscInt_FMT, off, wsz);
4466   /* Setup geometry */
4467   PetscCall(DMGetCoordinateField(dm, &coordField));
4468   PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
4469   if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
4470   if (!qGeom) {
4471     PetscFE fe;
4472 
4473     PetscCall(PetscDSGetDiscretization(prob, 0, (PetscObject *)&fe));
4474     PetscCall(PetscFEGetQuadrature(fe, &qGeom));
4475     PetscCall(PetscObjectReference((PetscObject)qGeom));
4476   }
4477   PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
4478   /* Compute volume integrals */
4479   if (assembleJac) PetscCall(MatZeroEntries(J));
4480   PetscCall(MatZeroEntries(JP));
4481   key.label = NULL;
4482   key.value = 0;
4483   key.part  = 0;
4484   for (chunk = 0; chunk < numChunks; ++chunk, offCell += chunkSize) {
4485     const PetscInt Ncell = PetscMin(chunkSize, numCells - offCell);
4486     PetscInt       c;
4487 
4488     /* Extract values */
4489     for (c = 0; c < Ncell; ++c) {
4490       const PetscInt cell = cells ? cells[c + offCell] : c + offCell;
4491       PetscScalar   *x = NULL, *x_t = NULL;
4492       PetscInt       i;
4493 
4494       if (X) {
4495         PetscCall(DMPlexVecGetClosure(dm, section, X, cell, NULL, &x));
4496         for (i = 0; i < totDim; ++i) u[c * totDim + i] = x[i];
4497         PetscCall(DMPlexVecRestoreClosure(dm, section, X, cell, NULL, &x));
4498       }
4499       if (X_t) {
4500         PetscCall(DMPlexVecGetClosure(dm, section, X_t, cell, NULL, &x_t));
4501         for (i = 0; i < totDim; ++i) u_t[c * totDim + i] = x_t[i];
4502         PetscCall(DMPlexVecRestoreClosure(dm, section, X_t, cell, NULL, &x_t));
4503       }
4504       if (dmAux) {
4505         PetscCall(DMPlexVecGetClosure(dmAux, sectionAux, A, cell, NULL, &x));
4506         for (i = 0; i < totDimAux; ++i) a[c * totDimAux + i] = x[i];
4507         PetscCall(DMPlexVecRestoreClosure(dmAux, sectionAux, A, cell, NULL, &x));
4508       }
4509     }
4510     for (fieldI = 0; fieldI < Nf; ++fieldI) {
4511       PetscFE fe;
4512       PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
4513       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
4514         key.field = fieldI * Nf + fieldJ;
4515         if (hasJac) PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMat));
4516         if (hasPrec) PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatP));
4517         if (hasDyn) PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatD));
4518       }
4519       /* For finite volume, add the identity */
4520       if (!isFE[fieldI]) {
4521         PetscFV  fv;
4522         PetscInt eOffset = 0, Nc, fc, foff;
4523 
4524         PetscCall(PetscDSGetFieldOffset(prob, fieldI, &foff));
4525         PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fv));
4526         PetscCall(PetscFVGetNumComponents(fv, &Nc));
4527         for (c = 0; c < chunkSize; ++c, eOffset += totDim * totDim) {
4528           for (fc = 0; fc < Nc; ++fc) {
4529             const PetscInt i = foff + fc;
4530             if (hasJac) elemMat[eOffset + i * totDim + i] = 1.0;
4531             if (hasPrec) elemMatP[eOffset + i * totDim + i] = 1.0;
4532           }
4533         }
4534       }
4535     }
4536     /*   Add contribution from X_t */
4537     if (hasDyn) {
4538       for (c = 0; c < chunkSize * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
4539     }
4540     /* Insert values into matrix */
4541     for (c = 0; c < Ncell; ++c) {
4542       const PetscInt cell = cells ? cells[c + offCell] : c + offCell;
4543       if (mesh->printFEM > 1) {
4544         if (hasJac) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[(c - cStart) * totDim * totDim]));
4545         if (hasPrec) PetscCall(DMPrintCellMatrix(cell, nameP, totDim, totDim, &elemMatP[(c - cStart) * totDim * totDim]));
4546       }
4547       if (assembleJac) PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, Jac, cell, &elemMat[(c - cStart) * totDim * totDim], ADD_VALUES));
4548       PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, JP, cell, &elemMat[(c - cStart) * totDim * totDim], ADD_VALUES));
4549     }
4550   }
4551   /* Cleanup */
4552   PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
4553   PetscCall(PetscQuadratureDestroy(&qGeom));
4554   if (hasFV) PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE));
4555   PetscCall(DMRestoreWorkArray(dm, Nf, MPIU_BOOL, &isFE));
4556   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));
4557   /* Compute boundary integrals */
4558   /* PetscCall(DMPlexComputeBdJacobian_Internal(dm, X, X_t, t, X_tShift, Jac, JacP, ctx)); */
4559   /* Assemble matrix */
4560   if (assembleJac) {
4561     PetscCall(MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY));
4562     PetscCall(MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY));
4563   }
4564   PetscCall(MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY));
4565   PetscCall(MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY));
4566   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
4567   PetscFunctionReturn(PETSC_SUCCESS);
4568 }
4569 
4570 /******** FEM Assembly Function ********/
4571 
4572 static PetscErrorCode DMConvertPlex_Internal(DM dm, DM *plex, PetscBool copy)
4573 {
4574   PetscBool isPlex;
4575 
4576   PetscFunctionBegin;
4577   PetscCall(PetscObjectTypeCompare((PetscObject)dm, DMPLEX, &isPlex));
4578   if (isPlex) {
4579     *plex = dm;
4580     PetscCall(PetscObjectReference((PetscObject)dm));
4581   } else {
4582     PetscCall(PetscObjectQuery((PetscObject)dm, "dm_plex", (PetscObject *)plex));
4583     if (!*plex) {
4584       PetscCall(DMConvert(dm, DMPLEX, plex));
4585       PetscCall(PetscObjectCompose((PetscObject)dm, "dm_plex", (PetscObject)*plex));
4586     } else {
4587       PetscCall(PetscObjectReference((PetscObject)*plex));
4588     }
4589     if (copy) PetscCall(DMCopyAuxiliaryVec(dm, *plex));
4590   }
4591   PetscFunctionReturn(PETSC_SUCCESS);
4592 }
4593 
4594 /*@
4595   DMPlexGetGeometryFVM - Return precomputed geometric data
4596 
4597   Collective
4598 
4599   Input Parameter:
4600 . dm - The `DM`
4601 
4602   Output Parameters:
4603 + facegeom  - The values precomputed from face geometry
4604 . cellgeom  - The values precomputed from cell geometry
4605 - minRadius - The minimum radius over the mesh of an inscribed sphere in a cell
4606 
4607   Level: developer
4608 
4609 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMTSSetRHSFunctionLocal()`
4610 @*/
4611 PetscErrorCode DMPlexGetGeometryFVM(DM dm, Vec *facegeom, Vec *cellgeom, PetscReal *minRadius)
4612 {
4613   DM plex;
4614 
4615   PetscFunctionBegin;
4616   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4617   PetscCall(DMConvertPlex_Internal(dm, &plex, PETSC_TRUE));
4618   PetscCall(DMPlexGetDataFVM(plex, NULL, cellgeom, facegeom, NULL));
4619   if (minRadius) PetscCall(DMPlexGetMinRadius(plex, minRadius));
4620   PetscCall(DMDestroy(&plex));
4621   PetscFunctionReturn(PETSC_SUCCESS);
4622 }
4623 
4624 /*@
4625   DMPlexGetGradientDM - Return gradient data layout
4626 
4627   Collective
4628 
4629   Input Parameters:
4630 + dm - The `DM`
4631 - fv - The `PetscFV`
4632 
4633   Output Parameter:
4634 . dmGrad - The layout for gradient values
4635 
4636   Level: developer
4637 
4638 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetGeometryFVM()`
4639 @*/
4640 PetscErrorCode DMPlexGetGradientDM(DM dm, PetscFV fv, DM *dmGrad)
4641 {
4642   DM        plex;
4643   PetscBool computeGradients;
4644 
4645   PetscFunctionBegin;
4646   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4647   PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
4648   PetscAssertPointer(dmGrad, 3);
4649   PetscCall(PetscFVGetComputeGradients(fv, &computeGradients));
4650   if (!computeGradients) {
4651     *dmGrad = NULL;
4652     PetscFunctionReturn(PETSC_SUCCESS);
4653   }
4654   PetscCall(DMConvertPlex_Internal(dm, &plex, PETSC_TRUE));
4655   PetscCall(DMPlexGetDataFVM(plex, fv, NULL, NULL, dmGrad));
4656   PetscCall(DMDestroy(&plex));
4657   PetscFunctionReturn(PETSC_SUCCESS);
4658 }
4659 
4660 static PetscErrorCode DMPlexComputeBdResidual_Single_Internal(DM dm, PetscReal t, PetscWeakForm wf, PetscFormKey key, Vec locX, Vec locX_t, Vec locF, DMField coordField, IS facetIS)
4661 {
4662   DM_Plex        *mesh = (DM_Plex *)dm->data;
4663   DM              plex = NULL, plexA = NULL;
4664   DMEnclosureType encAux;
4665   PetscDS         prob, probAux       = NULL;
4666   PetscSection    section, sectionAux = NULL;
4667   Vec             locA = NULL;
4668   PetscScalar    *u = NULL, *u_t = NULL, *a = NULL, *elemVec = NULL;
4669   PetscInt        totDim, totDimAux = 0;
4670 
4671   PetscFunctionBegin;
4672   PetscCall(DMConvert(dm, DMPLEX, &plex));
4673   PetscCall(DMGetLocalSection(dm, &section));
4674   PetscCall(DMGetDS(dm, &prob));
4675   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
4676   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &locA));
4677   if (locA) {
4678     DM dmAux;
4679 
4680     PetscCall(VecGetDM(locA, &dmAux));
4681     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
4682     PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
4683     PetscCall(DMGetDS(plexA, &probAux));
4684     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
4685     PetscCall(DMGetLocalSection(plexA, &sectionAux));
4686   }
4687   {
4688     PetscFEGeom    *fgeom;
4689     PetscInt        maxDegree;
4690     PetscQuadrature qGeom = NULL;
4691     IS              pointIS;
4692     const PetscInt *points;
4693     PetscInt        numFaces, face, Nq;
4694 
4695     PetscCall(DMLabelGetStratumIS(key.label, key.value, &pointIS));
4696     if (!pointIS) goto end; /* No points with that id on this process */
4697     {
4698       IS isectIS;
4699 
4700       /* TODO: Special cases of ISIntersect where it is quick to check a priori if one is a superset of the other */
4701       PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS));
4702       PetscCall(ISDestroy(&pointIS));
4703       pointIS = isectIS;
4704     }
4705     PetscCall(ISGetLocalSize(pointIS, &numFaces));
4706     PetscCall(ISGetIndices(pointIS, &points));
4707     PetscCall(PetscMalloc4(numFaces * totDim, &u, locX_t ? numFaces * totDim : 0, &u_t, numFaces * totDim, &elemVec, locA ? numFaces * totDimAux : 0, &a));
4708     PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
4709     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom));
4710     if (!qGeom) {
4711       PetscFE fe;
4712 
4713       PetscCall(PetscDSGetDiscretization(prob, key.field, (PetscObject *)&fe));
4714       PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
4715       PetscCall(PetscObjectReference((PetscObject)qGeom));
4716     }
4717     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
4718     PetscCall(DMSNESGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
4719     for (face = 0; face < numFaces; ++face) {
4720       const PetscInt point = points[face], *support;
4721       PetscScalar   *x     = NULL;
4722       PetscInt       i;
4723 
4724       PetscCall(DMPlexGetSupport(dm, point, &support));
4725       PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
4726       for (i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
4727       PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
4728       if (locX_t) {
4729         PetscCall(DMPlexVecGetClosure(plex, section, locX_t, support[0], NULL, &x));
4730         for (i = 0; i < totDim; ++i) u_t[face * totDim + i] = x[i];
4731         PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, support[0], NULL, &x));
4732       }
4733       if (locA) {
4734         PetscInt subp;
4735 
4736         PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
4737         PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
4738         for (i = 0; i < totDimAux; ++i) a[face * totDimAux + i] = x[i];
4739         PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
4740       }
4741     }
4742     PetscCall(PetscArrayzero(elemVec, numFaces * totDim));
4743     {
4744       PetscFE      fe;
4745       PetscInt     Nb;
4746       PetscFEGeom *chunkGeom = NULL;
4747       /* Conforming batches */
4748       PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
4749       /* Remainder */
4750       PetscInt Nr, offset;
4751 
4752       PetscCall(PetscDSGetDiscretization(prob, key.field, (PetscObject *)&fe));
4753       PetscCall(PetscFEGetDimension(fe, &Nb));
4754       PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
4755       /* TODO: documentation is unclear about what is going on with these numbers: how should Nb / Nq factor in ? */
4756       blockSize = Nb;
4757       batchSize = numBlocks * blockSize;
4758       PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
4759       numChunks = numFaces / (numBatches * batchSize);
4760       Ne        = numChunks * numBatches * batchSize;
4761       Nr        = numFaces % (numBatches * batchSize);
4762       offset    = numFaces - Nr;
4763       PetscCall(PetscFEGeomGetChunk(fgeom, 0, offset, &chunkGeom));
4764       PetscCall(PetscFEIntegrateBdResidual(prob, wf, key, Ne, chunkGeom, u, u_t, probAux, a, t, elemVec));
4765       PetscCall(PetscFEGeomRestoreChunk(fgeom, 0, offset, &chunkGeom));
4766       PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom));
4767       PetscCall(PetscFEIntegrateBdResidual(prob, wf, key, Nr, chunkGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), probAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, &elemVec[offset * totDim]));
4768       PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom));
4769     }
4770     for (face = 0; face < numFaces; ++face) {
4771       const PetscInt point = points[face], *support;
4772 
4773       if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(point, "BdResidual", totDim, &elemVec[face * totDim]));
4774       PetscCall(DMPlexGetSupport(plex, point, &support));
4775       PetscCall(DMPlexVecSetClosure(plex, NULL, locF, support[0], &elemVec[face * totDim], ADD_ALL_VALUES));
4776     }
4777     PetscCall(DMSNESRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
4778     PetscCall(PetscQuadratureDestroy(&qGeom));
4779     PetscCall(ISRestoreIndices(pointIS, &points));
4780     PetscCall(ISDestroy(&pointIS));
4781     PetscCall(PetscFree4(u, u_t, elemVec, a));
4782   }
4783 end:
4784   PetscCall(DMDestroy(&plex));
4785   PetscCall(DMDestroy(&plexA));
4786   PetscFunctionReturn(PETSC_SUCCESS);
4787 }
4788 
4789 PetscErrorCode DMPlexComputeBdResidualSingle(DM dm, PetscReal t, PetscWeakForm wf, PetscFormKey key, Vec locX, Vec locX_t, Vec locF)
4790 {
4791   DMField  coordField;
4792   DMLabel  depthLabel;
4793   IS       facetIS;
4794   PetscInt dim;
4795 
4796   PetscFunctionBegin;
4797   PetscCall(DMGetDimension(dm, &dim));
4798   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
4799   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
4800   PetscCall(DMGetCoordinateField(dm, &coordField));
4801   PetscCall(DMPlexComputeBdResidual_Single_Internal(dm, t, wf, key, locX, locX_t, locF, coordField, facetIS));
4802   PetscCall(ISDestroy(&facetIS));
4803   PetscFunctionReturn(PETSC_SUCCESS);
4804 }
4805 
4806 static PetscErrorCode DMPlexComputeBdResidual_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
4807 {
4808   PetscDS  prob;
4809   PetscInt numBd, bd;
4810   DMField  coordField = NULL;
4811   IS       facetIS    = NULL;
4812   DMLabel  depthLabel;
4813   PetscInt dim;
4814 
4815   PetscFunctionBegin;
4816   PetscCall(DMGetDS(dm, &prob));
4817   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
4818   PetscCall(DMGetDimension(dm, &dim));
4819   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
4820   PetscCall(PetscDSGetNumBoundary(prob, &numBd));
4821   for (bd = 0; bd < numBd; ++bd) {
4822     PetscWeakForm           wf;
4823     DMBoundaryConditionType type;
4824     DMLabel                 label;
4825     const PetscInt         *values;
4826     PetscInt                field, numValues, v;
4827     PetscObject             obj;
4828     PetscClassId            id;
4829     PetscFormKey            key;
4830 
4831     PetscCall(PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &field, NULL, NULL, NULL, NULL, NULL));
4832     if (type & DM_BC_ESSENTIAL) continue;
4833     PetscCall(PetscDSGetDiscretization(prob, field, &obj));
4834     PetscCall(PetscObjectGetClassId(obj, &id));
4835     if (id != PETSCFE_CLASSID) continue;
4836     if (!facetIS) {
4837       DMLabel  depthLabel;
4838       PetscInt dim;
4839 
4840       PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
4841       PetscCall(DMGetDimension(dm, &dim));
4842       PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
4843     }
4844     PetscCall(DMGetCoordinateField(dm, &coordField));
4845     for (v = 0; v < numValues; ++v) {
4846       key.label = label;
4847       key.value = values[v];
4848       key.field = field;
4849       key.part  = 0;
4850       PetscCall(DMPlexComputeBdResidual_Single_Internal(dm, t, wf, key, locX, locX_t, locF, coordField, facetIS));
4851     }
4852   }
4853   PetscCall(ISDestroy(&facetIS));
4854   PetscFunctionReturn(PETSC_SUCCESS);
4855 }
4856 
4857 PetscErrorCode DMPlexComputeResidual_Internal(DM dm, PetscFormKey key, IS cellIS, PetscReal time, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
4858 {
4859   DM_Plex        *mesh       = (DM_Plex *)dm->data;
4860   const char     *name       = "Residual";
4861   DM              dmAux      = NULL;
4862   DM              dmGrad     = NULL;
4863   DMLabel         ghostLabel = NULL;
4864   PetscDS         ds         = NULL;
4865   PetscDS         dsAux      = NULL;
4866   PetscSection    section    = NULL;
4867   PetscBool       useFEM     = PETSC_FALSE;
4868   PetscBool       useFVM     = PETSC_FALSE;
4869   PetscBool       isImplicit = (locX_t || time == PETSC_MIN_REAL) ? PETSC_TRUE : PETSC_FALSE;
4870   PetscFV         fvm        = NULL;
4871   DMField         coordField = NULL;
4872   Vec             locA, cellGeometryFVM = NULL, faceGeometryFVM = NULL, locGrad = NULL;
4873   PetscScalar    *u = NULL, *u_t, *a, *uL, *uR;
4874   IS              chunkIS;
4875   const PetscInt *cells;
4876   PetscInt        cStart, cEnd, numCells;
4877   PetscInt        Nf, f, totDim, totDimAux, numChunks, cellChunkSize, faceChunkSize, chunk, fStart, fEnd;
4878   PetscInt        maxDegree  = PETSC_MAX_INT;
4879   PetscQuadrature affineQuad = NULL, *quads = NULL;
4880   PetscFEGeom    *affineGeom = NULL, **geoms = NULL;
4881 
4882   PetscFunctionBegin;
4883   PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
4884   if (!cellIS) goto end;
4885   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
4886   if (cStart >= cEnd) goto end;
4887   /* TODO The places where we have to use isFE are probably the member functions for the PetscDisc class */
4888   /* TODO The FVM geometry is over-manipulated. Make the precalc functions return exactly what we need */
4889   /* FEM+FVM */
4890   PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
4891   /* 1: Get sizes from dm and dmAux */
4892   PetscCall(DMGetLocalSection(dm, &section));
4893   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
4894   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, NULL));
4895   PetscCall(PetscDSGetNumFields(ds, &Nf));
4896   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
4897   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &locA));
4898   if (locA) {
4899     PetscInt subcell;
4900     PetscCall(VecGetDM(locA, &dmAux));
4901     PetscCall(DMGetEnclosurePoint(dmAux, dm, DM_ENC_UNKNOWN, cells ? cells[cStart] : cStart, &subcell));
4902     PetscCall(DMGetCellDS(dmAux, subcell, &dsAux, NULL));
4903     PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
4904   }
4905   /* 2: Get geometric data */
4906   for (f = 0; f < Nf; ++f) {
4907     PetscObject  obj;
4908     PetscClassId id;
4909     PetscBool    fimp;
4910 
4911     PetscCall(PetscDSGetImplicit(ds, f, &fimp));
4912     if (isImplicit != fimp) continue;
4913     PetscCall(PetscDSGetDiscretization(ds, f, &obj));
4914     PetscCall(PetscObjectGetClassId(obj, &id));
4915     if (id == PETSCFE_CLASSID) useFEM = PETSC_TRUE;
4916     if (id == PETSCFV_CLASSID) {
4917       useFVM = PETSC_TRUE;
4918       fvm    = (PetscFV)obj;
4919     }
4920   }
4921   if (useFEM) {
4922     PetscCall(DMGetCoordinateField(dm, &coordField));
4923     PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
4924     if (maxDegree <= 1) {
4925       PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad));
4926       if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
4927     } else {
4928       PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
4929       for (f = 0; f < Nf; ++f) {
4930         PetscObject  obj;
4931         PetscClassId id;
4932         PetscBool    fimp;
4933 
4934         PetscCall(PetscDSGetImplicit(ds, f, &fimp));
4935         if (isImplicit != fimp) continue;
4936         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
4937         PetscCall(PetscObjectGetClassId(obj, &id));
4938         if (id == PETSCFE_CLASSID) {
4939           PetscFE fe = (PetscFE)obj;
4940 
4941           PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
4942           PetscCall(PetscObjectReference((PetscObject)quads[f]));
4943           PetscCall(DMSNESGetFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
4944         }
4945       }
4946     }
4947   }
4948   // Handle non-essential (e.g. outflow) boundary values
4949   if (useFVM) {
4950     PetscCall(DMPlexInsertBoundaryValuesFVM(dm, fvm, locX, time, &locGrad));
4951     PetscCall(DMPlexGetGeometryFVM(dm, &faceGeometryFVM, &cellGeometryFVM, NULL));
4952     PetscCall(DMPlexGetGradientDM(dm, fvm, &dmGrad));
4953   }
4954   /* Loop over chunks */
4955   if (useFEM) PetscCall(ISCreate(PETSC_COMM_SELF, &chunkIS));
4956   numCells      = cEnd - cStart;
4957   numChunks     = 1;
4958   cellChunkSize = numCells / numChunks;
4959   faceChunkSize = (fEnd - fStart) / numChunks;
4960   numChunks     = PetscMin(1, numCells);
4961   for (chunk = 0; chunk < numChunks; ++chunk) {
4962     PetscScalar     *elemVec, *fluxL, *fluxR;
4963     PetscReal       *vol;
4964     PetscFVFaceGeom *fgeom;
4965     PetscInt         cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
4966     PetscInt         fS = fStart + chunk * faceChunkSize, fE = PetscMin(fS + faceChunkSize, fEnd), numFaces = 0, face;
4967 
4968     /* Extract field coefficients */
4969     if (useFEM) {
4970       PetscCall(ISGetPointSubrange(chunkIS, cS, cE, cells));
4971       PetscCall(DMPlexGetCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
4972       PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
4973       PetscCall(PetscArrayzero(elemVec, numCells * totDim));
4974     }
4975     if (useFVM) {
4976       PetscCall(DMPlexGetFaceFields(dm, fS, fE, locX, locX_t, faceGeometryFVM, cellGeometryFVM, locGrad, &numFaces, &uL, &uR));
4977       PetscCall(DMPlexGetFaceGeometry(dm, fS, fE, faceGeometryFVM, cellGeometryFVM, &numFaces, &fgeom, &vol));
4978       PetscCall(DMGetWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxL));
4979       PetscCall(DMGetWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxR));
4980       PetscCall(PetscArrayzero(fluxL, numFaces * totDim));
4981       PetscCall(PetscArrayzero(fluxR, numFaces * totDim));
4982     }
4983     /* TODO We will interlace both our field coefficients (u, u_t, uL, uR, etc.) and our output (elemVec, fL, fR). I think this works */
4984     /* Loop over fields */
4985     for (f = 0; f < Nf; ++f) {
4986       PetscObject  obj;
4987       PetscClassId id;
4988       PetscBool    fimp;
4989       PetscInt     numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;
4990 
4991       key.field = f;
4992       PetscCall(PetscDSGetImplicit(ds, f, &fimp));
4993       if (isImplicit != fimp) continue;
4994       PetscCall(PetscDSGetDiscretization(ds, f, &obj));
4995       PetscCall(PetscObjectGetClassId(obj, &id));
4996       if (id == PETSCFE_CLASSID) {
4997         PetscFE         fe        = (PetscFE)obj;
4998         PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[f];
4999         PetscFEGeom    *chunkGeom = NULL;
5000         PetscQuadrature quad      = affineQuad ? affineQuad : quads[f];
5001         PetscInt        Nq, Nb;
5002 
5003         PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5004         PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
5005         PetscCall(PetscFEGetDimension(fe, &Nb));
5006         blockSize = Nb;
5007         batchSize = numBlocks * blockSize;
5008         PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5009         numChunks = numCells / (numBatches * batchSize);
5010         Ne        = numChunks * numBatches * batchSize;
5011         Nr        = numCells % (numBatches * batchSize);
5012         offset    = numCells - Nr;
5013         /* Integrate FE residual to get elemVec (need fields at quadrature points) */
5014         /*   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) */
5015         PetscCall(PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom));
5016         PetscCall(PetscFEIntegrateResidual(ds, key, Ne, chunkGeom, u, u_t, dsAux, a, t, elemVec));
5017         PetscCall(PetscFEGeomGetChunk(geom, offset, numCells, &chunkGeom));
5018         PetscCall(PetscFEIntegrateResidual(ds, key, Nr, chunkGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), dsAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, &elemVec[offset * totDim]));
5019         PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &chunkGeom));
5020       } else if (id == PETSCFV_CLASSID) {
5021         PetscFV fv = (PetscFV)obj;
5022 
5023         Ne = numFaces;
5024         /* Riemann solve over faces (need fields at face centroids) */
5025         /*   We need to evaluate FE fields at those coordinates */
5026         PetscCall(PetscFVIntegrateRHSFunction(fv, ds, f, Ne, fgeom, vol, uL, uR, fluxL, fluxR));
5027       } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
5028     }
5029     /* Loop over domain */
5030     if (useFEM) {
5031       /* Add elemVec to locX */
5032       for (c = cS; c < cE; ++c) {
5033         const PetscInt cell = cells ? cells[c] : c;
5034         const PetscInt cind = c - cStart;
5035 
5036         if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, name, totDim, &elemVec[cind * totDim]));
5037         if (ghostLabel) {
5038           PetscInt ghostVal;
5039 
5040           PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
5041           if (ghostVal > 0) continue;
5042         }
5043         PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind * totDim], ADD_ALL_VALUES));
5044       }
5045     }
5046     if (useFVM) {
5047       PetscScalar *fa;
5048       PetscInt     iface;
5049 
5050       PetscCall(VecGetArray(locF, &fa));
5051       for (f = 0; f < Nf; ++f) {
5052         PetscFV      fv;
5053         PetscObject  obj;
5054         PetscClassId id;
5055         PetscInt     cdim, foff, pdim;
5056 
5057         PetscCall(DMGetCoordinateDim(dm, &cdim));
5058         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
5059         PetscCall(PetscDSGetFieldOffset(ds, f, &foff));
5060         PetscCall(PetscObjectGetClassId(obj, &id));
5061         if (id != PETSCFV_CLASSID) continue;
5062         fv = (PetscFV)obj;
5063         PetscCall(PetscFVGetNumComponents(fv, &pdim));
5064         /* Accumulate fluxes to cells */
5065         for (face = fS, iface = 0; face < fE; ++face) {
5066           const PetscInt *scells;
5067           PetscScalar    *fL = NULL, *fR = NULL;
5068           PetscInt        ghost, d, nsupp, nchild;
5069 
5070           PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
5071           PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
5072           PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
5073           if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
5074           PetscCall(DMPlexGetSupport(dm, face, &scells));
5075           PetscCall(DMLabelGetValue(ghostLabel, scells[0], &ghost));
5076           if (ghost <= 0) PetscCall(DMPlexPointLocalFieldRef(dm, scells[0], f, fa, &fL));
5077           PetscCall(DMLabelGetValue(ghostLabel, scells[1], &ghost));
5078           if (ghost <= 0) PetscCall(DMPlexPointLocalFieldRef(dm, scells[1], f, fa, &fR));
5079           if (mesh->printFVM > 1) {
5080             PetscCall(DMPrintCellVectorReal(face, "Residual: normal", cdim, fgeom[iface].normal));
5081             PetscCall(DMPrintCellVector(face, "Residual: left state", pdim, &uL[iface * totDim + foff]));
5082             PetscCall(DMPrintCellVector(face, "Residual: right state", pdim, &uR[iface * totDim + foff]));
5083             PetscCall(DMPrintCellVector(face, "Residual: left flux", pdim, &fluxL[iface * totDim + foff]));
5084             PetscCall(DMPrintCellVector(face, "Residual: right flux", pdim, &fluxR[iface * totDim + foff]));
5085           }
5086           for (d = 0; d < pdim; ++d) {
5087             if (fL) fL[d] -= fluxL[iface * totDim + foff + d];
5088             if (fR) fR[d] += fluxR[iface * totDim + foff + d];
5089           }
5090           ++iface;
5091         }
5092       }
5093       PetscCall(VecRestoreArray(locF, &fa));
5094     }
5095     /* Handle time derivative */
5096     if (locX_t) {
5097       PetscScalar *x_t, *fa;
5098 
5099       PetscCall(VecGetArray(locF, &fa));
5100       PetscCall(VecGetArray(locX_t, &x_t));
5101       for (f = 0; f < Nf; ++f) {
5102         PetscFV      fv;
5103         PetscObject  obj;
5104         PetscClassId id;
5105         PetscInt     pdim, d;
5106 
5107         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
5108         PetscCall(PetscObjectGetClassId(obj, &id));
5109         if (id != PETSCFV_CLASSID) continue;
5110         fv = (PetscFV)obj;
5111         PetscCall(PetscFVGetNumComponents(fv, &pdim));
5112         for (c = cS; c < cE; ++c) {
5113           const PetscInt cell = cells ? cells[c] : c;
5114           PetscScalar   *u_t, *r;
5115 
5116           if (ghostLabel) {
5117             PetscInt ghostVal;
5118 
5119             PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
5120             if (ghostVal > 0) continue;
5121           }
5122           PetscCall(DMPlexPointLocalFieldRead(dm, cell, f, x_t, &u_t));
5123           PetscCall(DMPlexPointLocalFieldRef(dm, cell, f, fa, &r));
5124           for (d = 0; d < pdim; ++d) r[d] += u_t[d];
5125         }
5126       }
5127       PetscCall(VecRestoreArray(locX_t, &x_t));
5128       PetscCall(VecRestoreArray(locF, &fa));
5129     }
5130     if (useFEM) {
5131       PetscCall(DMPlexRestoreCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
5132       PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
5133     }
5134     if (useFVM) {
5135       PetscCall(DMPlexRestoreFaceFields(dm, fS, fE, locX, locX_t, faceGeometryFVM, cellGeometryFVM, locGrad, &numFaces, &uL, &uR));
5136       PetscCall(DMPlexRestoreFaceGeometry(dm, fS, fE, faceGeometryFVM, cellGeometryFVM, &numFaces, &fgeom, &vol));
5137       PetscCall(DMRestoreWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxL));
5138       PetscCall(DMRestoreWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxR));
5139       if (dmGrad) PetscCall(DMRestoreLocalVector(dmGrad, &locGrad));
5140     }
5141   }
5142   if (useFEM) PetscCall(ISDestroy(&chunkIS));
5143   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
5144 
5145   if (useFEM) {
5146     PetscCall(DMPlexComputeBdResidual_Internal(dm, locX, locX_t, t, locF, user));
5147 
5148     if (maxDegree <= 1) {
5149       PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
5150       PetscCall(PetscQuadratureDestroy(&affineQuad));
5151     } else {
5152       for (f = 0; f < Nf; ++f) {
5153         PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
5154         PetscCall(PetscQuadratureDestroy(&quads[f]));
5155       }
5156       PetscCall(PetscFree2(quads, geoms));
5157     }
5158   }
5159 
5160   /* FEM */
5161   /* 1: Get sizes from dm and dmAux */
5162   /* 2: Get geometric data */
5163   /* 3: Handle boundary values */
5164   /* 4: Loop over domain */
5165   /*   Extract coefficients */
5166   /* Loop over fields */
5167   /*   Set tiling for FE*/
5168   /*   Integrate FE residual to get elemVec */
5169   /*     Loop over subdomain */
5170   /*       Loop over quad points */
5171   /*         Transform coords to real space */
5172   /*         Evaluate field and aux fields at point */
5173   /*         Evaluate residual at point */
5174   /*         Transform residual to real space */
5175   /*       Add residual to elemVec */
5176   /* Loop over domain */
5177   /*   Add elemVec to locX */
5178 
5179   /* FVM */
5180   /* Get geometric data */
5181   /* If using gradients */
5182   /*   Compute gradient data */
5183   /*   Loop over domain faces */
5184   /*     Count computational faces */
5185   /*     Reconstruct cell gradient */
5186   /*   Loop over domain cells */
5187   /*     Limit cell gradients */
5188   /* Handle boundary values */
5189   /* Loop over domain faces */
5190   /*   Read out field, centroid, normal, volume for each side of face */
5191   /* Riemann solve over faces */
5192   /* Loop over domain faces */
5193   /*   Accumulate fluxes to cells */
5194   /* TODO Change printFEM to printDisc here */
5195   if (mesh->printFEM) {
5196     Vec          locFbc;
5197     PetscInt     pStart, pEnd, p, maxDof;
5198     PetscScalar *zeroes;
5199 
5200     PetscCall(VecDuplicate(locF, &locFbc));
5201     PetscCall(VecCopy(locF, locFbc));
5202     PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5203     PetscCall(PetscSectionGetMaxDof(section, &maxDof));
5204     PetscCall(PetscCalloc1(maxDof, &zeroes));
5205     for (p = pStart; p < pEnd; p++) PetscCall(VecSetValuesSection(locFbc, section, p, zeroes, INSERT_BC_VALUES));
5206     PetscCall(PetscFree(zeroes));
5207     PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc));
5208     PetscCall(VecDestroy(&locFbc));
5209   }
5210 end:
5211   PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
5212   PetscFunctionReturn(PETSC_SUCCESS);
5213 }
5214 
5215 /*
5216   1) Allow multiple kernels for BdResidual for hybrid DS
5217 
5218   DONE 2) Get out dsAux for either side at the same time as cohesive cell dsAux
5219 
5220   DONE 3) Change DMGetCellFields() to get different aux data a[] for each side
5221      - I think I just need to replace a[] with the closure from each face
5222 
5223   4) Run both kernels for each non-hybrid field with correct dsAux, and then hybrid field as before
5224 */
5225 PetscErrorCode DMPlexComputeResidual_Hybrid_Internal(DM dm, PetscFormKey key[], IS cellIS, PetscReal time, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
5226 {
5227   DM_Plex        *mesh       = (DM_Plex *)dm->data;
5228   const char     *name       = "Hybrid Residual";
5229   DM              dmAux[3]   = {NULL, NULL, NULL};
5230   DMLabel         ghostLabel = NULL;
5231   PetscDS         ds         = NULL;
5232   PetscDS         dsIn       = NULL;
5233   PetscDS         dsAux[3]   = {NULL, NULL, NULL};
5234   Vec             locA[3]    = {NULL, NULL, NULL};
5235   DM              dmScale[3] = {NULL, NULL, NULL};
5236   PetscDS         dsScale[3] = {NULL, NULL, NULL};
5237   Vec             locS[3]    = {NULL, NULL, NULL};
5238   PetscSection    section    = NULL;
5239   DMField         coordField = NULL;
5240   PetscScalar    *a[3]       = {NULL, NULL, NULL};
5241   PetscScalar    *s[3]       = {NULL, NULL, NULL};
5242   PetscScalar    *u          = NULL, *u_t;
5243   PetscScalar    *elemVecNeg, *elemVecPos, *elemVecCoh;
5244   IS              chunkIS;
5245   const PetscInt *cells;
5246   PetscInt       *faces;
5247   PetscInt        cStart, cEnd, numCells;
5248   PetscInt        Nf, f, totDim, totDimIn, totDimAux[3], totDimScale[3], numChunks, cellChunkSize, chunk;
5249   PetscInt        maxDegree  = PETSC_MAX_INT;
5250   PetscQuadrature affineQuad = NULL, *quads = NULL;
5251   PetscFEGeom    *affineGeom = NULL, **geoms = NULL;
5252 
5253   PetscFunctionBegin;
5254   PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
5255   if (!cellIS) goto end;
5256   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
5257   PetscCall(ISGetLocalSize(cellIS, &numCells));
5258   if (cStart >= cEnd) goto end;
5259   if ((key[0].label == key[1].label) && (key[0].value == key[1].value) && (key[0].part == key[1].part)) {
5260     const char *name;
5261     PetscCall(PetscObjectGetName((PetscObject)key[0].label, &name));
5262     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);
5263   }
5264   /* TODO The places where we have to use isFE are probably the member functions for the PetscDisc class */
5265   /* FEM */
5266   /* 1: Get sizes from dm and dmAux */
5267   PetscCall(DMGetSection(dm, &section));
5268   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
5269   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn));
5270   PetscCall(PetscDSGetNumFields(ds, &Nf));
5271   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
5272   PetscCall(PetscDSGetTotalDimension(dsIn, &totDimIn));
5273   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, key[2].value, key[2].part, &locA[2]));
5274   if (locA[2]) {
5275     const PetscInt cellStart = cells ? cells[cStart] : cStart;
5276 
5277     PetscCall(VecGetDM(locA[2], &dmAux[2]));
5278     PetscCall(DMGetCellDS(dmAux[2], cellStart, &dsAux[2], NULL));
5279     PetscCall(PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]));
5280     {
5281       const PetscInt *cone;
5282       PetscInt        c;
5283 
5284       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
5285       for (c = 0; c < 2; ++c) {
5286         const PetscInt *support;
5287         PetscInt        ssize, s;
5288 
5289         PetscCall(DMPlexGetSupport(dm, cone[c], &support));
5290         PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
5291         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);
5292         if (support[0] == cellStart) s = 1;
5293         else if (support[1] == cellStart) s = 0;
5294         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
5295         PetscCall(DMGetAuxiliaryVec(dm, key[c].label, key[c].value, key[c].part, &locA[c]));
5296         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);
5297         if (locA[c]) PetscCall(VecGetDM(locA[c], &dmAux[c]));
5298         else dmAux[c] = dmAux[2];
5299         PetscCall(DMGetCellDS(dmAux[c], support[s], &dsAux[c], NULL));
5300         PetscCall(PetscDSGetTotalDimension(dsAux[c], &totDimAux[c]));
5301       }
5302     }
5303   }
5304   /* Handle mass matrix scaling
5305        The field in key[2] is the field to be scaled, and the scaling field is the first in the dsScale */
5306   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, -key[2].value, key[2].part, &locS[2]));
5307   if (locS[2]) {
5308     const PetscInt cellStart = cells ? cells[cStart] : cStart;
5309     PetscInt       Nb, Nbs;
5310 
5311     PetscCall(VecGetDM(locS[2], &dmScale[2]));
5312     PetscCall(DMGetCellDS(dmScale[2], cellStart, &dsScale[2], NULL));
5313     PetscCall(PetscDSGetTotalDimension(dsScale[2], &totDimScale[2]));
5314     // BRAD: This is not set correctly
5315     key[2].field = 2;
5316     PetscCall(PetscDSGetFieldSize(ds, key[2].field, &Nb));
5317     PetscCall(PetscDSGetFieldSize(dsScale[2], 0, &Nbs));
5318     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);
5319     {
5320       const PetscInt *cone;
5321       PetscInt        c;
5322 
5323       locS[1] = locS[0] = locS[2];
5324       dmScale[1] = dmScale[0] = dmScale[2];
5325       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
5326       for (c = 0; c < 2; ++c) {
5327         const PetscInt *support;
5328         PetscInt        ssize, s;
5329 
5330         PetscCall(DMPlexGetSupport(dm, cone[c], &support));
5331         PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
5332         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);
5333         if (support[0] == cellStart) s = 1;
5334         else if (support[1] == cellStart) s = 0;
5335         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
5336         PetscCall(DMGetCellDS(dmScale[c], support[s], &dsScale[c], NULL));
5337         PetscCall(PetscDSGetTotalDimension(dsScale[c], &totDimScale[c]));
5338       }
5339     }
5340   }
5341   /* 2: Setup geometric data */
5342   PetscCall(DMGetCoordinateField(dm, &coordField));
5343   PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
5344   if (maxDegree > 1) {
5345     PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
5346     for (f = 0; f < Nf; ++f) {
5347       PetscFE fe;
5348 
5349       PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
5350       if (fe) {
5351         PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
5352         PetscCall(PetscObjectReference((PetscObject)quads[f]));
5353       }
5354     }
5355   }
5356   /* Loop over chunks */
5357   cellChunkSize = numCells;
5358   numChunks     = !numCells ? 0 : PetscCeilReal(((PetscReal)numCells) / cellChunkSize);
5359   PetscCall(PetscCalloc1(2 * cellChunkSize, &faces));
5360   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 2 * cellChunkSize, faces, PETSC_USE_POINTER, &chunkIS));
5361   /* Extract field coefficients */
5362   /* NOTE This needs the end cap faces to have identical orientations */
5363   PetscCall(DMPlexGetHybridCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
5364   PetscCall(DMPlexGetHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
5365   PetscCall(DMPlexGetHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_TRUE, s));
5366   PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecNeg));
5367   PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecPos));
5368   PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecCoh));
5369   for (chunk = 0; chunk < numChunks; ++chunk) {
5370     PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
5371 
5372     PetscCall(PetscArrayzero(elemVecNeg, cellChunkSize * totDim));
5373     PetscCall(PetscArrayzero(elemVecPos, cellChunkSize * totDim));
5374     PetscCall(PetscArrayzero(elemVecCoh, cellChunkSize * totDim));
5375     /* Get faces */
5376     for (c = cS; c < cE; ++c) {
5377       const PetscInt  cell = cells ? cells[c] : c;
5378       const PetscInt *cone;
5379       PetscCall(DMPlexGetCone(dm, cell, &cone));
5380       faces[(c - cS) * 2 + 0] = cone[0];
5381       faces[(c - cS) * 2 + 1] = cone[1];
5382     }
5383     PetscCall(ISGeneralSetIndices(chunkIS, 2 * cellChunkSize, faces, PETSC_USE_POINTER));
5384     /* Get geometric data */
5385     if (maxDegree <= 1) {
5386       if (!affineQuad) PetscCall(DMFieldCreateDefaultQuadrature(coordField, chunkIS, &affineQuad));
5387       if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, affineQuad, PETSC_TRUE, &affineGeom));
5388     } else {
5389       for (f = 0; f < Nf; ++f) {
5390         if (quads[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, quads[f], PETSC_TRUE, &geoms[f]));
5391       }
5392     }
5393     /* Loop over fields */
5394     for (f = 0; f < Nf; ++f) {
5395       PetscFE         fe;
5396       PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[f];
5397       PetscFEGeom    *chunkGeom = NULL, *remGeom = NULL;
5398       PetscQuadrature quad = affineQuad ? affineQuad : quads[f];
5399       PetscInt        numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb;
5400       PetscBool       isCohesiveField;
5401 
5402       PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
5403       if (!fe) continue;
5404       PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5405       PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
5406       PetscCall(PetscFEGetDimension(fe, &Nb));
5407       blockSize = Nb;
5408       batchSize = numBlocks * blockSize;
5409       PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5410       numChunks = numCells / (numBatches * batchSize);
5411       Ne        = numChunks * numBatches * batchSize;
5412       Nr        = numCells % (numBatches * batchSize);
5413       offset    = numCells - Nr;
5414       PetscCall(PetscFEGeomGetChunk(geom, 0, offset * 2, &chunkGeom));
5415       PetscCall(PetscFEGeomGetChunk(geom, offset * 2, numCells * 2, &remGeom));
5416       PetscCall(PetscDSGetCohesive(ds, f, &isCohesiveField));
5417       chunkGeom->isCohesive = remGeom->isCohesive = PETSC_TRUE;
5418       key[0].field                                = f;
5419       key[1].field                                = f;
5420       key[2].field                                = f;
5421       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, elemVecNeg));
5422       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]));
5423       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, elemVecPos));
5424       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]));
5425       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, elemVecCoh));
5426       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]));
5427       PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &remGeom));
5428       PetscCall(PetscFEGeomRestoreChunk(geom, 0, offset, &chunkGeom));
5429     }
5430     /* Add elemVec to locX */
5431     for (c = cS; c < cE; ++c) {
5432       const PetscInt cell = cells ? cells[c] : c;
5433       const PetscInt cind = c - cStart;
5434       PetscInt       i;
5435 
5436       /* Scale element values */
5437       if (locS[0]) {
5438         PetscInt  Nb, off = cind * totDim, soff = cind * totDimScale[0];
5439         PetscBool cohesive;
5440 
5441         for (f = 0; f < Nf; ++f) {
5442           PetscCall(PetscDSGetFieldSize(ds, f, &Nb));
5443           PetscCall(PetscDSGetCohesive(ds, f, &cohesive));
5444           if (f == key[2].field) {
5445             PetscCheck(cohesive, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Scaling should not happen for face fields");
5446             // No cohesive scaling field is currently input
5447             for (i = 0; i < Nb; ++i) elemVecCoh[off + i] += s[0][soff + i] * elemVecNeg[off + i] + s[1][soff + i] * elemVecPos[off + i];
5448             off += Nb;
5449           } else {
5450             const PetscInt N = cohesive ? Nb : Nb * 2;
5451 
5452             for (i = 0; i < N; ++i) elemVecCoh[off + i] += elemVecNeg[off + i] + elemVecPos[off + i];
5453             off += N;
5454           }
5455         }
5456       } else {
5457         for (i = cind * totDim; i < (cind + 1) * totDim; ++i) elemVecCoh[i] += elemVecNeg[i] + elemVecPos[i];
5458       }
5459       if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, name, totDim, &elemVecCoh[cind * totDim]));
5460       if (ghostLabel) {
5461         PetscInt ghostVal;
5462 
5463         PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
5464         if (ghostVal > 0) continue;
5465       }
5466       PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVecCoh[cind * totDim], ADD_ALL_VALUES));
5467     }
5468   }
5469   PetscCall(DMPlexRestoreCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
5470   PetscCall(DMPlexRestoreHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
5471   PetscCall(DMPlexRestoreHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_TRUE, s));
5472   PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecNeg));
5473   PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecPos));
5474   PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecCoh));
5475   PetscCall(PetscFree(faces));
5476   PetscCall(ISDestroy(&chunkIS));
5477   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
5478   if (maxDegree <= 1) {
5479     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
5480     PetscCall(PetscQuadratureDestroy(&affineQuad));
5481   } else {
5482     for (f = 0; f < Nf; ++f) {
5483       if (geoms) PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
5484       if (quads) PetscCall(PetscQuadratureDestroy(&quads[f]));
5485     }
5486     PetscCall(PetscFree2(quads, geoms));
5487   }
5488   if (mesh->printFEM) {
5489     Vec          locFbc;
5490     PetscInt     pStart, pEnd, p, maxDof;
5491     PetscScalar *zeroes;
5492 
5493     PetscCall(VecDuplicate(locF, &locFbc));
5494     PetscCall(VecCopy(locF, locFbc));
5495     PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5496     PetscCall(PetscSectionGetMaxDof(section, &maxDof));
5497     PetscCall(PetscCalloc1(maxDof, &zeroes));
5498     for (p = pStart; p < pEnd; p++) PetscCall(VecSetValuesSection(locFbc, section, p, zeroes, INSERT_BC_VALUES));
5499     PetscCall(PetscFree(zeroes));
5500     PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc));
5501     PetscCall(VecDestroy(&locFbc));
5502   }
5503 end:
5504   PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
5505   PetscFunctionReturn(PETSC_SUCCESS);
5506 }
5507 
5508 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)
5509 {
5510   DM_Plex        *mesh = (DM_Plex *)dm->data;
5511   DM              plex = NULL, plexA = NULL, tdm;
5512   DMEnclosureType encAux;
5513   PetscDS         ds, dsAux           = NULL;
5514   PetscSection    section, sectionAux = NULL;
5515   PetscSection    globalSection;
5516   Vec             locA = NULL, tv;
5517   PetscScalar    *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL, *elemMatP = NULL;
5518   PetscInt        v;
5519   PetscInt        Nf, totDim, totDimAux = 0;
5520   PetscBool       hasJac = PETSC_FALSE, hasPrec = PETSC_FALSE, transform;
5521 
5522   PetscFunctionBegin;
5523   PetscCall(DMConvert(dm, DMPLEX, &plex));
5524   PetscCall(DMHasBasisTransform(dm, &transform));
5525   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
5526   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
5527   PetscCall(DMGetLocalSection(dm, &section));
5528   PetscCall(DMGetDS(dm, &ds));
5529   PetscCall(PetscDSGetNumFields(ds, &Nf));
5530   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
5531   PetscCall(PetscDSHasJacobian(ds, &hasJac));
5532   PetscCall(PetscDSHasJacobianPreconditioner(ds, &hasPrec));
5533   PetscCall(DMGetAuxiliaryVec(dm, label, values[0], 0, &locA));
5534   if (locA) {
5535     DM dmAux;
5536 
5537     PetscCall(VecGetDM(locA, &dmAux));
5538     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
5539     PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
5540     PetscCall(DMGetDS(plexA, &dsAux));
5541     PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
5542     PetscCall(DMGetLocalSection(plexA, &sectionAux));
5543   }
5544 
5545   PetscCall(DMGetGlobalSection(dm, &globalSection));
5546   for (v = 0; v < numValues; ++v) {
5547     PetscFEGeom    *fgeom;
5548     PetscInt        maxDegree;
5549     PetscQuadrature qGeom = NULL;
5550     IS              pointIS;
5551     const PetscInt *points;
5552     PetscFormKey    key;
5553     PetscInt        numFaces, face, Nq;
5554 
5555     key.label = label;
5556     key.value = values[v];
5557     key.part  = 0;
5558     PetscCall(DMLabelGetStratumIS(label, values[v], &pointIS));
5559     if (!pointIS) continue; /* No points with that id on this process */
5560     {
5561       IS isectIS;
5562 
5563       /* TODO: Special cases of ISIntersect where it is quick to check a prior if one is a superset of the other */
5564       PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS));
5565       PetscCall(ISDestroy(&pointIS));
5566       pointIS = isectIS;
5567     }
5568     PetscCall(ISGetLocalSize(pointIS, &numFaces));
5569     PetscCall(ISGetIndices(pointIS, &points));
5570     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));
5571     PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
5572     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom));
5573     if (!qGeom) {
5574       PetscFE fe;
5575 
5576       PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&fe));
5577       PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
5578       PetscCall(PetscObjectReference((PetscObject)qGeom));
5579     }
5580     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
5581     PetscCall(DMSNESGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
5582     for (face = 0; face < numFaces; ++face) {
5583       const PetscInt point = points[face], *support;
5584       PetscScalar   *x     = NULL;
5585       PetscInt       i;
5586 
5587       PetscCall(DMPlexGetSupport(dm, point, &support));
5588       PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
5589       for (i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
5590       PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
5591       if (locX_t) {
5592         PetscCall(DMPlexVecGetClosure(plex, section, locX_t, support[0], NULL, &x));
5593         for (i = 0; i < totDim; ++i) u_t[face * totDim + i] = x[i];
5594         PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, support[0], NULL, &x));
5595       }
5596       if (locA) {
5597         PetscInt subp;
5598         PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
5599         PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
5600         for (i = 0; i < totDimAux; ++i) a[face * totDimAux + i] = x[i];
5601         PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
5602       }
5603     }
5604     PetscCall(PetscArrayzero(elemMat, numFaces * totDim * totDim));
5605     {
5606       PetscFE  fe;
5607       PetscInt Nb;
5608       /* Conforming batches */
5609       PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
5610       /* Remainder */
5611       PetscFEGeom *chunkGeom = NULL;
5612       PetscInt     fieldJ, Nr, offset;
5613 
5614       PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&fe));
5615       PetscCall(PetscFEGetDimension(fe, &Nb));
5616       PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5617       blockSize = Nb;
5618       batchSize = numBlocks * blockSize;
5619       PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5620       numChunks = numFaces / (numBatches * batchSize);
5621       Ne        = numChunks * numBatches * batchSize;
5622       Nr        = numFaces % (numBatches * batchSize);
5623       offset    = numFaces - Nr;
5624       PetscCall(PetscFEGeomGetChunk(fgeom, 0, offset, &chunkGeom));
5625       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5626         key.field = fieldI * Nf + fieldJ;
5627         if (hasJac) PetscCall(PetscFEIntegrateBdJacobian(ds, wf, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, dsAux, a, t, X_tShift, elemMat));
5628         if (hasPrec) PetscCall(PetscFEIntegrateBdJacobian(ds, wf, PETSCFE_JACOBIAN_PRE, key, Ne, chunkGeom, u, u_t, dsAux, a, t, X_tShift, elemMatP));
5629       }
5630       PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom));
5631       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5632         key.field = fieldI * Nf + fieldJ;
5633         if (hasJac)
5634           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]));
5635         if (hasPrec)
5636           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]));
5637       }
5638       PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom));
5639     }
5640     for (face = 0; face < numFaces; ++face) {
5641       const PetscInt point = points[face], *support;
5642 
5643       /* Transform to global basis before insertion in Jacobian */
5644       PetscCall(DMPlexGetSupport(plex, point, &support));
5645       if (transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, support[0], PETSC_TRUE, totDim, &elemMat[face * totDim * totDim]));
5646       if (hasPrec) {
5647         if (hasJac) {
5648           if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(point, "BdJacobian", totDim, totDim, &elemMat[face * totDim * totDim]));
5649           PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, JacP, support[0], &elemMat[face * totDim * totDim], ADD_VALUES));
5650         }
5651         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(point, "BdJacobian", totDim, totDim, &elemMatP[face * totDim * totDim]));
5652         PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, JacP, support[0], &elemMatP[face * totDim * totDim], ADD_VALUES));
5653       } else {
5654         if (hasJac) {
5655           if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(point, "BdJacobian", totDim, totDim, &elemMat[face * totDim * totDim]));
5656           PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, JacP, support[0], &elemMat[face * totDim * totDim], ADD_VALUES));
5657         }
5658       }
5659     }
5660     PetscCall(DMSNESRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
5661     PetscCall(PetscQuadratureDestroy(&qGeom));
5662     PetscCall(ISRestoreIndices(pointIS, &points));
5663     PetscCall(ISDestroy(&pointIS));
5664     PetscCall(PetscFree5(u, u_t, elemMat, elemMatP, a));
5665   }
5666   if (plex) PetscCall(DMDestroy(&plex));
5667   if (plexA) PetscCall(DMDestroy(&plexA));
5668   PetscFunctionReturn(PETSC_SUCCESS);
5669 }
5670 
5671 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)
5672 {
5673   DMField  coordField;
5674   DMLabel  depthLabel;
5675   IS       facetIS;
5676   PetscInt dim;
5677 
5678   PetscFunctionBegin;
5679   PetscCall(DMGetDimension(dm, &dim));
5680   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5681   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
5682   PetscCall(DMGetCoordinateField(dm, &coordField));
5683   PetscCall(DMPlexComputeBdJacobian_Single_Internal(dm, t, wf, label, numValues, values, field, locX, locX_t, X_tShift, Jac, JacP, coordField, facetIS));
5684   PetscCall(ISDestroy(&facetIS));
5685   PetscFunctionReturn(PETSC_SUCCESS);
5686 }
5687 
5688 static PetscErrorCode DMPlexComputeBdJacobian_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, PetscReal X_tShift, Mat Jac, Mat JacP, void *user)
5689 {
5690   PetscDS  prob;
5691   PetscInt dim, numBd, bd;
5692   DMLabel  depthLabel;
5693   DMField  coordField = NULL;
5694   IS       facetIS;
5695 
5696   PetscFunctionBegin;
5697   PetscCall(DMGetDS(dm, &prob));
5698   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5699   PetscCall(DMGetDimension(dm, &dim));
5700   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
5701   PetscCall(PetscDSGetNumBoundary(prob, &numBd));
5702   PetscCall(DMGetCoordinateField(dm, &coordField));
5703   for (bd = 0; bd < numBd; ++bd) {
5704     PetscWeakForm           wf;
5705     DMBoundaryConditionType type;
5706     DMLabel                 label;
5707     const PetscInt         *values;
5708     PetscInt                fieldI, numValues;
5709     PetscObject             obj;
5710     PetscClassId            id;
5711 
5712     PetscCall(PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &fieldI, NULL, NULL, NULL, NULL, NULL));
5713     if (type & DM_BC_ESSENTIAL) continue;
5714     PetscCall(PetscDSGetDiscretization(prob, fieldI, &obj));
5715     PetscCall(PetscObjectGetClassId(obj, &id));
5716     if (id != PETSCFE_CLASSID) continue;
5717     PetscCall(DMPlexComputeBdJacobian_Single_Internal(dm, t, wf, label, numValues, values, fieldI, locX, locX_t, X_tShift, Jac, JacP, coordField, facetIS));
5718   }
5719   PetscCall(ISDestroy(&facetIS));
5720   PetscFunctionReturn(PETSC_SUCCESS);
5721 }
5722 
5723 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)
5724 {
5725   DM_Plex        *mesh  = (DM_Plex *)dm->data;
5726   const char     *name  = "Jacobian";
5727   DM              dmAux = NULL, plex, tdm;
5728   DMEnclosureType encAux;
5729   Vec             A, tv;
5730   DMField         coordField;
5731   PetscDS         prob, probAux = NULL;
5732   PetscSection    section, globalSection, sectionAux;
5733   PetscScalar    *elemMat, *elemMatP, *elemMatD, *u, *u_t, *a = NULL;
5734   const PetscInt *cells;
5735   PetscInt        Nf, fieldI, fieldJ;
5736   PetscInt        totDim, totDimAux = 0, cStart, cEnd, numCells, c;
5737   PetscBool       hasJac = PETSC_FALSE, hasPrec = PETSC_FALSE, hasDyn, hasFV = PETSC_FALSE, transform;
5738 
5739   PetscFunctionBegin;
5740   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
5741   if (!cellIS) goto end;
5742   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
5743   PetscCall(ISGetLocalSize(cellIS, &numCells));
5744   if (cStart >= cEnd) goto end;
5745   PetscCall(DMHasBasisTransform(dm, &transform));
5746   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
5747   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
5748   PetscCall(DMGetLocalSection(dm, &section));
5749   PetscCall(DMGetGlobalSection(dm, &globalSection));
5750   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL));
5751   PetscCall(PetscDSGetNumFields(prob, &Nf));
5752   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
5753   PetscCall(PetscDSHasJacobian(prob, &hasJac));
5754   PetscCall(PetscDSHasJacobianPreconditioner(prob, &hasPrec));
5755   /* user passed in the same matrix, avoid double contributions and
5756      only assemble the Jacobian */
5757   if (hasJac && Jac == JacP) hasPrec = PETSC_FALSE;
5758   PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
5759   hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
5760   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &A));
5761   if (A) {
5762     PetscCall(VecGetDM(A, &dmAux));
5763     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
5764     PetscCall(DMConvert(dmAux, DMPLEX, &plex));
5765     PetscCall(DMGetLocalSection(plex, &sectionAux));
5766     PetscCall(DMGetDS(dmAux, &probAux));
5767     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
5768   }
5769   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));
5770   if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
5771   PetscCall(DMGetCoordinateField(dm, &coordField));
5772   for (c = cStart; c < cEnd; ++c) {
5773     const PetscInt cell = cells ? cells[c] : c;
5774     const PetscInt cind = c - cStart;
5775     PetscScalar   *x = NULL, *x_t = NULL;
5776     PetscInt       i;
5777 
5778     PetscCall(DMPlexVecGetClosure(dm, section, X, cell, NULL, &x));
5779     for (i = 0; i < totDim; ++i) u[cind * totDim + i] = x[i];
5780     PetscCall(DMPlexVecRestoreClosure(dm, section, X, cell, NULL, &x));
5781     if (X_t) {
5782       PetscCall(DMPlexVecGetClosure(dm, section, X_t, cell, NULL, &x_t));
5783       for (i = 0; i < totDim; ++i) u_t[cind * totDim + i] = x_t[i];
5784       PetscCall(DMPlexVecRestoreClosure(dm, section, X_t, cell, NULL, &x_t));
5785     }
5786     if (dmAux) {
5787       PetscInt subcell;
5788       PetscCall(DMGetEnclosurePoint(dmAux, dm, encAux, cell, &subcell));
5789       PetscCall(DMPlexVecGetClosure(plex, sectionAux, A, subcell, NULL, &x));
5790       for (i = 0; i < totDimAux; ++i) a[cind * totDimAux + i] = x[i];
5791       PetscCall(DMPlexVecRestoreClosure(plex, sectionAux, A, subcell, NULL, &x));
5792     }
5793   }
5794   if (hasJac) PetscCall(PetscArrayzero(elemMat, numCells * totDim * totDim));
5795   if (hasPrec) PetscCall(PetscArrayzero(elemMatP, numCells * totDim * totDim));
5796   if (hasDyn) PetscCall(PetscArrayzero(elemMatD, numCells * totDim * totDim));
5797   for (fieldI = 0; fieldI < Nf; ++fieldI) {
5798     PetscClassId    id;
5799     PetscFE         fe;
5800     PetscQuadrature qGeom = NULL;
5801     PetscInt        Nb;
5802     /* Conforming batches */
5803     PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
5804     /* Remainder */
5805     PetscInt     Nr, offset, Nq;
5806     PetscInt     maxDegree;
5807     PetscFEGeom *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL;
5808 
5809     PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
5810     PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
5811     if (id == PETSCFV_CLASSID) {
5812       hasFV = PETSC_TRUE;
5813       continue;
5814     }
5815     PetscCall(PetscFEGetDimension(fe, &Nb));
5816     PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5817     PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
5818     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
5819     if (!qGeom) {
5820       PetscCall(PetscFEGetQuadrature(fe, &qGeom));
5821       PetscCall(PetscObjectReference((PetscObject)qGeom));
5822     }
5823     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
5824     PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
5825     blockSize = Nb;
5826     batchSize = numBlocks * blockSize;
5827     PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5828     numChunks = numCells / (numBatches * batchSize);
5829     Ne        = numChunks * numBatches * batchSize;
5830     Nr        = numCells % (numBatches * batchSize);
5831     offset    = numCells - Nr;
5832     PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
5833     PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &remGeom));
5834     for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5835       key.field = fieldI * Nf + fieldJ;
5836       if (hasJac) {
5837         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat));
5838         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]));
5839       }
5840       if (hasPrec) {
5841         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatP));
5842         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]));
5843       }
5844       if (hasDyn) {
5845         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatD));
5846         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]));
5847       }
5848     }
5849     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &remGeom));
5850     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, 0, offset, &chunkGeom));
5851     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
5852     PetscCall(PetscQuadratureDestroy(&qGeom));
5853   }
5854   /*   Add contribution from X_t */
5855   if (hasDyn) {
5856     for (c = 0; c < numCells * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
5857   }
5858   if (hasFV) {
5859     PetscClassId id;
5860     PetscFV      fv;
5861     PetscInt     offsetI, NcI, NbI = 1, fc, f;
5862 
5863     for (fieldI = 0; fieldI < Nf; ++fieldI) {
5864       PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fv));
5865       PetscCall(PetscDSGetFieldOffset(prob, fieldI, &offsetI));
5866       PetscCall(PetscObjectGetClassId((PetscObject)fv, &id));
5867       if (id != PETSCFV_CLASSID) continue;
5868       /* Put in the identity */
5869       PetscCall(PetscFVGetNumComponents(fv, &NcI));
5870       for (c = cStart; c < cEnd; ++c) {
5871         const PetscInt cind    = c - cStart;
5872         const PetscInt eOffset = cind * totDim * totDim;
5873         for (fc = 0; fc < NcI; ++fc) {
5874           for (f = 0; f < NbI; ++f) {
5875             const PetscInt i = offsetI + f * NcI + fc;
5876             if (hasPrec) {
5877               if (hasJac) elemMat[eOffset + i * totDim + i] = 1.0;
5878               elemMatP[eOffset + i * totDim + i] = 1.0;
5879             } else {
5880               elemMat[eOffset + i * totDim + i] = 1.0;
5881             }
5882           }
5883         }
5884       }
5885     }
5886     /* No allocated space for FV stuff, so ignore the zero entries */
5887     PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE));
5888   }
5889   /* Insert values into matrix */
5890   for (c = cStart; c < cEnd; ++c) {
5891     const PetscInt cell = cells ? cells[c] : c;
5892     const PetscInt cind = c - cStart;
5893 
5894     /* Transform to global basis before insertion in Jacobian */
5895     if (transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, cell, PETSC_TRUE, totDim, &elemMat[cind * totDim * totDim]));
5896     if (hasPrec) {
5897       if (hasJac) {
5898         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
5899         PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, Jac, cell, &elemMat[cind * totDim * totDim], ADD_VALUES));
5900       }
5901       if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatP[cind * totDim * totDim]));
5902       PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, JacP, cell, &elemMatP[cind * totDim * totDim], ADD_VALUES));
5903     } else {
5904       if (hasJac) {
5905         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
5906         PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, JacP, cell, &elemMat[cind * totDim * totDim], ADD_VALUES));
5907       }
5908     }
5909   }
5910   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
5911   if (hasFV) PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE));
5912   PetscCall(PetscFree5(u, u_t, elemMat, elemMatP, elemMatD));
5913   if (dmAux) {
5914     PetscCall(PetscFree(a));
5915     PetscCall(DMDestroy(&plex));
5916   }
5917   /* Compute boundary integrals */
5918   PetscCall(DMPlexComputeBdJacobian_Internal(dm, X, X_t, t, X_tShift, Jac, JacP, user));
5919   /* Assemble matrix */
5920 end : {
5921   PetscBool assOp = hasJac && hasPrec ? PETSC_TRUE : PETSC_FALSE, gassOp;
5922 
5923   PetscCall(MPIU_Allreduce(&assOp, &gassOp, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
5924   if (hasJac && hasPrec) {
5925     PetscCall(MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY));
5926     PetscCall(MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY));
5927   }
5928 }
5929   PetscCall(MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY));
5930   PetscCall(MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY));
5931   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
5932   PetscFunctionReturn(PETSC_SUCCESS);
5933 }
5934 
5935 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)
5936 {
5937   DM_Plex        *mesh          = (DM_Plex *)dm->data;
5938   const char     *name          = "Hybrid Jacobian";
5939   DM              dmAux[3]      = {NULL, NULL, NULL};
5940   DMLabel         ghostLabel    = NULL;
5941   DM              plex          = NULL;
5942   DM              plexA         = NULL;
5943   PetscDS         ds            = NULL;
5944   PetscDS         dsIn          = NULL;
5945   PetscDS         dsAux[3]      = {NULL, NULL, NULL};
5946   Vec             locA[3]       = {NULL, NULL, NULL};
5947   DM              dmScale[3]    = {NULL, NULL, NULL};
5948   PetscDS         dsScale[3]    = {NULL, NULL, NULL};
5949   Vec             locS[3]       = {NULL, NULL, NULL};
5950   PetscSection    section       = NULL;
5951   PetscSection    sectionAux[3] = {NULL, NULL, NULL};
5952   DMField         coordField    = NULL;
5953   PetscScalar    *a[3]          = {NULL, NULL, NULL};
5954   PetscScalar    *s[3]          = {NULL, NULL, NULL};
5955   PetscScalar    *u             = NULL, *u_t;
5956   PetscScalar    *elemMatNeg, *elemMatPos, *elemMatCoh;
5957   PetscScalar    *elemMatNegP, *elemMatPosP, *elemMatCohP;
5958   PetscSection    globalSection;
5959   IS              chunkIS;
5960   const PetscInt *cells;
5961   PetscInt       *faces;
5962   PetscInt        cStart, cEnd, numCells;
5963   PetscInt        Nf, fieldI, fieldJ, totDim, totDimIn, totDimAux[3], totDimScale[3], numChunks, cellChunkSize, chunk;
5964   PetscInt        maxDegree  = PETSC_MAX_INT;
5965   PetscQuadrature affineQuad = NULL, *quads = NULL;
5966   PetscFEGeom    *affineGeom = NULL, **geoms = NULL;
5967   PetscBool       hasBdJac, hasBdPrec;
5968 
5969   PetscFunctionBegin;
5970   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
5971   if (!cellIS) goto end;
5972   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
5973   PetscCall(ISGetLocalSize(cellIS, &numCells));
5974   if (cStart >= cEnd) goto end;
5975   if ((key[0].label == key[1].label) && (key[0].value == key[1].value) && (key[0].part == key[1].part)) {
5976     const char *name;
5977     PetscCall(PetscObjectGetName((PetscObject)key[0].label, &name));
5978     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);
5979   }
5980   PetscCall(DMConvert(dm, DMPLEX, &plex));
5981   PetscCall(DMGetSection(dm, &section));
5982   PetscCall(DMGetGlobalSection(dm, &globalSection));
5983   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
5984   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn));
5985   PetscCall(PetscDSGetNumFields(ds, &Nf));
5986   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
5987   PetscCall(PetscDSGetTotalDimension(dsIn, &totDimIn));
5988   PetscCall(PetscDSHasBdJacobian(ds, &hasBdJac));
5989   PetscCall(PetscDSHasBdJacobianPreconditioner(ds, &hasBdPrec));
5990   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, key[2].value, key[2].part, &locA[2]));
5991   if (locA[2]) {
5992     const PetscInt cellStart = cells ? cells[cStart] : cStart;
5993 
5994     PetscCall(VecGetDM(locA[2], &dmAux[2]));
5995     PetscCall(DMConvert(dmAux[2], DMPLEX, &plexA));
5996     PetscCall(DMGetSection(dmAux[2], &sectionAux[2]));
5997     PetscCall(DMGetCellDS(dmAux[2], cellStart, &dsAux[2], NULL));
5998     PetscCall(PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]));
5999     {
6000       const PetscInt *cone;
6001       PetscInt        c;
6002 
6003       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
6004       for (c = 0; c < 2; ++c) {
6005         const PetscInt *support;
6006         PetscInt        ssize, s;
6007 
6008         PetscCall(DMPlexGetSupport(dm, cone[c], &support));
6009         PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
6010         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);
6011         if (support[0] == cellStart) s = 1;
6012         else if (support[1] == cellStart) s = 0;
6013         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
6014         PetscCall(DMGetAuxiliaryVec(dm, key[c].label, key[c].value, key[c].part, &locA[c]));
6015         if (locA[c]) PetscCall(VecGetDM(locA[c], &dmAux[c]));
6016         else dmAux[c] = dmAux[2];
6017         PetscCall(DMGetCellDS(dmAux[c], support[s], &dsAux[c], NULL));
6018         PetscCall(PetscDSGetTotalDimension(dsAux[c], &totDimAux[c]));
6019       }
6020     }
6021   }
6022   /* Handle mass matrix scaling
6023        The field in key[2] is the field to be scaled, and the scaling field is the first in the dsScale */
6024   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, -key[2].value, key[2].part, &locS[2]));
6025   if (locS[2]) {
6026     const PetscInt cellStart = cells ? cells[cStart] : cStart;
6027     PetscInt       Nb, Nbs;
6028 
6029     PetscCall(VecGetDM(locS[2], &dmScale[2]));
6030     PetscCall(DMGetCellDS(dmScale[2], cells ? cells[cStart] : cStart, &dsScale[2], NULL));
6031     PetscCall(PetscDSGetTotalDimension(dsScale[2], &totDimScale[2]));
6032     // BRAD: This is not set correctly
6033     key[2].field = 2;
6034     PetscCall(PetscDSGetFieldSize(ds, key[2].field, &Nb));
6035     PetscCall(PetscDSGetFieldSize(dsScale[2], 0, &Nbs));
6036     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);
6037     {
6038       const PetscInt *cone;
6039       PetscInt        c;
6040 
6041       locS[1] = locS[0] = locS[2];
6042       dmScale[1] = dmScale[0] = dmScale[2];
6043       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
6044       for (c = 0; c < 2; ++c) {
6045         const PetscInt *support;
6046         PetscInt        ssize, s;
6047 
6048         PetscCall(DMPlexGetSupport(dm, cone[c], &support));
6049         PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
6050         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);
6051         if (support[0] == cellStart) s = 1;
6052         else if (support[1] == cellStart) s = 0;
6053         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
6054         PetscCall(DMGetCellDS(dmScale[c], support[s], &dsScale[c], NULL));
6055         PetscCall(PetscDSGetTotalDimension(dsScale[c], &totDimScale[c]));
6056       }
6057     }
6058   }
6059   /* 2: Setup geometric data */
6060   PetscCall(DMGetCoordinateField(dm, &coordField));
6061   PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
6062   if (maxDegree > 1) {
6063     PetscInt f;
6064     PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
6065     for (f = 0; f < Nf; ++f) {
6066       PetscFE fe;
6067 
6068       PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
6069       if (fe) {
6070         PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
6071         PetscCall(PetscObjectReference((PetscObject)quads[f]));
6072       }
6073     }
6074   }
6075   /* Loop over chunks */
6076   cellChunkSize = numCells;
6077   numChunks     = !numCells ? 0 : PetscCeilReal(((PetscReal)numCells) / cellChunkSize);
6078   PetscCall(PetscCalloc1(2 * cellChunkSize, &faces));
6079   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 1 * cellChunkSize, faces, PETSC_USE_POINTER, &chunkIS));
6080   /* Extract field coefficients */
6081   /* NOTE This needs the end cap faces to have identical orientations */
6082   PetscCall(DMPlexGetHybridCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
6083   PetscCall(DMPlexGetHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
6084   PetscCall(DMPlexGetHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_TRUE, s));
6085   PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNeg));
6086   PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPos));
6087   PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCoh));
6088   PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNegP));
6089   PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPosP));
6090   PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCohP));
6091   for (chunk = 0; chunk < numChunks; ++chunk) {
6092     PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
6093 
6094     if (hasBdJac) {
6095       PetscCall(PetscArrayzero(elemMatNeg, cellChunkSize * totDim * totDim));
6096       PetscCall(PetscArrayzero(elemMatPos, cellChunkSize * totDim * totDim));
6097       PetscCall(PetscArrayzero(elemMatCoh, cellChunkSize * totDim * totDim));
6098     }
6099     if (hasBdPrec) {
6100       PetscCall(PetscArrayzero(elemMatNegP, cellChunkSize * totDim * totDim));
6101       PetscCall(PetscArrayzero(elemMatPosP, cellChunkSize * totDim * totDim));
6102       PetscCall(PetscArrayzero(elemMatCohP, cellChunkSize * totDim * totDim));
6103     }
6104     /* Get faces */
6105     for (c = cS; c < cE; ++c) {
6106       const PetscInt  cell = cells ? cells[c] : c;
6107       const PetscInt *cone;
6108       PetscCall(DMPlexGetCone(plex, cell, &cone));
6109       faces[(c - cS) * 2 + 0] = cone[0];
6110       faces[(c - cS) * 2 + 1] = cone[1];
6111     }
6112     PetscCall(ISGeneralSetIndices(chunkIS, 2 * cellChunkSize, faces, PETSC_USE_POINTER));
6113     if (maxDegree <= 1) {
6114       if (!affineQuad) PetscCall(DMFieldCreateDefaultQuadrature(coordField, chunkIS, &affineQuad));
6115       if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, affineQuad, PETSC_TRUE, &affineGeom));
6116     } else {
6117       PetscInt f;
6118       for (f = 0; f < Nf; ++f) {
6119         if (quads[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, quads[f], PETSC_TRUE, &geoms[f]));
6120       }
6121     }
6122 
6123     for (fieldI = 0; fieldI < Nf; ++fieldI) {
6124       PetscFE         feI;
6125       PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[fieldI];
6126       PetscFEGeom    *chunkGeom = NULL, *remGeom = NULL;
6127       PetscQuadrature quad = affineQuad ? affineQuad : quads[fieldI];
6128       PetscInt        numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb;
6129       PetscBool       isCohesiveField;
6130 
6131       PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&feI));
6132       if (!feI) continue;
6133       PetscCall(PetscFEGetTileSizes(feI, NULL, &numBlocks, NULL, &numBatches));
6134       PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
6135       PetscCall(PetscFEGetDimension(feI, &Nb));
6136       blockSize = Nb;
6137       batchSize = numBlocks * blockSize;
6138       PetscCall(PetscFESetTileSizes(feI, blockSize, numBlocks, batchSize, numBatches));
6139       numChunks = numCells / (numBatches * batchSize);
6140       Ne        = numChunks * numBatches * batchSize;
6141       Nr        = numCells % (numBatches * batchSize);
6142       offset    = numCells - Nr;
6143       PetscCall(PetscFEGeomGetChunk(geom, 0, offset * 2, &chunkGeom));
6144       PetscCall(PetscFEGeomGetChunk(geom, offset * 2, numCells * 2, &remGeom));
6145       PetscCall(PetscDSGetCohesive(ds, fieldI, &isCohesiveField));
6146       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
6147         PetscFE feJ;
6148 
6149         PetscCall(PetscDSGetDiscretization(ds, fieldJ, (PetscObject *)&feJ));
6150         if (!feJ) continue;
6151         key[0].field = fieldI * Nf + fieldJ;
6152         key[1].field = fieldI * Nf + fieldJ;
6153         key[2].field = fieldI * Nf + fieldJ;
6154         if (hasBdJac) {
6155           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, X_tShift, elemMatNeg));
6156           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]));
6157           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, X_tShift, elemMatPos));
6158           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]));
6159         }
6160         if (hasBdPrec) {
6161           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, X_tShift, elemMatNegP));
6162           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]));
6163           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, X_tShift, elemMatPosP));
6164           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]));
6165         }
6166         if (hasBdJac) {
6167           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, X_tShift, elemMatCoh));
6168           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]));
6169         }
6170         if (hasBdPrec) {
6171           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, X_tShift, elemMatCohP));
6172           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]));
6173         }
6174       }
6175       PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &remGeom));
6176       PetscCall(PetscFEGeomRestoreChunk(geom, 0, offset, &chunkGeom));
6177     }
6178     /* Insert values into matrix */
6179     for (c = cS; c < cE; ++c) {
6180       const PetscInt cell = cells ? cells[c] : c;
6181       const PetscInt cind = c - cS, coff = cind * totDim * totDim;
6182       PetscInt       i, j;
6183 
6184       /* Scale element values */
6185       if (locS[0]) {
6186         PetscInt  Nb, soff = cind * totDimScale[0], off = 0;
6187         PetscBool cohesive;
6188 
6189         for (fieldI = 0; fieldI < Nf; ++fieldI) {
6190           PetscCall(PetscDSGetFieldSize(ds, fieldI, &Nb));
6191           PetscCall(PetscDSGetCohesive(ds, fieldI, &cohesive));
6192 
6193           if (fieldI == key[2].field) {
6194             PetscCheck(cohesive, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Scaling should not happen for face fields");
6195             for (i = 0; i < Nb; ++i) {
6196               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];
6197               if (hasBdPrec)
6198                 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];
6199             }
6200             off += Nb;
6201           } else {
6202             const PetscInt N = cohesive ? Nb : Nb * 2;
6203 
6204             for (i = 0; i < N; ++i) {
6205               for (j = 0; j < totDim; ++j) elemMatCoh[coff + (off + i) * totDim + j] += elemMatNeg[coff + (off + i) * totDim + j] + elemMatPos[coff + (off + i) * totDim + j];
6206               if (hasBdPrec)
6207                 for (j = 0; j < totDim; ++j) elemMatCohP[coff + (off + i) * totDim + j] += elemMatNegP[coff + (off + i) * totDim + j] + elemMatPosP[coff + (off + i) * totDim + j];
6208             }
6209             off += N;
6210           }
6211         }
6212       } else {
6213         for (i = 0; i < totDim * totDim; ++i) elemMatCoh[coff + i] += elemMatNeg[coff + i] + elemMatPos[coff + i];
6214         if (hasBdPrec)
6215           for (i = 0; i < totDim * totDim; ++i) elemMatCohP[coff + i] += elemMatNegP[coff + i] + elemMatPosP[coff + i];
6216       }
6217       if (hasBdPrec) {
6218         if (hasBdJac) {
6219           if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCoh[cind * totDim * totDim]));
6220           PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, Jac, cell, &elemMatCoh[cind * totDim * totDim], ADD_VALUES));
6221         }
6222         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCohP[cind * totDim * totDim]));
6223         PetscCall(DMPlexMatSetClosure(plex, section, globalSection, JacP, cell, &elemMatCohP[cind * totDim * totDim], ADD_VALUES));
6224       } else if (hasBdJac) {
6225         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCoh[cind * totDim * totDim]));
6226         PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, JacP, cell, &elemMatCoh[cind * totDim * totDim], ADD_VALUES));
6227       }
6228     }
6229   }
6230   PetscCall(DMPlexRestoreCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
6231   PetscCall(DMPlexRestoreHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
6232   PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNeg));
6233   PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPos));
6234   PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCoh));
6235   PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNegP));
6236   PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPosP));
6237   PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCohP));
6238   PetscCall(PetscFree(faces));
6239   PetscCall(ISDestroy(&chunkIS));
6240   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
6241   if (maxDegree <= 1) {
6242     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
6243     PetscCall(PetscQuadratureDestroy(&affineQuad));
6244   } else {
6245     PetscInt f;
6246     for (f = 0; f < Nf; ++f) {
6247       if (geoms) PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
6248       if (quads) PetscCall(PetscQuadratureDestroy(&quads[f]));
6249     }
6250     PetscCall(PetscFree2(quads, geoms));
6251   }
6252   if (dmAux[2]) PetscCall(DMDestroy(&plexA));
6253   PetscCall(DMDestroy(&plex));
6254 end:
6255   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
6256   PetscFunctionReturn(PETSC_SUCCESS);
6257 }
6258 
6259 /*
6260   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.
6261 
6262   Input Parameters:
6263 + dm     - The mesh
6264 . key    - The PetscWeakFormKey indicating where integration should happen
6265 . cellIS - The cells to integrate over
6266 . t      - The time
6267 . X_tShift - The multiplier for the Jacobian with respect to X_t
6268 . X      - Local solution vector
6269 . X_t    - Time-derivative of the local solution vector
6270 . Y      - Local input vector
6271 - user   - the user context
6272 
6273   Output Parameter:
6274 . Z - Local output vector
6275 
6276   Note:
6277   We form the residual one batch of elements at a time. This allows us to offload work onto an accelerator,
6278   like a GPU, or vectorize on a multicore machine.
6279 */
6280 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)
6281 {
6282   DM_Plex        *mesh  = (DM_Plex *)dm->data;
6283   const char     *name  = "Jacobian";
6284   DM              dmAux = NULL, plex, plexAux = NULL;
6285   DMEnclosureType encAux;
6286   Vec             A;
6287   DMField         coordField;
6288   PetscDS         prob, probAux = NULL;
6289   PetscQuadrature quad;
6290   PetscSection    section, globalSection, sectionAux;
6291   PetscScalar    *elemMat, *elemMatD, *u, *u_t, *a = NULL, *y, *z;
6292   const PetscInt *cells;
6293   PetscInt        Nf, fieldI, fieldJ;
6294   PetscInt        totDim, totDimAux = 0, cStart, cEnd, numCells, c;
6295   PetscBool       hasDyn;
6296 
6297   PetscFunctionBegin;
6298   if (!cellIS) PetscFunctionReturn(PETSC_SUCCESS);
6299   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
6300   PetscCall(DMConvert(dm, DMPLEX, &plex));
6301   PetscCall(ISGetLocalSize(cellIS, &numCells));
6302   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
6303   PetscCall(DMGetLocalSection(dm, &section));
6304   PetscCall(DMGetGlobalSection(dm, &globalSection));
6305   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL));
6306   PetscCall(PetscDSGetNumFields(prob, &Nf));
6307   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
6308   PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
6309   hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
6310   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &A));
6311   if (A) {
6312     PetscCall(VecGetDM(A, &dmAux));
6313     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
6314     PetscCall(DMConvert(dmAux, DMPLEX, &plexAux));
6315     PetscCall(DMGetLocalSection(plexAux, &sectionAux));
6316     PetscCall(DMGetDS(dmAux, &probAux));
6317     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
6318   }
6319   PetscCall(VecSet(Z, 0.0));
6320   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));
6321   if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
6322   PetscCall(DMGetCoordinateField(dm, &coordField));
6323   for (c = cStart; c < cEnd; ++c) {
6324     const PetscInt cell = cells ? cells[c] : c;
6325     const PetscInt cind = c - cStart;
6326     PetscScalar   *x = NULL, *x_t = NULL;
6327     PetscInt       i;
6328 
6329     PetscCall(DMPlexVecGetClosure(plex, section, X, cell, NULL, &x));
6330     for (i = 0; i < totDim; ++i) u[cind * totDim + i] = x[i];
6331     PetscCall(DMPlexVecRestoreClosure(plex, section, X, cell, NULL, &x));
6332     if (X_t) {
6333       PetscCall(DMPlexVecGetClosure(plex, section, X_t, cell, NULL, &x_t));
6334       for (i = 0; i < totDim; ++i) u_t[cind * totDim + i] = x_t[i];
6335       PetscCall(DMPlexVecRestoreClosure(plex, section, X_t, cell, NULL, &x_t));
6336     }
6337     if (dmAux) {
6338       PetscInt subcell;
6339       PetscCall(DMGetEnclosurePoint(dmAux, dm, encAux, cell, &subcell));
6340       PetscCall(DMPlexVecGetClosure(plexAux, sectionAux, A, subcell, NULL, &x));
6341       for (i = 0; i < totDimAux; ++i) a[cind * totDimAux + i] = x[i];
6342       PetscCall(DMPlexVecRestoreClosure(plexAux, sectionAux, A, subcell, NULL, &x));
6343     }
6344     PetscCall(DMPlexVecGetClosure(plex, section, Y, cell, NULL, &x));
6345     for (i = 0; i < totDim; ++i) y[cind * totDim + i] = x[i];
6346     PetscCall(DMPlexVecRestoreClosure(plex, section, Y, cell, NULL, &x));
6347   }
6348   PetscCall(PetscArrayzero(elemMat, numCells * totDim * totDim));
6349   if (hasDyn) PetscCall(PetscArrayzero(elemMatD, numCells * totDim * totDim));
6350   for (fieldI = 0; fieldI < Nf; ++fieldI) {
6351     PetscFE  fe;
6352     PetscInt Nb;
6353     /* Conforming batches */
6354     PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
6355     /* Remainder */
6356     PetscInt        Nr, offset, Nq;
6357     PetscQuadrature qGeom = NULL;
6358     PetscInt        maxDegree;
6359     PetscFEGeom    *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL;
6360 
6361     PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
6362     PetscCall(PetscFEGetQuadrature(fe, &quad));
6363     PetscCall(PetscFEGetDimension(fe, &Nb));
6364     PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
6365     PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
6366     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
6367     if (!qGeom) {
6368       PetscCall(PetscFEGetQuadrature(fe, &qGeom));
6369       PetscCall(PetscObjectReference((PetscObject)qGeom));
6370     }
6371     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
6372     PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
6373     blockSize = Nb;
6374     batchSize = numBlocks * blockSize;
6375     PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
6376     numChunks = numCells / (numBatches * batchSize);
6377     Ne        = numChunks * numBatches * batchSize;
6378     Nr        = numCells % (numBatches * batchSize);
6379     offset    = numCells - Nr;
6380     PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
6381     PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &remGeom));
6382     for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
6383       key.field = fieldI * Nf + fieldJ;
6384       PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat));
6385       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]));
6386       if (hasDyn) {
6387         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatD));
6388         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]));
6389       }
6390     }
6391     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &remGeom));
6392     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, 0, offset, &chunkGeom));
6393     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
6394     PetscCall(PetscQuadratureDestroy(&qGeom));
6395   }
6396   if (hasDyn) {
6397     for (c = 0; c < numCells * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
6398   }
6399   for (c = cStart; c < cEnd; ++c) {
6400     const PetscInt     cell = cells ? cells[c] : c;
6401     const PetscInt     cind = c - cStart;
6402     const PetscBLASInt M = totDim, one = 1;
6403     const PetscScalar  a = 1.0, b = 0.0;
6404 
6405     PetscCallBLAS("BLASgemv", BLASgemv_("N", &M, &M, &a, &elemMat[cind * totDim * totDim], &M, &y[cind * totDim], &one, &b, z, &one));
6406     if (mesh->printFEM > 1) {
6407       PetscCall(DMPrintCellMatrix(c, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
6408       PetscCall(DMPrintCellVector(c, "Y", totDim, &y[cind * totDim]));
6409       PetscCall(DMPrintCellVector(c, "Z", totDim, z));
6410     }
6411     PetscCall(DMPlexVecSetClosure(dm, section, Z, cell, z, ADD_VALUES));
6412   }
6413   PetscCall(PetscFree6(u, u_t, elemMat, elemMatD, y, z));
6414   if (mesh->printFEM) {
6415     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)Z), "Z:\n"));
6416     PetscCall(VecView(Z, NULL));
6417   }
6418   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
6419   PetscCall(PetscFree(a));
6420   PetscCall(DMDestroy(&plexAux));
6421   PetscCall(DMDestroy(&plex));
6422   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
6423   PetscFunctionReturn(PETSC_SUCCESS);
6424 }
6425