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