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