xref: /petsc/src/dm/impls/plex/plexfem.c (revision be664eb1aa8937d3d8706d421fb3e5a6fb48db75)
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 = NULL;
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, PetscSafePointerPlusOffset(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, PetscSafePointerPlusOffset(a, offset * totDimAux), &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 = PetscSafePointerPlusOffset(*u_t, cind * totDim);
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], PetscSafePointerPlusOffset(u_t, offset * totDim), 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], PetscSafePointerPlusOffset(u_t, offset * totDim), probAux, PetscSafePointerPlusOffset(a, offset * totDimAux), 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   PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
4875   if (!cellIS) goto end;
4876   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
4877   if (cStart >= cEnd) goto end;
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], PetscSafePointerPlusOffset(u_t, offset * totDim), dsAux, PetscSafePointerPlusOffset(a, offset * totDimAux), 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 end:
5202   PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
5203   PetscFunctionReturn(PETSC_SUCCESS);
5204 }
5205 
5206 /*
5207   1) Allow multiple kernels for BdResidual for hybrid DS
5208 
5209   DONE 2) Get out dsAux for either side at the same time as cohesive cell dsAux
5210 
5211   DONE 3) Change DMGetCellFields() to get different aux data a[] for each side
5212      - I think I just need to replace a[] with the closure from each face
5213 
5214   4) Run both kernels for each non-hybrid field with correct dsAux, and then hybrid field as before
5215 */
5216 PetscErrorCode DMPlexComputeResidual_Hybrid_Internal(DM dm, PetscFormKey key[], IS cellIS, PetscReal time, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
5217 {
5218   DM_Plex        *mesh       = (DM_Plex *)dm->data;
5219   const char     *name       = "Hybrid Residual";
5220   DM              dmAux[3]   = {NULL, NULL, NULL};
5221   DMLabel         ghostLabel = NULL;
5222   PetscDS         ds         = NULL;
5223   PetscDS         dsIn       = NULL;
5224   PetscDS         dsAux[3]   = {NULL, NULL, NULL};
5225   Vec             locA[3]    = {NULL, NULL, NULL};
5226   DM              dmScale[3] = {NULL, NULL, NULL};
5227   PetscDS         dsScale[3] = {NULL, NULL, NULL};
5228   Vec             locS[3]    = {NULL, NULL, NULL};
5229   PetscSection    section    = NULL;
5230   DMField         coordField = NULL;
5231   PetscScalar    *a[3]       = {NULL, NULL, NULL};
5232   PetscScalar    *s[3]       = {NULL, NULL, NULL};
5233   PetscScalar    *u          = NULL, *u_t;
5234   PetscScalar    *elemVecNeg, *elemVecPos, *elemVecCoh;
5235   IS              chunkIS;
5236   const PetscInt *cells;
5237   PetscInt       *faces;
5238   PetscInt        cStart, cEnd, numCells;
5239   PetscInt        Nf, f, totDim, totDimIn, totDimAux[3], totDimScale[3], numChunks, cellChunkSize, chunk;
5240   PetscInt        maxDegree  = PETSC_MAX_INT;
5241   PetscQuadrature affineQuad = NULL, *quads = NULL;
5242   PetscFEGeom    *affineGeom = NULL, **geoms = NULL;
5243 
5244   PetscFunctionBegin;
5245   PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
5246   if (!cellIS) goto end;
5247   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
5248   PetscCall(ISGetLocalSize(cellIS, &numCells));
5249   if (cStart >= cEnd) goto end;
5250   if ((key[0].label == key[1].label) && (key[0].value == key[1].value) && (key[0].part == key[1].part)) {
5251     const char *name;
5252     PetscCall(PetscObjectGetName((PetscObject)key[0].label, &name));
5253     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);
5254   }
5255   /* TODO The places where we have to use isFE are probably the member functions for the PetscDisc class */
5256   /* FEM */
5257   /* 1: Get sizes from dm and dmAux */
5258   PetscCall(DMGetSection(dm, &section));
5259   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
5260   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn));
5261   PetscCall(PetscDSGetNumFields(ds, &Nf));
5262   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
5263   PetscCall(PetscDSGetTotalDimension(dsIn, &totDimIn));
5264   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, key[2].value, key[2].part, &locA[2]));
5265   if (locA[2]) {
5266     const PetscInt cellStart = cells ? cells[cStart] : cStart;
5267 
5268     PetscCall(VecGetDM(locA[2], &dmAux[2]));
5269     PetscCall(DMGetCellDS(dmAux[2], cellStart, &dsAux[2], NULL));
5270     PetscCall(PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]));
5271     {
5272       const PetscInt *cone;
5273       PetscInt        c;
5274 
5275       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
5276       for (c = 0; c < 2; ++c) {
5277         const PetscInt *support;
5278         PetscInt        ssize, s;
5279 
5280         PetscCall(DMPlexGetSupport(dm, cone[c], &support));
5281         PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
5282         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);
5283         if (support[0] == cellStart) s = 1;
5284         else if (support[1] == cellStart) s = 0;
5285         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
5286         PetscCall(DMGetAuxiliaryVec(dm, key[c].label, key[c].value, key[c].part, &locA[c]));
5287         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);
5288         if (locA[c]) PetscCall(VecGetDM(locA[c], &dmAux[c]));
5289         else dmAux[c] = dmAux[2];
5290         PetscCall(DMGetCellDS(dmAux[c], support[s], &dsAux[c], NULL));
5291         PetscCall(PetscDSGetTotalDimension(dsAux[c], &totDimAux[c]));
5292       }
5293     }
5294   }
5295   /* Handle mass matrix scaling
5296        The field in key[2] is the field to be scaled, and the scaling field is the first in the dsScale */
5297   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, -key[2].value, key[2].part, &locS[2]));
5298   if (locS[2]) {
5299     const PetscInt cellStart = cells ? cells[cStart] : cStart;
5300     PetscInt       Nb, Nbs;
5301 
5302     PetscCall(VecGetDM(locS[2], &dmScale[2]));
5303     PetscCall(DMGetCellDS(dmScale[2], cellStart, &dsScale[2], NULL));
5304     PetscCall(PetscDSGetTotalDimension(dsScale[2], &totDimScale[2]));
5305     // BRAD: This is not set correctly
5306     key[2].field = 2;
5307     PetscCall(PetscDSGetFieldSize(ds, key[2].field, &Nb));
5308     PetscCall(PetscDSGetFieldSize(dsScale[2], 0, &Nbs));
5309     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);
5310     {
5311       const PetscInt *cone;
5312       PetscInt        c;
5313 
5314       locS[1] = locS[0] = locS[2];
5315       dmScale[1] = dmScale[0] = dmScale[2];
5316       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
5317       for (c = 0; c < 2; ++c) {
5318         const PetscInt *support;
5319         PetscInt        ssize, s;
5320 
5321         PetscCall(DMPlexGetSupport(dm, cone[c], &support));
5322         PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
5323         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);
5324         if (support[0] == cellStart) s = 1;
5325         else if (support[1] == cellStart) s = 0;
5326         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
5327         PetscCall(DMGetCellDS(dmScale[c], support[s], &dsScale[c], NULL));
5328         PetscCall(PetscDSGetTotalDimension(dsScale[c], &totDimScale[c]));
5329       }
5330     }
5331   }
5332   /* 2: Setup geometric data */
5333   PetscCall(DMGetCoordinateField(dm, &coordField));
5334   PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
5335   if (maxDegree > 1) {
5336     PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
5337     for (f = 0; f < Nf; ++f) {
5338       PetscFE fe;
5339 
5340       PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
5341       if (fe) {
5342         PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
5343         PetscCall(PetscObjectReference((PetscObject)quads[f]));
5344       }
5345     }
5346   }
5347   /* Loop over chunks */
5348   cellChunkSize = numCells;
5349   numChunks     = !numCells ? 0 : PetscCeilReal(((PetscReal)numCells) / cellChunkSize);
5350   PetscCall(PetscCalloc1(2 * cellChunkSize, &faces));
5351   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 2 * cellChunkSize, faces, PETSC_USE_POINTER, &chunkIS));
5352   /* Extract field coefficients */
5353   /* NOTE This needs the end cap faces to have identical orientations */
5354   PetscCall(DMPlexGetHybridCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
5355   PetscCall(DMPlexGetHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
5356   PetscCall(DMPlexGetHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_TRUE, s));
5357   PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecNeg));
5358   PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecPos));
5359   PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecCoh));
5360   for (chunk = 0; chunk < numChunks; ++chunk) {
5361     PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
5362 
5363     PetscCall(PetscArrayzero(elemVecNeg, cellChunkSize * totDim));
5364     PetscCall(PetscArrayzero(elemVecPos, cellChunkSize * totDim));
5365     PetscCall(PetscArrayzero(elemVecCoh, cellChunkSize * totDim));
5366     /* Get faces */
5367     for (c = cS; c < cE; ++c) {
5368       const PetscInt  cell = cells ? cells[c] : c;
5369       const PetscInt *cone;
5370       PetscCall(DMPlexGetCone(dm, cell, &cone));
5371       faces[(c - cS) * 2 + 0] = cone[0];
5372       faces[(c - cS) * 2 + 1] = cone[1];
5373     }
5374     PetscCall(ISGeneralSetIndices(chunkIS, 2 * cellChunkSize, faces, PETSC_USE_POINTER));
5375     /* Get geometric data */
5376     if (maxDegree <= 1) {
5377       if (!affineQuad) PetscCall(DMFieldCreateDefaultQuadrature(coordField, chunkIS, &affineQuad));
5378       if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, affineQuad, PETSC_TRUE, &affineGeom));
5379     } else {
5380       for (f = 0; f < Nf; ++f) {
5381         if (quads[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, quads[f], PETSC_TRUE, &geoms[f]));
5382       }
5383     }
5384     /* Loop over fields */
5385     for (f = 0; f < Nf; ++f) {
5386       PetscFE         fe;
5387       PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[f];
5388       PetscFEGeom    *chunkGeom = NULL, *remGeom = NULL;
5389       PetscQuadrature quad = affineQuad ? affineQuad : quads[f];
5390       PetscInt        numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb;
5391       PetscBool       isCohesiveField;
5392 
5393       PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
5394       if (!fe) continue;
5395       PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5396       PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
5397       PetscCall(PetscFEGetDimension(fe, &Nb));
5398       blockSize = Nb;
5399       batchSize = numBlocks * blockSize;
5400       PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5401       numChunks = numCells / (numBatches * batchSize);
5402       Ne        = numChunks * numBatches * batchSize;
5403       Nr        = numCells % (numBatches * batchSize);
5404       offset    = numCells - Nr;
5405       PetscCall(PetscFEGeomGetChunk(geom, 0, offset * 2, &chunkGeom));
5406       PetscCall(PetscFEGeomGetChunk(geom, offset * 2, numCells * 2, &remGeom));
5407       PetscCall(PetscDSGetCohesive(ds, f, &isCohesiveField));
5408       chunkGeom->isCohesive = remGeom->isCohesive = PETSC_TRUE;
5409       key[0].field                                = f;
5410       key[1].field                                = f;
5411       key[2].field                                = f;
5412       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, elemVecNeg));
5413       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[0], 0, Nr, remGeom, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[0], PetscSafePointerPlusOffset(a[0], offset * totDimAux[0]), t, &elemVecNeg[offset * totDim]));
5414       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, elemVecPos));
5415       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[1], 1, Nr, remGeom, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[1], PetscSafePointerPlusOffset(a[1], offset * totDimAux[1]), t, &elemVecPos[offset * totDim]));
5416       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, elemVecCoh));
5417       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[2], 2, Nr, remGeom, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[2], PetscSafePointerPlusOffset(a[2], offset * totDimAux[2]), t, &elemVecCoh[offset * totDim]));
5418       PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &remGeom));
5419       PetscCall(PetscFEGeomRestoreChunk(geom, 0, offset, &chunkGeom));
5420     }
5421     /* Add elemVec to locX */
5422     for (c = cS; c < cE; ++c) {
5423       const PetscInt cell = cells ? cells[c] : c;
5424       const PetscInt cind = c - cStart;
5425       PetscInt       i;
5426 
5427       /* Scale element values */
5428       if (locS[0]) {
5429         PetscInt  Nb, off = cind * totDim, soff = cind * totDimScale[0];
5430         PetscBool cohesive;
5431 
5432         for (f = 0; f < Nf; ++f) {
5433           PetscCall(PetscDSGetFieldSize(ds, f, &Nb));
5434           PetscCall(PetscDSGetCohesive(ds, f, &cohesive));
5435           if (f == key[2].field) {
5436             PetscCheck(cohesive, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Scaling should not happen for face fields");
5437             // No cohesive scaling field is currently input
5438             for (i = 0; i < Nb; ++i) elemVecCoh[off + i] += s[0][soff + i] * elemVecNeg[off + i] + s[1][soff + i] * elemVecPos[off + i];
5439             off += Nb;
5440           } else {
5441             const PetscInt N = cohesive ? Nb : Nb * 2;
5442 
5443             for (i = 0; i < N; ++i) elemVecCoh[off + i] += elemVecNeg[off + i] + elemVecPos[off + i];
5444             off += N;
5445           }
5446         }
5447       } else {
5448         for (i = cind * totDim; i < (cind + 1) * totDim; ++i) elemVecCoh[i] += elemVecNeg[i] + elemVecPos[i];
5449       }
5450       if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, name, totDim, &elemVecCoh[cind * totDim]));
5451       if (ghostLabel) {
5452         PetscInt ghostVal;
5453 
5454         PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
5455         if (ghostVal > 0) continue;
5456       }
5457       PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVecCoh[cind * totDim], ADD_ALL_VALUES));
5458     }
5459   }
5460   PetscCall(DMPlexRestoreCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
5461   PetscCall(DMPlexRestoreHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
5462   PetscCall(DMPlexRestoreHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_TRUE, s));
5463   PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecNeg));
5464   PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecPos));
5465   PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecCoh));
5466   PetscCall(PetscFree(faces));
5467   PetscCall(ISDestroy(&chunkIS));
5468   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
5469   if (maxDegree <= 1) {
5470     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
5471     PetscCall(PetscQuadratureDestroy(&affineQuad));
5472   } else {
5473     for (f = 0; f < Nf; ++f) {
5474       if (geoms) PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
5475       if (quads) PetscCall(PetscQuadratureDestroy(&quads[f]));
5476     }
5477     PetscCall(PetscFree2(quads, geoms));
5478   }
5479   if (mesh->printFEM) {
5480     Vec          locFbc;
5481     PetscInt     pStart, pEnd, p, maxDof;
5482     PetscScalar *zeroes;
5483 
5484     PetscCall(VecDuplicate(locF, &locFbc));
5485     PetscCall(VecCopy(locF, locFbc));
5486     PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5487     PetscCall(PetscSectionGetMaxDof(section, &maxDof));
5488     PetscCall(PetscCalloc1(maxDof, &zeroes));
5489     for (p = pStart; p < pEnd; p++) PetscCall(VecSetValuesSection(locFbc, section, p, zeroes, INSERT_BC_VALUES));
5490     PetscCall(PetscFree(zeroes));
5491     PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc));
5492     PetscCall(VecDestroy(&locFbc));
5493   }
5494 end:
5495   PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
5496   PetscFunctionReturn(PETSC_SUCCESS);
5497 }
5498 
5499 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)
5500 {
5501   DM_Plex        *mesh = (DM_Plex *)dm->data;
5502   DM              plex = NULL, plexA = NULL, tdm;
5503   DMEnclosureType encAux;
5504   PetscDS         ds, dsAux           = NULL;
5505   PetscSection    section, sectionAux = NULL;
5506   PetscSection    globalSection;
5507   Vec             locA = NULL, tv;
5508   PetscScalar    *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL, *elemMatP = NULL;
5509   PetscInt        v;
5510   PetscInt        Nf, totDim, totDimAux = 0;
5511   PetscBool       hasJac = PETSC_FALSE, hasPrec = PETSC_FALSE, transform;
5512 
5513   PetscFunctionBegin;
5514   PetscCall(DMConvert(dm, DMPLEX, &plex));
5515   PetscCall(DMHasBasisTransform(dm, &transform));
5516   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
5517   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
5518   PetscCall(DMGetLocalSection(dm, &section));
5519   PetscCall(DMGetDS(dm, &ds));
5520   PetscCall(PetscDSGetNumFields(ds, &Nf));
5521   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
5522   PetscCall(PetscDSHasJacobian(ds, &hasJac));
5523   PetscCall(PetscDSHasJacobianPreconditioner(ds, &hasPrec));
5524   PetscCall(DMGetAuxiliaryVec(dm, label, values[0], 0, &locA));
5525   if (locA) {
5526     DM dmAux;
5527 
5528     PetscCall(VecGetDM(locA, &dmAux));
5529     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
5530     PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
5531     PetscCall(DMGetDS(plexA, &dsAux));
5532     PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
5533     PetscCall(DMGetLocalSection(plexA, &sectionAux));
5534   }
5535 
5536   PetscCall(DMGetGlobalSection(dm, &globalSection));
5537   for (v = 0; v < numValues; ++v) {
5538     PetscFEGeom    *fgeom;
5539     PetscInt        maxDegree;
5540     PetscQuadrature qGeom = NULL;
5541     IS              pointIS;
5542     const PetscInt *points;
5543     PetscFormKey    key;
5544     PetscInt        numFaces, face, Nq;
5545 
5546     key.label = label;
5547     key.value = values[v];
5548     key.part  = 0;
5549     PetscCall(DMLabelGetStratumIS(label, values[v], &pointIS));
5550     if (!pointIS) continue; /* No points with that id on this process */
5551     {
5552       IS isectIS;
5553 
5554       /* TODO: Special cases of ISIntersect where it is quick to check a prior if one is a superset of the other */
5555       PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS));
5556       PetscCall(ISDestroy(&pointIS));
5557       pointIS = isectIS;
5558     }
5559     PetscCall(ISGetLocalSize(pointIS, &numFaces));
5560     PetscCall(ISGetIndices(pointIS, &points));
5561     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));
5562     PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
5563     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom));
5564     if (!qGeom) {
5565       PetscFE fe;
5566 
5567       PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&fe));
5568       PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
5569       PetscCall(PetscObjectReference((PetscObject)qGeom));
5570     }
5571     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
5572     PetscCall(DMSNESGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
5573     for (face = 0; face < numFaces; ++face) {
5574       const PetscInt point = points[face], *support;
5575       PetscScalar   *x     = NULL;
5576       PetscInt       i;
5577 
5578       PetscCall(DMPlexGetSupport(dm, point, &support));
5579       PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
5580       for (i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
5581       PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
5582       if (locX_t) {
5583         PetscCall(DMPlexVecGetClosure(plex, section, locX_t, support[0], NULL, &x));
5584         for (i = 0; i < totDim; ++i) u_t[face * totDim + i] = x[i];
5585         PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, support[0], NULL, &x));
5586       }
5587       if (locA) {
5588         PetscInt subp;
5589         PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
5590         PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
5591         for (i = 0; i < totDimAux; ++i) a[face * totDimAux + i] = x[i];
5592         PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
5593       }
5594     }
5595     PetscCall(PetscArrayzero(elemMat, numFaces * totDim * totDim));
5596     {
5597       PetscFE  fe;
5598       PetscInt Nb;
5599       /* Conforming batches */
5600       PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
5601       /* Remainder */
5602       PetscFEGeom *chunkGeom = NULL;
5603       PetscInt     fieldJ, Nr, offset;
5604 
5605       PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&fe));
5606       PetscCall(PetscFEGetDimension(fe, &Nb));
5607       PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5608       blockSize = Nb;
5609       batchSize = numBlocks * blockSize;
5610       PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5611       numChunks = numFaces / (numBatches * batchSize);
5612       Ne        = numChunks * numBatches * batchSize;
5613       Nr        = numFaces % (numBatches * batchSize);
5614       offset    = numFaces - Nr;
5615       PetscCall(PetscFEGeomGetChunk(fgeom, 0, offset, &chunkGeom));
5616       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5617         key.field = fieldI * Nf + fieldJ;
5618         if (hasJac) PetscCall(PetscFEIntegrateBdJacobian(ds, wf, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, dsAux, a, t, X_tShift, elemMat));
5619         if (hasPrec) PetscCall(PetscFEIntegrateBdJacobian(ds, wf, PETSCFE_JACOBIAN_PRE, key, Ne, chunkGeom, u, u_t, dsAux, a, t, X_tShift, elemMatP));
5620       }
5621       PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom));
5622       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5623         key.field = fieldI * Nf + fieldJ;
5624         if (hasJac)
5625           PetscCall(PetscFEIntegrateBdJacobian(ds, wf, PETSCFE_JACOBIAN, key, Nr, chunkGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), dsAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, X_tShift, &elemMat[offset * totDim * totDim]));
5626         if (hasPrec)
5627           PetscCall(PetscFEIntegrateBdJacobian(ds, wf, PETSCFE_JACOBIAN_PRE, key, Nr, chunkGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), dsAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, X_tShift, &elemMatP[offset * totDim * totDim]));
5628       }
5629       PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom));
5630     }
5631     for (face = 0; face < numFaces; ++face) {
5632       const PetscInt point = points[face], *support;
5633 
5634       /* Transform to global basis before insertion in Jacobian */
5635       PetscCall(DMPlexGetSupport(plex, point, &support));
5636       if (transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, support[0], PETSC_TRUE, totDim, &elemMat[face * totDim * totDim]));
5637       if (hasPrec) {
5638         if (hasJac) {
5639           if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(point, "BdJacobian", totDim, totDim, &elemMat[face * totDim * totDim]));
5640           PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, JacP, support[0], &elemMat[face * totDim * totDim], ADD_VALUES));
5641         }
5642         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(point, "BdJacobian", totDim, totDim, &elemMatP[face * totDim * totDim]));
5643         PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, JacP, support[0], &elemMatP[face * totDim * totDim], ADD_VALUES));
5644       } else {
5645         if (hasJac) {
5646           if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(point, "BdJacobian", totDim, totDim, &elemMat[face * totDim * totDim]));
5647           PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, JacP, support[0], &elemMat[face * totDim * totDim], ADD_VALUES));
5648         }
5649       }
5650     }
5651     PetscCall(DMSNESRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
5652     PetscCall(PetscQuadratureDestroy(&qGeom));
5653     PetscCall(ISRestoreIndices(pointIS, &points));
5654     PetscCall(ISDestroy(&pointIS));
5655     PetscCall(PetscFree5(u, u_t, elemMat, elemMatP, a));
5656   }
5657   if (plex) PetscCall(DMDestroy(&plex));
5658   if (plexA) PetscCall(DMDestroy(&plexA));
5659   PetscFunctionReturn(PETSC_SUCCESS);
5660 }
5661 
5662 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)
5663 {
5664   DMField  coordField;
5665   DMLabel  depthLabel;
5666   IS       facetIS;
5667   PetscInt dim;
5668 
5669   PetscFunctionBegin;
5670   PetscCall(DMGetDimension(dm, &dim));
5671   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5672   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
5673   PetscCall(DMGetCoordinateField(dm, &coordField));
5674   PetscCall(DMPlexComputeBdJacobian_Single_Internal(dm, t, wf, label, numValues, values, field, locX, locX_t, X_tShift, Jac, JacP, coordField, facetIS));
5675   PetscCall(ISDestroy(&facetIS));
5676   PetscFunctionReturn(PETSC_SUCCESS);
5677 }
5678 
5679 static PetscErrorCode DMPlexComputeBdJacobian_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, PetscReal X_tShift, Mat Jac, Mat JacP, void *user)
5680 {
5681   PetscDS  prob;
5682   PetscInt dim, numBd, bd;
5683   DMLabel  depthLabel;
5684   DMField  coordField = NULL;
5685   IS       facetIS;
5686 
5687   PetscFunctionBegin;
5688   PetscCall(DMGetDS(dm, &prob));
5689   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5690   PetscCall(DMGetDimension(dm, &dim));
5691   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
5692   PetscCall(PetscDSGetNumBoundary(prob, &numBd));
5693   PetscCall(DMGetCoordinateField(dm, &coordField));
5694   for (bd = 0; bd < numBd; ++bd) {
5695     PetscWeakForm           wf;
5696     DMBoundaryConditionType type;
5697     DMLabel                 label;
5698     const PetscInt         *values;
5699     PetscInt                fieldI, numValues;
5700     PetscObject             obj;
5701     PetscClassId            id;
5702 
5703     PetscCall(PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &fieldI, NULL, NULL, NULL, NULL, NULL));
5704     if (type & DM_BC_ESSENTIAL) continue;
5705     PetscCall(PetscDSGetDiscretization(prob, fieldI, &obj));
5706     PetscCall(PetscObjectGetClassId(obj, &id));
5707     if (id != PETSCFE_CLASSID) continue;
5708     PetscCall(DMPlexComputeBdJacobian_Single_Internal(dm, t, wf, label, numValues, values, fieldI, locX, locX_t, X_tShift, Jac, JacP, coordField, facetIS));
5709   }
5710   PetscCall(ISDestroy(&facetIS));
5711   PetscFunctionReturn(PETSC_SUCCESS);
5712 }
5713 
5714 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)
5715 {
5716   DM_Plex        *mesh  = (DM_Plex *)dm->data;
5717   const char     *name  = "Jacobian";
5718   DM              dmAux = NULL, plex, tdm;
5719   DMEnclosureType encAux;
5720   Vec             A, tv;
5721   DMField         coordField;
5722   PetscDS         prob, probAux = NULL;
5723   PetscSection    section, globalSection, sectionAux;
5724   PetscScalar    *elemMat, *elemMatP, *elemMatD, *u, *u_t, *a = NULL;
5725   const PetscInt *cells;
5726   PetscInt        Nf, fieldI, fieldJ;
5727   PetscInt        totDim, totDimAux = 0, cStart, cEnd, numCells, c;
5728   PetscBool       hasJac = PETSC_FALSE, hasPrec = PETSC_FALSE, hasDyn, hasFV = PETSC_FALSE, transform;
5729 
5730   PetscFunctionBegin;
5731   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
5732   if (!cellIS) goto end;
5733   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
5734   PetscCall(ISGetLocalSize(cellIS, &numCells));
5735   if (cStart >= cEnd) goto end;
5736   PetscCall(DMHasBasisTransform(dm, &transform));
5737   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
5738   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
5739   PetscCall(DMGetLocalSection(dm, &section));
5740   PetscCall(DMGetGlobalSection(dm, &globalSection));
5741   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL));
5742   PetscCall(PetscDSGetNumFields(prob, &Nf));
5743   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
5744   PetscCall(PetscDSHasJacobian(prob, &hasJac));
5745   PetscCall(PetscDSHasJacobianPreconditioner(prob, &hasPrec));
5746   /* user passed in the same matrix, avoid double contributions and
5747      only assemble the Jacobian */
5748   if (hasJac && Jac == JacP) hasPrec = PETSC_FALSE;
5749   PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
5750   hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
5751   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &A));
5752   if (A) {
5753     PetscCall(VecGetDM(A, &dmAux));
5754     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
5755     PetscCall(DMConvert(dmAux, DMPLEX, &plex));
5756     PetscCall(DMGetLocalSection(plex, &sectionAux));
5757     PetscCall(DMGetDS(dmAux, &probAux));
5758     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
5759   }
5760   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));
5761   if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
5762   PetscCall(DMGetCoordinateField(dm, &coordField));
5763   for (c = cStart; c < cEnd; ++c) {
5764     const PetscInt cell = cells ? cells[c] : c;
5765     const PetscInt cind = c - cStart;
5766     PetscScalar   *x = NULL, *x_t = NULL;
5767     PetscInt       i;
5768 
5769     PetscCall(DMPlexVecGetClosure(dm, section, X, cell, NULL, &x));
5770     for (i = 0; i < totDim; ++i) u[cind * totDim + i] = x[i];
5771     PetscCall(DMPlexVecRestoreClosure(dm, section, X, cell, NULL, &x));
5772     if (X_t) {
5773       PetscCall(DMPlexVecGetClosure(dm, section, X_t, cell, NULL, &x_t));
5774       for (i = 0; i < totDim; ++i) u_t[cind * totDim + i] = x_t[i];
5775       PetscCall(DMPlexVecRestoreClosure(dm, section, X_t, cell, NULL, &x_t));
5776     }
5777     if (dmAux) {
5778       PetscInt subcell;
5779       PetscCall(DMGetEnclosurePoint(dmAux, dm, encAux, cell, &subcell));
5780       PetscCall(DMPlexVecGetClosure(plex, sectionAux, A, subcell, NULL, &x));
5781       for (i = 0; i < totDimAux; ++i) a[cind * totDimAux + i] = x[i];
5782       PetscCall(DMPlexVecRestoreClosure(plex, sectionAux, A, subcell, NULL, &x));
5783     }
5784   }
5785   if (hasJac) PetscCall(PetscArrayzero(elemMat, numCells * totDim * totDim));
5786   if (hasPrec) PetscCall(PetscArrayzero(elemMatP, numCells * totDim * totDim));
5787   if (hasDyn) PetscCall(PetscArrayzero(elemMatD, numCells * totDim * totDim));
5788   for (fieldI = 0; fieldI < Nf; ++fieldI) {
5789     PetscClassId    id;
5790     PetscFE         fe;
5791     PetscQuadrature qGeom = NULL;
5792     PetscInt        Nb;
5793     /* Conforming batches */
5794     PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
5795     /* Remainder */
5796     PetscInt     Nr, offset, Nq;
5797     PetscInt     maxDegree;
5798     PetscFEGeom *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL;
5799 
5800     PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
5801     PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
5802     if (id == PETSCFV_CLASSID) {
5803       hasFV = PETSC_TRUE;
5804       continue;
5805     }
5806     PetscCall(PetscFEGetDimension(fe, &Nb));
5807     PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5808     PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
5809     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
5810     if (!qGeom) {
5811       PetscCall(PetscFEGetQuadrature(fe, &qGeom));
5812       PetscCall(PetscObjectReference((PetscObject)qGeom));
5813     }
5814     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
5815     PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
5816     blockSize = Nb;
5817     batchSize = numBlocks * blockSize;
5818     PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5819     numChunks = numCells / (numBatches * batchSize);
5820     Ne        = numChunks * numBatches * batchSize;
5821     Nr        = numCells % (numBatches * batchSize);
5822     offset    = numCells - Nr;
5823     PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
5824     PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &remGeom));
5825     for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5826       key.field = fieldI * Nf + fieldJ;
5827       if (hasJac) {
5828         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat));
5829         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Nr, remGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), probAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, X_tShift, &elemMat[offset * totDim * totDim]));
5830       }
5831       if (hasPrec) {
5832         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatP));
5833         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, key, Nr, remGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), probAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, X_tShift, &elemMatP[offset * totDim * totDim]));
5834       }
5835       if (hasDyn) {
5836         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatD));
5837         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Nr, remGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), probAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, X_tShift, &elemMatD[offset * totDim * totDim]));
5838       }
5839     }
5840     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &remGeom));
5841     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, 0, offset, &chunkGeom));
5842     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
5843     PetscCall(PetscQuadratureDestroy(&qGeom));
5844   }
5845   /*   Add contribution from X_t */
5846   if (hasDyn) {
5847     for (c = 0; c < numCells * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
5848   }
5849   if (hasFV) {
5850     PetscClassId id;
5851     PetscFV      fv;
5852     PetscInt     offsetI, NcI, NbI = 1, fc, f;
5853 
5854     for (fieldI = 0; fieldI < Nf; ++fieldI) {
5855       PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fv));
5856       PetscCall(PetscDSGetFieldOffset(prob, fieldI, &offsetI));
5857       PetscCall(PetscObjectGetClassId((PetscObject)fv, &id));
5858       if (id != PETSCFV_CLASSID) continue;
5859       /* Put in the identity */
5860       PetscCall(PetscFVGetNumComponents(fv, &NcI));
5861       for (c = cStart; c < cEnd; ++c) {
5862         const PetscInt cind    = c - cStart;
5863         const PetscInt eOffset = cind * totDim * totDim;
5864         for (fc = 0; fc < NcI; ++fc) {
5865           for (f = 0; f < NbI; ++f) {
5866             const PetscInt i = offsetI + f * NcI + fc;
5867             if (hasPrec) {
5868               if (hasJac) elemMat[eOffset + i * totDim + i] = 1.0;
5869               elemMatP[eOffset + i * totDim + i] = 1.0;
5870             } else {
5871               elemMat[eOffset + i * totDim + i] = 1.0;
5872             }
5873           }
5874         }
5875       }
5876     }
5877     /* No allocated space for FV stuff, so ignore the zero entries */
5878     PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE));
5879   }
5880   /* Insert values into matrix */
5881   for (c = cStart; c < cEnd; ++c) {
5882     const PetscInt cell = cells ? cells[c] : c;
5883     const PetscInt cind = c - cStart;
5884 
5885     /* Transform to global basis before insertion in Jacobian */
5886     if (transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, cell, PETSC_TRUE, totDim, &elemMat[cind * totDim * totDim]));
5887     if (hasPrec) {
5888       if (hasJac) {
5889         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
5890         PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, Jac, cell, &elemMat[cind * totDim * totDim], ADD_VALUES));
5891       }
5892       if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatP[cind * totDim * totDim]));
5893       PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, JacP, cell, &elemMatP[cind * totDim * totDim], ADD_VALUES));
5894     } else {
5895       if (hasJac) {
5896         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
5897         PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, JacP, cell, &elemMat[cind * totDim * totDim], ADD_VALUES));
5898       }
5899     }
5900   }
5901   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
5902   if (hasFV) PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE));
5903   PetscCall(PetscFree5(u, u_t, elemMat, elemMatP, elemMatD));
5904   if (dmAux) {
5905     PetscCall(PetscFree(a));
5906     PetscCall(DMDestroy(&plex));
5907   }
5908   /* Compute boundary integrals */
5909   PetscCall(DMPlexComputeBdJacobian_Internal(dm, X, X_t, t, X_tShift, Jac, JacP, user));
5910   /* Assemble matrix */
5911 end : {
5912   PetscBool assOp = hasJac && hasPrec ? PETSC_TRUE : PETSC_FALSE, gassOp;
5913 
5914   PetscCall(MPIU_Allreduce(&assOp, &gassOp, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
5915   if (hasJac && hasPrec) {
5916     PetscCall(MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY));
5917     PetscCall(MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY));
5918   }
5919 }
5920   PetscCall(MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY));
5921   PetscCall(MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY));
5922   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
5923   PetscFunctionReturn(PETSC_SUCCESS);
5924 }
5925 
5926 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)
5927 {
5928   DM_Plex        *mesh          = (DM_Plex *)dm->data;
5929   const char     *name          = "Hybrid Jacobian";
5930   DM              dmAux[3]      = {NULL, NULL, NULL};
5931   DMLabel         ghostLabel    = NULL;
5932   DM              plex          = NULL;
5933   DM              plexA         = NULL;
5934   PetscDS         ds            = NULL;
5935   PetscDS         dsIn          = NULL;
5936   PetscDS         dsAux[3]      = {NULL, NULL, NULL};
5937   Vec             locA[3]       = {NULL, NULL, NULL};
5938   DM              dmScale[3]    = {NULL, NULL, NULL};
5939   PetscDS         dsScale[3]    = {NULL, NULL, NULL};
5940   Vec             locS[3]       = {NULL, NULL, NULL};
5941   PetscSection    section       = NULL;
5942   PetscSection    sectionAux[3] = {NULL, NULL, NULL};
5943   DMField         coordField    = NULL;
5944   PetscScalar    *a[3]          = {NULL, NULL, NULL};
5945   PetscScalar    *s[3]          = {NULL, NULL, NULL};
5946   PetscScalar    *u             = NULL, *u_t;
5947   PetscScalar    *elemMatNeg, *elemMatPos, *elemMatCoh;
5948   PetscScalar    *elemMatNegP, *elemMatPosP, *elemMatCohP;
5949   PetscSection    globalSection;
5950   IS              chunkIS;
5951   const PetscInt *cells;
5952   PetscInt       *faces;
5953   PetscInt        cStart, cEnd, numCells;
5954   PetscInt        Nf, fieldI, fieldJ, totDim, totDimIn, totDimAux[3], totDimScale[3], numChunks, cellChunkSize, chunk;
5955   PetscInt        maxDegree  = PETSC_MAX_INT;
5956   PetscQuadrature affineQuad = NULL, *quads = NULL;
5957   PetscFEGeom    *affineGeom = NULL, **geoms = NULL;
5958   PetscBool       hasBdJac, hasBdPrec;
5959 
5960   PetscFunctionBegin;
5961   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
5962   if (!cellIS) goto end;
5963   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
5964   PetscCall(ISGetLocalSize(cellIS, &numCells));
5965   if (cStart >= cEnd) goto end;
5966   if ((key[0].label == key[1].label) && (key[0].value == key[1].value) && (key[0].part == key[1].part)) {
5967     const char *name;
5968     PetscCall(PetscObjectGetName((PetscObject)key[0].label, &name));
5969     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);
5970   }
5971   PetscCall(DMConvert(dm, DMPLEX, &plex));
5972   PetscCall(DMGetSection(dm, &section));
5973   PetscCall(DMGetGlobalSection(dm, &globalSection));
5974   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
5975   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn));
5976   PetscCall(PetscDSGetNumFields(ds, &Nf));
5977   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
5978   PetscCall(PetscDSGetTotalDimension(dsIn, &totDimIn));
5979   PetscCall(PetscDSHasBdJacobian(ds, &hasBdJac));
5980   PetscCall(PetscDSHasBdJacobianPreconditioner(ds, &hasBdPrec));
5981   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, key[2].value, key[2].part, &locA[2]));
5982   if (locA[2]) {
5983     const PetscInt cellStart = cells ? cells[cStart] : cStart;
5984 
5985     PetscCall(VecGetDM(locA[2], &dmAux[2]));
5986     PetscCall(DMConvert(dmAux[2], DMPLEX, &plexA));
5987     PetscCall(DMGetSection(dmAux[2], &sectionAux[2]));
5988     PetscCall(DMGetCellDS(dmAux[2], cellStart, &dsAux[2], NULL));
5989     PetscCall(PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]));
5990     {
5991       const PetscInt *cone;
5992       PetscInt        c;
5993 
5994       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
5995       for (c = 0; c < 2; ++c) {
5996         const PetscInt *support;
5997         PetscInt        ssize, s;
5998 
5999         PetscCall(DMPlexGetSupport(dm, cone[c], &support));
6000         PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
6001         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);
6002         if (support[0] == cellStart) s = 1;
6003         else if (support[1] == cellStart) s = 0;
6004         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
6005         PetscCall(DMGetAuxiliaryVec(dm, key[c].label, key[c].value, key[c].part, &locA[c]));
6006         if (locA[c]) PetscCall(VecGetDM(locA[c], &dmAux[c]));
6007         else dmAux[c] = dmAux[2];
6008         PetscCall(DMGetCellDS(dmAux[c], support[s], &dsAux[c], NULL));
6009         PetscCall(PetscDSGetTotalDimension(dsAux[c], &totDimAux[c]));
6010       }
6011     }
6012   }
6013   /* Handle mass matrix scaling
6014        The field in key[2] is the field to be scaled, and the scaling field is the first in the dsScale */
6015   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, -key[2].value, key[2].part, &locS[2]));
6016   if (locS[2]) {
6017     const PetscInt cellStart = cells ? cells[cStart] : cStart;
6018     PetscInt       Nb, Nbs;
6019 
6020     PetscCall(VecGetDM(locS[2], &dmScale[2]));
6021     PetscCall(DMGetCellDS(dmScale[2], cells ? cells[cStart] : cStart, &dsScale[2], NULL));
6022     PetscCall(PetscDSGetTotalDimension(dsScale[2], &totDimScale[2]));
6023     // BRAD: This is not set correctly
6024     key[2].field = 2;
6025     PetscCall(PetscDSGetFieldSize(ds, key[2].field, &Nb));
6026     PetscCall(PetscDSGetFieldSize(dsScale[2], 0, &Nbs));
6027     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);
6028     {
6029       const PetscInt *cone;
6030       PetscInt        c;
6031 
6032       locS[1] = locS[0] = locS[2];
6033       dmScale[1] = dmScale[0] = dmScale[2];
6034       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
6035       for (c = 0; c < 2; ++c) {
6036         const PetscInt *support;
6037         PetscInt        ssize, s;
6038 
6039         PetscCall(DMPlexGetSupport(dm, cone[c], &support));
6040         PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
6041         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);
6042         if (support[0] == cellStart) s = 1;
6043         else if (support[1] == cellStart) s = 0;
6044         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
6045         PetscCall(DMGetCellDS(dmScale[c], support[s], &dsScale[c], NULL));
6046         PetscCall(PetscDSGetTotalDimension(dsScale[c], &totDimScale[c]));
6047       }
6048     }
6049   }
6050   /* 2: Setup geometric data */
6051   PetscCall(DMGetCoordinateField(dm, &coordField));
6052   PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
6053   if (maxDegree > 1) {
6054     PetscInt f;
6055     PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
6056     for (f = 0; f < Nf; ++f) {
6057       PetscFE fe;
6058 
6059       PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
6060       if (fe) {
6061         PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
6062         PetscCall(PetscObjectReference((PetscObject)quads[f]));
6063       }
6064     }
6065   }
6066   /* Loop over chunks */
6067   cellChunkSize = numCells;
6068   numChunks     = !numCells ? 0 : PetscCeilReal(((PetscReal)numCells) / cellChunkSize);
6069   PetscCall(PetscCalloc1(2 * cellChunkSize, &faces));
6070   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 1 * cellChunkSize, faces, PETSC_USE_POINTER, &chunkIS));
6071   /* Extract field coefficients */
6072   /* NOTE This needs the end cap faces to have identical orientations */
6073   PetscCall(DMPlexGetHybridCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
6074   PetscCall(DMPlexGetHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
6075   PetscCall(DMPlexGetHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_TRUE, s));
6076   PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNeg));
6077   PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPos));
6078   PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCoh));
6079   PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNegP));
6080   PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPosP));
6081   PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCohP));
6082   for (chunk = 0; chunk < numChunks; ++chunk) {
6083     PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
6084 
6085     if (hasBdJac) {
6086       PetscCall(PetscArrayzero(elemMatNeg, cellChunkSize * totDim * totDim));
6087       PetscCall(PetscArrayzero(elemMatPos, cellChunkSize * totDim * totDim));
6088       PetscCall(PetscArrayzero(elemMatCoh, cellChunkSize * totDim * totDim));
6089     }
6090     if (hasBdPrec) {
6091       PetscCall(PetscArrayzero(elemMatNegP, cellChunkSize * totDim * totDim));
6092       PetscCall(PetscArrayzero(elemMatPosP, cellChunkSize * totDim * totDim));
6093       PetscCall(PetscArrayzero(elemMatCohP, cellChunkSize * totDim * totDim));
6094     }
6095     /* Get faces */
6096     for (c = cS; c < cE; ++c) {
6097       const PetscInt  cell = cells ? cells[c] : c;
6098       const PetscInt *cone;
6099       PetscCall(DMPlexGetCone(plex, cell, &cone));
6100       faces[(c - cS) * 2 + 0] = cone[0];
6101       faces[(c - cS) * 2 + 1] = cone[1];
6102     }
6103     PetscCall(ISGeneralSetIndices(chunkIS, 2 * cellChunkSize, faces, PETSC_USE_POINTER));
6104     if (maxDegree <= 1) {
6105       if (!affineQuad) PetscCall(DMFieldCreateDefaultQuadrature(coordField, chunkIS, &affineQuad));
6106       if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, affineQuad, PETSC_TRUE, &affineGeom));
6107     } else {
6108       PetscInt f;
6109       for (f = 0; f < Nf; ++f) {
6110         if (quads[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, quads[f], PETSC_TRUE, &geoms[f]));
6111       }
6112     }
6113 
6114     for (fieldI = 0; fieldI < Nf; ++fieldI) {
6115       PetscFE         feI;
6116       PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[fieldI];
6117       PetscFEGeom    *chunkGeom = NULL, *remGeom = NULL;
6118       PetscQuadrature quad = affineQuad ? affineQuad : quads[fieldI];
6119       PetscInt        numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb;
6120       PetscBool       isCohesiveField;
6121 
6122       PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&feI));
6123       if (!feI) continue;
6124       PetscCall(PetscFEGetTileSizes(feI, NULL, &numBlocks, NULL, &numBatches));
6125       PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
6126       PetscCall(PetscFEGetDimension(feI, &Nb));
6127       blockSize = Nb;
6128       batchSize = numBlocks * blockSize;
6129       PetscCall(PetscFESetTileSizes(feI, blockSize, numBlocks, batchSize, numBatches));
6130       numChunks = numCells / (numBatches * batchSize);
6131       Ne        = numChunks * numBatches * batchSize;
6132       Nr        = numCells % (numBatches * batchSize);
6133       offset    = numCells - Nr;
6134       PetscCall(PetscFEGeomGetChunk(geom, 0, offset * 2, &chunkGeom));
6135       PetscCall(PetscFEGeomGetChunk(geom, offset * 2, numCells * 2, &remGeom));
6136       PetscCall(PetscDSGetCohesive(ds, fieldI, &isCohesiveField));
6137       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
6138         PetscFE feJ;
6139 
6140         PetscCall(PetscDSGetDiscretization(ds, fieldJ, (PetscObject *)&feJ));
6141         if (!feJ) continue;
6142         key[0].field = fieldI * Nf + fieldJ;
6143         key[1].field = fieldI * Nf + fieldJ;
6144         key[2].field = fieldI * Nf + fieldJ;
6145         if (hasBdJac) {
6146           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, X_tShift, elemMatNeg));
6147           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[0], 0, Nr, remGeom, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[0], PetscSafePointerPlusOffset(a[0], offset * totDimAux[0]), t, X_tShift, &elemMatNeg[offset * totDim * totDim]));
6148           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, X_tShift, elemMatPos));
6149           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[1], 1, Nr, remGeom, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[1], PetscSafePointerPlusOffset(a[1], offset * totDimAux[1]), t, X_tShift, &elemMatPos[offset * totDim * totDim]));
6150         }
6151         if (hasBdPrec) {
6152           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, X_tShift, elemMatNegP));
6153           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[0], 0, Nr, remGeom, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[0], &a[0][offset * totDimAux[0]], t, X_tShift, &elemMatNegP[offset * totDim * totDim]));
6154           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, X_tShift, elemMatPosP));
6155           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[1], 1, Nr, remGeom, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[1], &a[1][offset * totDimAux[1]], t, X_tShift, &elemMatPosP[offset * totDim * totDim]));
6156         }
6157         if (hasBdJac) {
6158           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, X_tShift, elemMatCoh));
6159           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[2], 2, Nr, remGeom, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[2], PetscSafePointerPlusOffset(a[2], offset * totDimAux[2]), t, X_tShift, &elemMatCoh[offset * totDim * totDim]));
6160         }
6161         if (hasBdPrec) {
6162           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, X_tShift, elemMatCohP));
6163           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[2], 2, Nr, remGeom, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[2], &a[2][offset * totDimAux[2]], t, X_tShift, &elemMatCohP[offset * totDim * totDim]));
6164         }
6165       }
6166       PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &remGeom));
6167       PetscCall(PetscFEGeomRestoreChunk(geom, 0, offset, &chunkGeom));
6168     }
6169     /* Insert values into matrix */
6170     for (c = cS; c < cE; ++c) {
6171       const PetscInt cell = cells ? cells[c] : c;
6172       const PetscInt cind = c - cS, coff = cind * totDim * totDim;
6173       PetscInt       i, j;
6174 
6175       /* Scale element values */
6176       if (locS[0]) {
6177         PetscInt  Nb, soff = cind * totDimScale[0], off = 0;
6178         PetscBool cohesive;
6179 
6180         for (fieldI = 0; fieldI < Nf; ++fieldI) {
6181           PetscCall(PetscDSGetFieldSize(ds, fieldI, &Nb));
6182           PetscCall(PetscDSGetCohesive(ds, fieldI, &cohesive));
6183 
6184           if (fieldI == key[2].field) {
6185             PetscCheck(cohesive, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Scaling should not happen for face fields");
6186             for (i = 0; i < Nb; ++i) {
6187               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];
6188               if (hasBdPrec)
6189                 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];
6190             }
6191             off += Nb;
6192           } else {
6193             const PetscInt N = cohesive ? Nb : Nb * 2;
6194 
6195             for (i = 0; i < N; ++i) {
6196               for (j = 0; j < totDim; ++j) elemMatCoh[coff + (off + i) * totDim + j] += elemMatNeg[coff + (off + i) * totDim + j] + elemMatPos[coff + (off + i) * totDim + j];
6197               if (hasBdPrec)
6198                 for (j = 0; j < totDim; ++j) elemMatCohP[coff + (off + i) * totDim + j] += elemMatNegP[coff + (off + i) * totDim + j] + elemMatPosP[coff + (off + i) * totDim + j];
6199             }
6200             off += N;
6201           }
6202         }
6203       } else {
6204         for (i = 0; i < totDim * totDim; ++i) elemMatCoh[coff + i] += elemMatNeg[coff + i] + elemMatPos[coff + i];
6205         if (hasBdPrec)
6206           for (i = 0; i < totDim * totDim; ++i) elemMatCohP[coff + i] += elemMatNegP[coff + i] + elemMatPosP[coff + i];
6207       }
6208       if (hasBdPrec) {
6209         if (hasBdJac) {
6210           if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCoh[cind * totDim * totDim]));
6211           PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, Jac, cell, &elemMatCoh[cind * totDim * totDim], ADD_VALUES));
6212         }
6213         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCohP[cind * totDim * totDim]));
6214         PetscCall(DMPlexMatSetClosure(plex, section, globalSection, JacP, cell, &elemMatCohP[cind * totDim * totDim], ADD_VALUES));
6215       } else if (hasBdJac) {
6216         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCoh[cind * totDim * totDim]));
6217         PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, JacP, cell, &elemMatCoh[cind * totDim * totDim], ADD_VALUES));
6218       }
6219     }
6220   }
6221   PetscCall(DMPlexRestoreCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
6222   PetscCall(DMPlexRestoreHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
6223   PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNeg));
6224   PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPos));
6225   PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCoh));
6226   PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNegP));
6227   PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPosP));
6228   PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCohP));
6229   PetscCall(PetscFree(faces));
6230   PetscCall(ISDestroy(&chunkIS));
6231   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
6232   if (maxDegree <= 1) {
6233     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
6234     PetscCall(PetscQuadratureDestroy(&affineQuad));
6235   } else {
6236     PetscInt f;
6237     for (f = 0; f < Nf; ++f) {
6238       if (geoms) PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
6239       if (quads) PetscCall(PetscQuadratureDestroy(&quads[f]));
6240     }
6241     PetscCall(PetscFree2(quads, geoms));
6242   }
6243   if (dmAux[2]) PetscCall(DMDestroy(&plexA));
6244   PetscCall(DMDestroy(&plex));
6245 end:
6246   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
6247   PetscFunctionReturn(PETSC_SUCCESS);
6248 }
6249 
6250 /*
6251   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.
6252 
6253   Input Parameters:
6254 + dm     - The mesh
6255 . key    - The PetscWeakFormKey indicating where integration should happen
6256 . cellIS - The cells to integrate over
6257 . t      - The time
6258 . X_tShift - The multiplier for the Jacobian with respect to X_t
6259 . X      - Local solution vector
6260 . X_t    - Time-derivative of the local solution vector
6261 . Y      - Local input vector
6262 - user   - the user context
6263 
6264   Output Parameter:
6265 . Z - Local output vector
6266 
6267   Note:
6268   We form the residual one batch of elements at a time. This allows us to offload work onto an accelerator,
6269   like a GPU, or vectorize on a multicore machine.
6270 */
6271 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)
6272 {
6273   DM_Plex        *mesh  = (DM_Plex *)dm->data;
6274   const char     *name  = "Jacobian";
6275   DM              dmAux = NULL, plex, plexAux = NULL;
6276   DMEnclosureType encAux;
6277   Vec             A;
6278   DMField         coordField;
6279   PetscDS         prob, probAux = NULL;
6280   PetscQuadrature quad;
6281   PetscSection    section, globalSection, sectionAux;
6282   PetscScalar    *elemMat, *elemMatD, *u, *u_t, *a = NULL, *y, *z;
6283   const PetscInt *cells;
6284   PetscInt        Nf, fieldI, fieldJ;
6285   PetscInt        totDim, totDimAux = 0, cStart, cEnd, numCells, c;
6286   PetscBool       hasDyn;
6287 
6288   PetscFunctionBegin;
6289   if (!cellIS) PetscFunctionReturn(PETSC_SUCCESS);
6290   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
6291   PetscCall(DMConvert(dm, DMPLEX, &plex));
6292   PetscCall(ISGetLocalSize(cellIS, &numCells));
6293   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
6294   PetscCall(DMGetLocalSection(dm, &section));
6295   PetscCall(DMGetGlobalSection(dm, &globalSection));
6296   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL));
6297   PetscCall(PetscDSGetNumFields(prob, &Nf));
6298   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
6299   PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
6300   hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
6301   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &A));
6302   if (A) {
6303     PetscCall(VecGetDM(A, &dmAux));
6304     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
6305     PetscCall(DMConvert(dmAux, DMPLEX, &plexAux));
6306     PetscCall(DMGetLocalSection(plexAux, &sectionAux));
6307     PetscCall(DMGetDS(dmAux, &probAux));
6308     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
6309   }
6310   PetscCall(VecSet(Z, 0.0));
6311   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));
6312   if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
6313   PetscCall(DMGetCoordinateField(dm, &coordField));
6314   for (c = cStart; c < cEnd; ++c) {
6315     const PetscInt cell = cells ? cells[c] : c;
6316     const PetscInt cind = c - cStart;
6317     PetscScalar   *x = NULL, *x_t = NULL;
6318     PetscInt       i;
6319 
6320     PetscCall(DMPlexVecGetClosure(plex, section, X, cell, NULL, &x));
6321     for (i = 0; i < totDim; ++i) u[cind * totDim + i] = x[i];
6322     PetscCall(DMPlexVecRestoreClosure(plex, section, X, cell, NULL, &x));
6323     if (X_t) {
6324       PetscCall(DMPlexVecGetClosure(plex, section, X_t, cell, NULL, &x_t));
6325       for (i = 0; i < totDim; ++i) u_t[cind * totDim + i] = x_t[i];
6326       PetscCall(DMPlexVecRestoreClosure(plex, section, X_t, cell, NULL, &x_t));
6327     }
6328     if (dmAux) {
6329       PetscInt subcell;
6330       PetscCall(DMGetEnclosurePoint(dmAux, dm, encAux, cell, &subcell));
6331       PetscCall(DMPlexVecGetClosure(plexAux, sectionAux, A, subcell, NULL, &x));
6332       for (i = 0; i < totDimAux; ++i) a[cind * totDimAux + i] = x[i];
6333       PetscCall(DMPlexVecRestoreClosure(plexAux, sectionAux, A, subcell, NULL, &x));
6334     }
6335     PetscCall(DMPlexVecGetClosure(plex, section, Y, cell, NULL, &x));
6336     for (i = 0; i < totDim; ++i) y[cind * totDim + i] = x[i];
6337     PetscCall(DMPlexVecRestoreClosure(plex, section, Y, cell, NULL, &x));
6338   }
6339   PetscCall(PetscArrayzero(elemMat, numCells * totDim * totDim));
6340   if (hasDyn) PetscCall(PetscArrayzero(elemMatD, numCells * totDim * totDim));
6341   for (fieldI = 0; fieldI < Nf; ++fieldI) {
6342     PetscFE  fe;
6343     PetscInt Nb;
6344     /* Conforming batches */
6345     PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
6346     /* Remainder */
6347     PetscInt        Nr, offset, Nq;
6348     PetscQuadrature qGeom = NULL;
6349     PetscInt        maxDegree;
6350     PetscFEGeom    *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL;
6351 
6352     PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
6353     PetscCall(PetscFEGetQuadrature(fe, &quad));
6354     PetscCall(PetscFEGetDimension(fe, &Nb));
6355     PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
6356     PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
6357     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
6358     if (!qGeom) {
6359       PetscCall(PetscFEGetQuadrature(fe, &qGeom));
6360       PetscCall(PetscObjectReference((PetscObject)qGeom));
6361     }
6362     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
6363     PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
6364     blockSize = Nb;
6365     batchSize = numBlocks * blockSize;
6366     PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
6367     numChunks = numCells / (numBatches * batchSize);
6368     Ne        = numChunks * numBatches * batchSize;
6369     Nr        = numCells % (numBatches * batchSize);
6370     offset    = numCells - Nr;
6371     PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
6372     PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &remGeom));
6373     for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
6374       key.field = fieldI * Nf + fieldJ;
6375       PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat));
6376       PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Nr, remGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), probAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, X_tShift, &elemMat[offset * totDim * totDim]));
6377       if (hasDyn) {
6378         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatD));
6379         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Nr, remGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), probAux, &a[offset * totDimAux], t, X_tShift, &elemMatD[offset * totDim * totDim]));
6380       }
6381     }
6382     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &remGeom));
6383     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, 0, offset, &chunkGeom));
6384     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
6385     PetscCall(PetscQuadratureDestroy(&qGeom));
6386   }
6387   if (hasDyn) {
6388     for (c = 0; c < numCells * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
6389   }
6390   for (c = cStart; c < cEnd; ++c) {
6391     const PetscInt     cell = cells ? cells[c] : c;
6392     const PetscInt     cind = c - cStart;
6393     const PetscBLASInt M = totDim, one = 1;
6394     const PetscScalar  a = 1.0, b = 0.0;
6395 
6396     PetscCallBLAS("BLASgemv", BLASgemv_("N", &M, &M, &a, &elemMat[cind * totDim * totDim], &M, &y[cind * totDim], &one, &b, z, &one));
6397     if (mesh->printFEM > 1) {
6398       PetscCall(DMPrintCellMatrix(c, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
6399       PetscCall(DMPrintCellVector(c, "Y", totDim, &y[cind * totDim]));
6400       PetscCall(DMPrintCellVector(c, "Z", totDim, z));
6401     }
6402     PetscCall(DMPlexVecSetClosure(dm, section, Z, cell, z, ADD_VALUES));
6403   }
6404   PetscCall(PetscFree6(u, u_t, elemMat, elemMatD, y, z));
6405   if (mesh->printFEM) {
6406     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)Z), "Z:\n"));
6407     PetscCall(VecView(Z, NULL));
6408   }
6409   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
6410   PetscCall(PetscFree(a));
6411   PetscCall(DMDestroy(&plexAux));
6412   PetscCall(DMDestroy(&plex));
6413   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
6414   PetscFunctionReturn(PETSC_SUCCESS);
6415 }
6416