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