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