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