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