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