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