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