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