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