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