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