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