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