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