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