xref: /petsc/src/dm/impls/plex/plexfem.c (revision ef5c7bd2f4ec6f9849104a8422b5777b3b2f72fb)
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 Nf, 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()
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()
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
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(), 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
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(), 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   DMPlexInsertBoundaryValuesRiemann - Insert boundary values into a local vector
810 
811   Input Parameters:
812 + dm     - The DM, with a PetscDS that matches the problem being constrained
813 . time   - The time
814 . faceGeometry - A vector with the FVM face geometry information
815 . cellGeometry - A vector with the FVM cell geometry information
816 . Grad         - A vector with the FVM cell gradient information
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
824 - ctx    - An optional user context for bcFunc
825 
826   Output Parameter:
827 . locX   - A local vector to receives the boundary values
828 
829   Note: This implementation currently ignores the numcomps/comps argument from DMAddBoundary()
830 
831   Level: developer
832 
833 .seealso: DMPlexInsertBoundaryValuesEssential(), DMPlexInsertBoundaryValuesEssentialField(), DMAddBoundary()
834 @*/
835 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[],
836                                                  PetscErrorCode (*func)(PetscReal,const PetscReal*,const PetscReal*,const PetscScalar*,PetscScalar*,void*), void *ctx, Vec locX)
837 {
838   PetscDS            prob;
839   PetscSF            sf;
840   DM                 dmFace, dmCell, dmGrad;
841   const PetscScalar *facegeom, *cellgeom = NULL, *grad;
842   const PetscInt    *leaves;
843   PetscScalar       *x, *fx;
844   PetscInt           dim, nleaves, loc, fStart, fEnd, pdim, i;
845   PetscErrorCode     ierr, ierru = 0;
846 
847   PetscFunctionBegin;
848   ierr = DMGetPointSF(dm, &sf);CHKERRQ(ierr);
849   ierr = PetscSFGetGraph(sf, NULL, &nleaves, &leaves, NULL);CHKERRQ(ierr);
850   nleaves = PetscMax(0, nleaves);
851   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
852   ierr = DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd);CHKERRQ(ierr);
853   ierr = DMGetDS(dm, &prob);CHKERRQ(ierr);
854   ierr = VecGetDM(faceGeometry, &dmFace);CHKERRQ(ierr);
855   ierr = VecGetArrayRead(faceGeometry, &facegeom);CHKERRQ(ierr);
856   if (cellGeometry) {
857     ierr = VecGetDM(cellGeometry, &dmCell);CHKERRQ(ierr);
858     ierr = VecGetArrayRead(cellGeometry, &cellgeom);CHKERRQ(ierr);
859   }
860   if (Grad) {
861     PetscFV fv;
862 
863     ierr = PetscDSGetDiscretization(prob, field, (PetscObject *) &fv);CHKERRQ(ierr);
864     ierr = VecGetDM(Grad, &dmGrad);CHKERRQ(ierr);
865     ierr = VecGetArrayRead(Grad, &grad);CHKERRQ(ierr);
866     ierr = PetscFVGetNumComponents(fv, &pdim);CHKERRQ(ierr);
867     ierr = DMGetWorkArray(dm, pdim, MPIU_SCALAR, &fx);CHKERRQ(ierr);
868   }
869   ierr = VecGetArray(locX, &x);CHKERRQ(ierr);
870   for (i = 0; i < numids; ++i) {
871     IS              faceIS;
872     const PetscInt *faces;
873     PetscInt        numFaces, f;
874 
875     ierr = DMLabelGetStratumIS(label, ids[i], &faceIS);CHKERRQ(ierr);
876     if (!faceIS) continue; /* No points with that id on this process */
877     ierr = ISGetLocalSize(faceIS, &numFaces);CHKERRQ(ierr);
878     ierr = ISGetIndices(faceIS, &faces);CHKERRQ(ierr);
879     for (f = 0; f < numFaces; ++f) {
880       const PetscInt         face = faces[f], *cells;
881       PetscFVFaceGeom        *fg;
882 
883       if ((face < fStart) || (face >= fEnd)) continue; /* Refinement adds non-faces to labels */
884       ierr = PetscFindInt(face, nleaves, (PetscInt *) leaves, &loc);CHKERRQ(ierr);
885       if (loc >= 0) continue;
886       ierr = DMPlexPointLocalRead(dmFace, face, facegeom, &fg);CHKERRQ(ierr);
887       ierr = DMPlexGetSupport(dm, face, &cells);CHKERRQ(ierr);
888       if (Grad) {
889         PetscFVCellGeom       *cg;
890         PetscScalar           *cx, *cgrad;
891         PetscScalar           *xG;
892         PetscReal              dx[3];
893         PetscInt               d;
894 
895         ierr = DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cg);CHKERRQ(ierr);
896         ierr = DMPlexPointLocalRead(dm, cells[0], x, &cx);CHKERRQ(ierr);
897         ierr = DMPlexPointLocalRead(dmGrad, cells[0], grad, &cgrad);CHKERRQ(ierr);
898         ierr = DMPlexPointLocalFieldRef(dm, cells[1], field, x, &xG);CHKERRQ(ierr);
899         DMPlex_WaxpyD_Internal(dim, -1, cg->centroid, fg->centroid, dx);
900         for (d = 0; d < pdim; ++d) fx[d] = cx[d] + DMPlex_DotD_Internal(dim, &cgrad[d*dim], dx);
901         ierru = (*func)(time, fg->centroid, fg->normal, fx, xG, ctx);
902         if (ierru) {
903           ierr = ISRestoreIndices(faceIS, &faces);CHKERRQ(ierr);
904           ierr = ISDestroy(&faceIS);CHKERRQ(ierr);
905           goto cleanup;
906         }
907       } else {
908         PetscScalar       *xI;
909         PetscScalar       *xG;
910 
911         ierr = DMPlexPointLocalRead(dm, cells[0], x, &xI);CHKERRQ(ierr);
912         ierr = DMPlexPointLocalFieldRef(dm, cells[1], field, x, &xG);CHKERRQ(ierr);
913         ierru = (*func)(time, fg->centroid, fg->normal, xI, xG, ctx);
914         if (ierru) {
915           ierr = ISRestoreIndices(faceIS, &faces);CHKERRQ(ierr);
916           ierr = ISDestroy(&faceIS);CHKERRQ(ierr);
917           goto cleanup;
918         }
919       }
920     }
921     ierr = ISRestoreIndices(faceIS, &faces);CHKERRQ(ierr);
922     ierr = ISDestroy(&faceIS);CHKERRQ(ierr);
923   }
924   cleanup:
925   ierr = VecRestoreArray(locX, &x);CHKERRQ(ierr);
926   if (Grad) {
927     ierr = DMRestoreWorkArray(dm, pdim, MPIU_SCALAR, &fx);CHKERRQ(ierr);
928     ierr = VecRestoreArrayRead(Grad, &grad);CHKERRQ(ierr);
929   }
930   if (cellGeometry) {ierr = VecRestoreArrayRead(cellGeometry, &cellgeom);CHKERRQ(ierr);}
931   ierr = VecRestoreArrayRead(faceGeometry, &facegeom);CHKERRQ(ierr);
932   CHKERRQ(ierru);
933   PetscFunctionReturn(0);
934 }
935 
936 PetscErrorCode DMPlexInsertBoundaryValues_Plex(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
937 {
938   PetscDS        prob;
939   PetscInt       numBd, b;
940   PetscErrorCode ierr;
941 
942   PetscFunctionBegin;
943   ierr = DMGetDS(dm, &prob);CHKERRQ(ierr);
944   ierr = PetscDSGetNumBoundary(prob, &numBd);CHKERRQ(ierr);
945   for (b = 0; b < numBd; ++b) {
946     DMBoundaryConditionType type;
947     const char             *name, *labelname;
948     DMLabel                 label;
949     PetscInt                field, Nc;
950     const PetscInt         *comps;
951     PetscObject             obj;
952     PetscClassId            id;
953     void                    (*func)(void);
954     PetscInt                numids;
955     const PetscInt         *ids;
956     void                   *ctx;
957 
958     ierr = DMGetBoundary(dm, b, &type, &name, &labelname, &field, &Nc, &comps, &func, &numids, &ids, &ctx);CHKERRQ(ierr);
959     if (insertEssential != (type & DM_BC_ESSENTIAL)) continue;
960     ierr = DMGetLabel(dm, labelname, &label);CHKERRQ(ierr);
961     if (!label) SETERRQ2(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONGSTATE, "Label %s for boundary condition %s does not exist in the DM", labelname, name);
962     ierr = DMGetField(dm, field, NULL, &obj);CHKERRQ(ierr);
963     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
964     if (id == PETSCFE_CLASSID) {
965       switch (type) {
966         /* for FEM, there is no insertion to be done for non-essential boundary conditions */
967       case DM_BC_ESSENTIAL:
968         ierr = DMPlexLabelAddCells(dm,label);CHKERRQ(ierr);
969         ierr = DMPlexInsertBoundaryValuesEssential(dm, time, field, Nc, comps, label, numids, ids, (PetscErrorCode (*)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *)) func, ctx, locX);CHKERRQ(ierr);
970         ierr = DMPlexLabelClearCells(dm,label);CHKERRQ(ierr);
971         break;
972       case DM_BC_ESSENTIAL_FIELD:
973         ierr = DMPlexLabelAddCells(dm,label);CHKERRQ(ierr);
974         ierr = DMPlexInsertBoundaryValuesEssentialField(dm, time, locX, field, Nc, comps, label, numids, ids,
975                                                         (void (*)(PetscInt, PetscInt, PetscInt, const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[],
976                                                                   const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[],
977                                                                   PetscReal, const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[])) func, ctx, locX);CHKERRQ(ierr);
978         ierr = DMPlexLabelClearCells(dm,label);CHKERRQ(ierr);
979         break;
980       default: break;
981       }
982     } else if (id == PETSCFV_CLASSID) {
983       if (!faceGeomFVM) continue;
984       ierr = DMPlexInsertBoundaryValuesRiemann(dm, time, faceGeomFVM, cellGeomFVM, gradFVM, field, Nc, comps, label, numids, ids,
985                                                (PetscErrorCode (*)(PetscReal,const PetscReal*,const PetscReal*,const PetscScalar*,PetscScalar*,void*)) func, ctx, locX);CHKERRQ(ierr);
986     } else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", field);
987   }
988   PetscFunctionReturn(0);
989 }
990 
991 /*@
992   DMPlexInsertBoundaryValues - Puts coefficients which represent boundary values into the local solution vector
993 
994   Input Parameters:
995 + dm - The DM
996 . insertEssential - Should I insert essential (e.g. Dirichlet) or inessential (e.g. Neumann) boundary conditions
997 . time - The time
998 . faceGeomFVM - Face geometry data for FV discretizations
999 . cellGeomFVM - Cell geometry data for FV discretizations
1000 - gradFVM - Gradient reconstruction data for FV discretizations
1001 
1002   Output Parameters:
1003 . locX - Solution updated with boundary values
1004 
1005   Level: developer
1006 
1007 .seealso: DMProjectFunctionLabelLocal()
1008 @*/
1009 PetscErrorCode DMPlexInsertBoundaryValues(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
1010 {
1011   PetscErrorCode ierr;
1012 
1013   PetscFunctionBegin;
1014   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1015   PetscValidHeaderSpecific(locX, VEC_CLASSID, 2);
1016   if (faceGeomFVM) {PetscValidHeaderSpecific(faceGeomFVM, VEC_CLASSID, 4);}
1017   if (cellGeomFVM) {PetscValidHeaderSpecific(cellGeomFVM, VEC_CLASSID, 5);}
1018   if (gradFVM)     {PetscValidHeaderSpecific(gradFVM, VEC_CLASSID, 6);}
1019   ierr = PetscTryMethod(dm,"DMPlexInsertBoundaryValues_C",(DM,PetscBool,Vec,PetscReal,Vec,Vec,Vec),(dm,insertEssential,locX,time,faceGeomFVM,cellGeomFVM,gradFVM));CHKERRQ(ierr);
1020   PetscFunctionReturn(0);
1021 }
1022 
1023 PetscErrorCode DMComputeL2Diff_Plex(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal [], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, PetscReal *diff)
1024 {
1025   Vec              localX;
1026   PetscErrorCode   ierr;
1027 
1028   PetscFunctionBegin;
1029   ierr = DMGetLocalVector(dm, &localX);CHKERRQ(ierr);
1030   ierr = DMPlexInsertBoundaryValues(dm, PETSC_TRUE, localX, time, NULL, NULL, NULL);CHKERRQ(ierr);
1031   ierr = DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX);CHKERRQ(ierr);
1032   ierr = DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX);CHKERRQ(ierr);
1033   ierr = DMPlexComputeL2DiffLocal(dm, time, funcs, ctxs, localX, diff);CHKERRQ(ierr);
1034   ierr = DMRestoreLocalVector(dm, &localX);CHKERRQ(ierr);
1035   PetscFunctionReturn(0);
1036 }
1037 
1038 /*@C
1039   DMComputeL2DiffLocal - This function computes the L_2 difference between a function u and an FEM interpolant solution u_h.
1040 
1041   Collective on dm
1042 
1043   Input Parameters:
1044 + dm     - The DM
1045 . time   - The time
1046 . funcs  - The functions to evaluate for each field component
1047 . ctxs   - Optional array of contexts to pass to each function, or NULL.
1048 - localX - The coefficient vector u_h, a local vector
1049 
1050   Output Parameter:
1051 . diff - The diff ||u - u_h||_2
1052 
1053   Level: developer
1054 
1055 .seealso: DMProjectFunction(), DMComputeL2FieldDiff(), DMComputeL2GradientDiff()
1056 @*/
1057 PetscErrorCode DMPlexComputeL2DiffLocal(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal [], PetscInt, PetscScalar *, void *), void **ctxs, Vec localX, PetscReal *diff)
1058 {
1059   const PetscInt   debug = ((DM_Plex*)dm->data)->printL2;
1060   DM               tdm;
1061   Vec              tv;
1062   PetscSection     section;
1063   PetscQuadrature  quad;
1064   PetscFEGeom      fegeom;
1065   PetscScalar     *funcVal, *interpolant;
1066   PetscReal       *coords, *gcoords;
1067   PetscReal        localDiff = 0.0;
1068   const PetscReal *quadWeights;
1069   PetscInt         dim, coordDim, numFields, numComponents = 0, qNc, Nq, cellHeight, cStart, cEnd, cEndInterior, c, field, fieldOffset;
1070   PetscBool        transform;
1071   PetscErrorCode   ierr;
1072 
1073   PetscFunctionBegin;
1074   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
1075   ierr = DMGetCoordinateDim(dm, &coordDim);CHKERRQ(ierr);
1076   fegeom.dimEmbed = coordDim;
1077   ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);
1078   ierr = PetscSectionGetNumFields(section, &numFields);CHKERRQ(ierr);
1079   ierr = DMGetBasisTransformDM_Internal(dm, &tdm);CHKERRQ(ierr);
1080   ierr = DMGetBasisTransformVec_Internal(dm, &tv);CHKERRQ(ierr);
1081   ierr = DMHasBasisTransform(dm, &transform);CHKERRQ(ierr);
1082   for (field = 0; field < numFields; ++field) {
1083     PetscObject  obj;
1084     PetscClassId id;
1085     PetscInt     Nc;
1086 
1087     ierr = DMGetField(dm, field, NULL, &obj);CHKERRQ(ierr);
1088     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
1089     if (id == PETSCFE_CLASSID) {
1090       PetscFE fe = (PetscFE) obj;
1091 
1092       ierr = PetscFEGetQuadrature(fe, &quad);CHKERRQ(ierr);
1093       ierr = PetscFEGetNumComponents(fe, &Nc);CHKERRQ(ierr);
1094     } else if (id == PETSCFV_CLASSID) {
1095       PetscFV fv = (PetscFV) obj;
1096 
1097       ierr = PetscFVGetQuadrature(fv, &quad);CHKERRQ(ierr);
1098       ierr = PetscFVGetNumComponents(fv, &Nc);CHKERRQ(ierr);
1099     } else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", field);
1100     numComponents += Nc;
1101   }
1102   ierr = PetscQuadratureGetData(quad, NULL, &qNc, &Nq, NULL, &quadWeights);CHKERRQ(ierr);
1103   if ((qNc != 1) && (qNc != numComponents)) SETERRQ2(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_SIZ, "Quadrature components %D != %D field components", qNc, numComponents);
1104   ierr = PetscMalloc6(numComponents,&funcVal,numComponents,&interpolant,coordDim*Nq,&coords,Nq,&fegeom.detJ,coordDim*coordDim*Nq,&fegeom.J,coordDim*coordDim*Nq,&fegeom.invJ);CHKERRQ(ierr);
1105   ierr = DMPlexGetVTKCellHeight(dm, &cellHeight);CHKERRQ(ierr);
1106   ierr = DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd);CHKERRQ(ierr);
1107   ierr = DMPlexGetGhostCellStratum(dm, &cEndInterior, NULL);CHKERRQ(ierr);
1108   cEnd = cEndInterior < 0 ? cEnd : cEndInterior;
1109   ierr = DMPlexGetHybridBounds(dm, &cEndInterior, NULL, NULL, NULL);CHKERRQ(ierr);
1110   cEnd = cEndInterior < 0 ? cEnd : cEndInterior;
1111   for (c = cStart; c < cEnd; ++c) {
1112     PetscScalar *x = NULL;
1113     PetscReal    elemDiff = 0.0;
1114     PetscInt     qc = 0;
1115 
1116     ierr = DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ);CHKERRQ(ierr);
1117     ierr = DMPlexVecGetClosure(dm, NULL, localX, c, NULL, &x);CHKERRQ(ierr);
1118 
1119     for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1120       PetscObject  obj;
1121       PetscClassId id;
1122       void * const ctx = ctxs ? ctxs[field] : NULL;
1123       PetscInt     Nb, Nc, q, fc;
1124 
1125       ierr = DMGetField(dm, field, NULL, &obj);CHKERRQ(ierr);
1126       ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
1127       if (id == PETSCFE_CLASSID)      {ierr = PetscFEGetNumComponents((PetscFE) obj, &Nc);CHKERRQ(ierr);ierr = PetscFEGetDimension((PetscFE) obj, &Nb);CHKERRQ(ierr);}
1128       else if (id == PETSCFV_CLASSID) {ierr = PetscFVGetNumComponents((PetscFV) obj, &Nc);CHKERRQ(ierr);Nb = 1;}
1129       else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", field);
1130       if (debug) {
1131         char title[1024];
1132         ierr = PetscSNPrintf(title, 1023, "Solution for Field %D", field);CHKERRQ(ierr);
1133         ierr = DMPrintCellVector(c, title, Nb, &x[fieldOffset]);CHKERRQ(ierr);
1134       }
1135       for (q = 0; q < Nq; ++q) {
1136         PetscFEGeom qgeom;
1137 
1138         qgeom.dimEmbed = fegeom.dimEmbed;
1139         qgeom.J        = &fegeom.J[q*coordDim*coordDim];
1140         qgeom.invJ     = &fegeom.invJ[q*coordDim*coordDim];
1141         qgeom.detJ     = &fegeom.detJ[q];
1142         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);
1143         if (transform) {
1144           gcoords = &coords[coordDim*Nq];
1145           ierr = DMPlexBasisTransformApplyReal_Internal(dm, &coords[coordDim*q], PETSC_TRUE, coordDim, &coords[coordDim*q], gcoords, dm->transformCtx);CHKERRQ(ierr);
1146         } else {
1147           gcoords = &coords[coordDim*q];
1148         }
1149         ierr = (*funcs[field])(coordDim, time, gcoords, Nc, funcVal, ctx);
1150         if (ierr) {
1151           PetscErrorCode ierr2;
1152           ierr2 = DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x);CHKERRQ(ierr2);
1153           ierr2 = DMRestoreLocalVector(dm, &localX);CHKERRQ(ierr2);
1154           ierr2 = PetscFree6(funcVal,interpolant,coords,fegeom.detJ,fegeom.J,fegeom.invJ);CHKERRQ(ierr2);
1155           CHKERRQ(ierr);
1156         }
1157         if (transform) {ierr = DMPlexBasisTransformApply_Internal(dm, &coords[coordDim*q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx);CHKERRQ(ierr);}
1158         if (id == PETSCFE_CLASSID)      {ierr = PetscFEInterpolate_Static((PetscFE) obj, &x[fieldOffset], &qgeom, q, interpolant);CHKERRQ(ierr);}
1159         else if (id == PETSCFV_CLASSID) {ierr = PetscFVInterpolate_Static((PetscFV) obj, &x[fieldOffset], q, interpolant);CHKERRQ(ierr);}
1160         else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", field);
1161         for (fc = 0; fc < Nc; ++fc) {
1162           const PetscReal wt = quadWeights[q*qNc+(qNc == 1 ? 0 : qc+fc)];
1163           if (debug) {ierr = PetscPrintf(PETSC_COMM_SELF, "    elem %D field %D,%D diff %g\n", c, field, fc, (double)(PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc]))*wt*fegeom.detJ[q]));CHKERRQ(ierr);}
1164           elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc]))*wt*fegeom.detJ[q];
1165         }
1166       }
1167       fieldOffset += Nb;
1168       qc += Nc;
1169     }
1170     ierr = DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x);CHKERRQ(ierr);
1171     if (debug) {ierr = PetscPrintf(PETSC_COMM_SELF, "  elem %D diff %g\n", c, (double)elemDiff);CHKERRQ(ierr);}
1172     localDiff += elemDiff;
1173   }
1174   ierr  = PetscFree6(funcVal,interpolant,coords,fegeom.detJ,fegeom.J,fegeom.invJ);CHKERRQ(ierr);
1175   ierr  = MPIU_Allreduce(&localDiff, diff, 1, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm));CHKERRQ(ierr);
1176   *diff = PetscSqrtReal(*diff);
1177   PetscFunctionReturn(0);
1178 }
1179 
1180 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)
1181 {
1182   const PetscInt   debug = ((DM_Plex*)dm->data)->printL2;
1183   DM               tdm;
1184   PetscSection     section;
1185   PetscQuadrature  quad;
1186   Vec              localX, tv;
1187   PetscScalar     *funcVal, *interpolant;
1188   const PetscReal *quadWeights;
1189   PetscFEGeom      fegeom;
1190   PetscReal       *coords, *gcoords;
1191   PetscReal        localDiff = 0.0;
1192   PetscInt         dim, coordDim, qNc = 0, Nq = 0, numFields, numComponents = 0, cStart, cEnd, c, field, fieldOffset;
1193   PetscBool        transform;
1194   PetscErrorCode   ierr;
1195 
1196   PetscFunctionBegin;
1197   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
1198   ierr = DMGetCoordinateDim(dm, &coordDim);CHKERRQ(ierr);
1199   fegeom.dimEmbed = coordDim;
1200   ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);
1201   ierr = PetscSectionGetNumFields(section, &numFields);CHKERRQ(ierr);
1202   ierr = DMGetLocalVector(dm, &localX);CHKERRQ(ierr);
1203   ierr = DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX);CHKERRQ(ierr);
1204   ierr = DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX);CHKERRQ(ierr);
1205   ierr = DMGetBasisTransformDM_Internal(dm, &tdm);CHKERRQ(ierr);
1206   ierr = DMGetBasisTransformVec_Internal(dm, &tv);CHKERRQ(ierr);
1207   ierr = DMHasBasisTransform(dm, &transform);CHKERRQ(ierr);
1208   for (field = 0; field < numFields; ++field) {
1209     PetscFE  fe;
1210     PetscInt Nc;
1211 
1212     ierr = DMGetField(dm, field, NULL, (PetscObject *) &fe);CHKERRQ(ierr);
1213     ierr = PetscFEGetQuadrature(fe, &quad);CHKERRQ(ierr);
1214     ierr = PetscFEGetNumComponents(fe, &Nc);CHKERRQ(ierr);
1215     numComponents += Nc;
1216   }
1217   ierr = PetscQuadratureGetData(quad, NULL, &qNc, &Nq, NULL, &quadWeights);CHKERRQ(ierr);
1218   if ((qNc != 1) && (qNc != numComponents)) SETERRQ2(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_SIZ, "Quadrature components %D != %D field components", qNc, numComponents);
1219   /* ierr = DMProjectFunctionLocal(dm, fe, funcs, INSERT_BC_VALUES, localX);CHKERRQ(ierr); */
1220   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);
1221   ierr = DMPlexGetInteriorCellStratum(dm, &cStart, &cEnd);CHKERRQ(ierr);
1222   for (c = cStart; c < cEnd; ++c) {
1223     PetscScalar *x = NULL;
1224     PetscReal    elemDiff = 0.0;
1225     PetscInt     qc = 0;
1226 
1227     ierr = DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ);CHKERRQ(ierr);
1228     ierr = DMPlexVecGetClosure(dm, NULL, localX, c, NULL, &x);CHKERRQ(ierr);
1229 
1230     for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1231       PetscFE          fe;
1232       void * const     ctx = ctxs ? ctxs[field] : NULL;
1233       PetscInt         Nb, Nc, q, fc;
1234 
1235       ierr = DMGetField(dm, field, NULL, (PetscObject *) &fe);CHKERRQ(ierr);
1236       ierr = PetscFEGetDimension(fe, &Nb);CHKERRQ(ierr);
1237       ierr = PetscFEGetNumComponents(fe, &Nc);CHKERRQ(ierr);
1238       if (debug) {
1239         char title[1024];
1240         ierr = PetscSNPrintf(title, 1023, "Solution for Field %D", field);CHKERRQ(ierr);
1241         ierr = DMPrintCellVector(c, title, Nb, &x[fieldOffset]);CHKERRQ(ierr);
1242       }
1243       for (q = 0; q < Nq; ++q) {
1244         PetscFEGeom qgeom;
1245 
1246         qgeom.dimEmbed = fegeom.dimEmbed;
1247         qgeom.J        = &fegeom.J[q*coordDim*coordDim];
1248         qgeom.invJ     = &fegeom.invJ[q*coordDim*coordDim];
1249         qgeom.detJ     = &fegeom.detJ[q];
1250         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);
1251         if (transform) {
1252           gcoords = &coords[coordDim*Nq];
1253           ierr = DMPlexBasisTransformApplyReal_Internal(dm, &coords[coordDim*q], PETSC_TRUE, coordDim, &coords[coordDim*q], gcoords, dm->transformCtx);CHKERRQ(ierr);
1254         } else {
1255           gcoords = &coords[coordDim*q];
1256         }
1257         ierr = (*funcs[field])(coordDim, time, gcoords, n, Nc, funcVal, ctx);
1258         if (ierr) {
1259           PetscErrorCode ierr2;
1260           ierr2 = DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x);CHKERRQ(ierr2);
1261           ierr2 = DMRestoreLocalVector(dm, &localX);CHKERRQ(ierr2);
1262           ierr2 = PetscFree6(funcVal,coords,fegeom.J,fegeom.invJ,interpolant,fegeom.detJ);CHKERRQ(ierr2);
1263           CHKERRQ(ierr);
1264         }
1265         if (transform) {ierr = DMPlexBasisTransformApply_Internal(dm, &coords[coordDim*q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx);CHKERRQ(ierr);}
1266         ierr = PetscFEInterpolateGradient_Static(fe, &x[fieldOffset], &qgeom, q, interpolant);CHKERRQ(ierr);
1267         /* Overwrite with the dot product if the normal is given */
1268         if (n) {
1269           for (fc = 0; fc < Nc; ++fc) {
1270             PetscScalar sum = 0.0;
1271             PetscInt    d;
1272             for (d = 0; d < dim; ++d) sum += interpolant[fc*dim+d]*n[d];
1273             interpolant[fc] = sum;
1274           }
1275         }
1276         for (fc = 0; fc < Nc; ++fc) {
1277           const PetscReal wt = quadWeights[q*qNc+(qNc == 1 ? 0 : qc+fc)];
1278           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);}
1279           elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc]))*wt*fegeom.detJ[q];
1280         }
1281       }
1282       fieldOffset += Nb;
1283       qc          += Nc;
1284     }
1285     ierr = DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x);CHKERRQ(ierr);
1286     if (debug) {ierr = PetscPrintf(PETSC_COMM_SELF, "  elem %D diff %g\n", c, (double)elemDiff);CHKERRQ(ierr);}
1287     localDiff += elemDiff;
1288   }
1289   ierr  = PetscFree6(funcVal,coords,fegeom.J,fegeom.invJ,interpolant,fegeom.detJ);CHKERRQ(ierr);
1290   ierr  = DMRestoreLocalVector(dm, &localX);CHKERRQ(ierr);
1291   ierr  = MPIU_Allreduce(&localDiff, diff, 1, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm));CHKERRQ(ierr);
1292   *diff = PetscSqrtReal(*diff);
1293   PetscFunctionReturn(0);
1294 }
1295 
1296 PetscErrorCode DMComputeL2FieldDiff_Plex(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal [], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, PetscReal *diff)
1297 {
1298   const PetscInt   debug = ((DM_Plex*)dm->data)->printL2;
1299   DM               tdm;
1300   PetscSection     section;
1301   PetscQuadrature  quad;
1302   Vec              localX, tv;
1303   PetscFEGeom      fegeom;
1304   PetscScalar     *funcVal, *interpolant;
1305   PetscReal       *coords, *gcoords;
1306   PetscReal       *localDiff;
1307   const PetscReal *quadPoints, *quadWeights;
1308   PetscInt         dim, coordDim, numFields, numComponents = 0, qNc, Nq, cStart, cEnd, c, field, fieldOffset;
1309   PetscBool        transform;
1310   PetscErrorCode   ierr;
1311 
1312   PetscFunctionBegin;
1313   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
1314   ierr = DMGetCoordinateDim(dm, &coordDim);CHKERRQ(ierr);
1315   ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);
1316   ierr = PetscSectionGetNumFields(section, &numFields);CHKERRQ(ierr);
1317   ierr = DMGetLocalVector(dm, &localX);CHKERRQ(ierr);
1318   ierr = VecSet(localX, 0.0);CHKERRQ(ierr);
1319   ierr = DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX);CHKERRQ(ierr);
1320   ierr = DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX);CHKERRQ(ierr);
1321   ierr = DMProjectFunctionLocal(dm, time, funcs, ctxs, INSERT_BC_VALUES, localX);CHKERRQ(ierr);
1322   ierr = DMGetBasisTransformDM_Internal(dm, &tdm);CHKERRQ(ierr);
1323   ierr = DMGetBasisTransformVec_Internal(dm, &tv);CHKERRQ(ierr);
1324   ierr = DMHasBasisTransform(dm, &transform);CHKERRQ(ierr);
1325   for (field = 0; field < numFields; ++field) {
1326     PetscObject  obj;
1327     PetscClassId id;
1328     PetscInt     Nc;
1329 
1330     ierr = DMGetField(dm, field, NULL, &obj);CHKERRQ(ierr);
1331     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
1332     if (id == PETSCFE_CLASSID) {
1333       PetscFE fe = (PetscFE) obj;
1334 
1335       ierr = PetscFEGetQuadrature(fe, &quad);CHKERRQ(ierr);
1336       ierr = PetscFEGetNumComponents(fe, &Nc);CHKERRQ(ierr);
1337     } else if (id == PETSCFV_CLASSID) {
1338       PetscFV fv = (PetscFV) obj;
1339 
1340       ierr = PetscFVGetQuadrature(fv, &quad);CHKERRQ(ierr);
1341       ierr = PetscFVGetNumComponents(fv, &Nc);CHKERRQ(ierr);
1342     } else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", field);
1343     numComponents += Nc;
1344   }
1345   ierr = PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights);CHKERRQ(ierr);
1346   if ((qNc != 1) && (qNc != numComponents)) SETERRQ2(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_SIZ, "Quadrature components %D != %D field components", qNc, numComponents);
1347   ierr = PetscCalloc7(numFields,&localDiff,numComponents,&funcVal,numComponents,&interpolant,coordDim*(Nq+1),&coords,Nq,&fegeom.detJ,coordDim*coordDim*Nq,&fegeom.J,coordDim*coordDim*Nq,&fegeom.invJ);CHKERRQ(ierr);
1348   ierr = DMPlexGetInteriorCellStratum(dm, &cStart, &cEnd);CHKERRQ(ierr);
1349   for (c = cStart; c < cEnd; ++c) {
1350     PetscScalar *x = NULL;
1351     PetscInt     qc = 0;
1352 
1353     ierr = DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ);CHKERRQ(ierr);
1354     ierr = DMPlexVecGetClosure(dm, NULL, localX, c, NULL, &x);CHKERRQ(ierr);
1355 
1356     for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1357       PetscObject  obj;
1358       PetscClassId id;
1359       void * const ctx = ctxs ? ctxs[field] : NULL;
1360       PetscInt     Nb, Nc, q, fc;
1361 
1362       PetscReal       elemDiff = 0.0;
1363 
1364       ierr = DMGetField(dm, field, NULL, &obj);CHKERRQ(ierr);
1365       ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
1366       if (id == PETSCFE_CLASSID)      {ierr = PetscFEGetNumComponents((PetscFE) obj, &Nc);CHKERRQ(ierr);ierr = PetscFEGetDimension((PetscFE) obj, &Nb);CHKERRQ(ierr);}
1367       else if (id == PETSCFV_CLASSID) {ierr = PetscFVGetNumComponents((PetscFV) obj, &Nc);CHKERRQ(ierr);Nb = 1;}
1368       else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", field);
1369       if (debug) {
1370         char title[1024];
1371         ierr = PetscSNPrintf(title, 1023, "Solution for Field %D", field);CHKERRQ(ierr);
1372         ierr = DMPrintCellVector(c, title, Nb, &x[fieldOffset]);CHKERRQ(ierr);
1373       }
1374       for (q = 0; q < Nq; ++q) {
1375         PetscFEGeom qgeom;
1376 
1377         qgeom.dimEmbed = fegeom.dimEmbed;
1378         qgeom.J        = &fegeom.J[q*coordDim*coordDim];
1379         qgeom.invJ     = &fegeom.invJ[q*coordDim*coordDim];
1380         qgeom.detJ     = &fegeom.detJ[q];
1381         if (fegeom.detJ[q] <= 0.0) SETERRQ3(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for element %D, quadrature point %D", (double)fegeom.detJ[q], c, q);
1382         if (transform) {
1383           gcoords = &coords[coordDim*Nq];
1384           ierr = DMPlexBasisTransformApplyReal_Internal(dm, &coords[coordDim*q], PETSC_TRUE, coordDim, &coords[coordDim*q], gcoords, dm->transformCtx);CHKERRQ(ierr);
1385         } else {
1386           gcoords = &coords[coordDim*q];
1387         }
1388         ierr = (*funcs[field])(coordDim, time, gcoords, Nc, funcVal, ctx);
1389         if (ierr) {
1390           PetscErrorCode ierr2;
1391           ierr2 = DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x);CHKERRQ(ierr2);
1392           ierr2 = DMRestoreLocalVector(dm, &localX);CHKERRQ(ierr2);
1393           ierr2 = PetscFree7(localDiff,funcVal,interpolant,coords,fegeom.detJ,fegeom.J,fegeom.invJ);CHKERRQ(ierr2);
1394           CHKERRQ(ierr);
1395         }
1396         if (transform) {ierr = DMPlexBasisTransformApply_Internal(dm, &coords[coordDim*q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx);CHKERRQ(ierr);}
1397         if (id == PETSCFE_CLASSID)      {ierr = PetscFEInterpolate_Static((PetscFE) obj, &x[fieldOffset], &qgeom, q, interpolant);CHKERRQ(ierr);}
1398         else if (id == PETSCFV_CLASSID) {ierr = PetscFVInterpolate_Static((PetscFV) obj, &x[fieldOffset], q, interpolant);CHKERRQ(ierr);}
1399         else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", field);
1400         for (fc = 0; fc < Nc; ++fc) {
1401           const PetscReal wt = quadWeights[q*qNc+(qNc == 1 ? 0 : qc+fc)];
1402           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);}
1403           elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc]))*wt*fegeom.detJ[q];
1404         }
1405       }
1406       fieldOffset += Nb;
1407       qc          += Nc;
1408       localDiff[field] += elemDiff;
1409       if (debug) {ierr = PetscPrintf(PETSC_COMM_SELF, "  elem %D field %D cum diff %g\n", c, field, (double)localDiff[field]);CHKERRQ(ierr);}
1410     }
1411     ierr = DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x);CHKERRQ(ierr);
1412   }
1413   ierr = DMRestoreLocalVector(dm, &localX);CHKERRQ(ierr);
1414   ierr = MPIU_Allreduce(localDiff, diff, numFields, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm));CHKERRQ(ierr);
1415   for (field = 0; field < numFields; ++field) diff[field] = PetscSqrtReal(diff[field]);
1416   ierr = PetscFree7(localDiff,funcVal,interpolant,coords,fegeom.detJ,fegeom.J,fegeom.invJ);CHKERRQ(ierr);
1417   PetscFunctionReturn(0);
1418 }
1419 
1420 /*@C
1421   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.
1422 
1423   Collective on dm
1424 
1425   Input Parameters:
1426 + dm    - The DM
1427 . time  - The time
1428 . funcs - The functions to evaluate for each field component: NULL means that component does not contribute to error calculation
1429 . ctxs  - Optional array of contexts to pass to each function, or NULL.
1430 - X     - The coefficient vector u_h
1431 
1432   Output Parameter:
1433 . D - A Vec which holds the difference ||u - u_h||_2 for each cell
1434 
1435   Level: developer
1436 
1437 .seealso: DMProjectFunction(), DMComputeL2Diff(), DMPlexComputeL2FieldDiff(), DMComputeL2GradientDiff()
1438 @*/
1439 PetscErrorCode DMPlexComputeL2DiffVec(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal [], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, Vec D)
1440 {
1441   PetscSection     section;
1442   PetscQuadrature  quad;
1443   Vec              localX;
1444   PetscFEGeom      fegeom;
1445   PetscScalar     *funcVal, *interpolant;
1446   PetscReal       *coords;
1447   const PetscReal *quadPoints, *quadWeights;
1448   PetscInt         dim, coordDim, numFields, numComponents = 0, qNc, Nq, cStart, cEnd, c, field, fieldOffset;
1449   PetscErrorCode   ierr;
1450 
1451   PetscFunctionBegin;
1452   ierr = VecSet(D, 0.0);CHKERRQ(ierr);
1453   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
1454   ierr = DMGetCoordinateDim(dm, &coordDim);CHKERRQ(ierr);
1455   ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);
1456   ierr = PetscSectionGetNumFields(section, &numFields);CHKERRQ(ierr);
1457   ierr = DMGetLocalVector(dm, &localX);CHKERRQ(ierr);
1458   ierr = DMProjectFunctionLocal(dm, time, funcs, ctxs, INSERT_BC_VALUES, localX);CHKERRQ(ierr);
1459   ierr = DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX);CHKERRQ(ierr);
1460   ierr = DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX);CHKERRQ(ierr);
1461   for (field = 0; field < numFields; ++field) {
1462     PetscObject  obj;
1463     PetscClassId id;
1464     PetscInt     Nc;
1465 
1466     ierr = DMGetField(dm, field, NULL, &obj);CHKERRQ(ierr);
1467     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
1468     if (id == PETSCFE_CLASSID) {
1469       PetscFE fe = (PetscFE) obj;
1470 
1471       ierr = PetscFEGetQuadrature(fe, &quad);CHKERRQ(ierr);
1472       ierr = PetscFEGetNumComponents(fe, &Nc);CHKERRQ(ierr);
1473     } else if (id == PETSCFV_CLASSID) {
1474       PetscFV fv = (PetscFV) obj;
1475 
1476       ierr = PetscFVGetQuadrature(fv, &quad);CHKERRQ(ierr);
1477       ierr = PetscFVGetNumComponents(fv, &Nc);CHKERRQ(ierr);
1478     } else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", field);
1479     numComponents += Nc;
1480   }
1481   ierr = PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights);CHKERRQ(ierr);
1482   if ((qNc != 1) && (qNc != numComponents)) SETERRQ2(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_SIZ, "Quadrature components %D != %D field components", qNc, numComponents);
1483   ierr = PetscMalloc6(numComponents,&funcVal,numComponents,&interpolant,coordDim*Nq,&coords,Nq,&fegeom.detJ,coordDim*coordDim*Nq,&fegeom.J,coordDim*coordDim*Nq,&fegeom.invJ);CHKERRQ(ierr);
1484   ierr = DMPlexGetInteriorCellStratum(dm, &cStart, &cEnd);CHKERRQ(ierr);
1485   for (c = cStart; c < cEnd; ++c) {
1486     PetscScalar *x = NULL;
1487     PetscScalar  elemDiff = 0.0;
1488     PetscInt     qc = 0;
1489 
1490     ierr = DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ);CHKERRQ(ierr);
1491     ierr = DMPlexVecGetClosure(dm, NULL, localX, c, NULL, &x);CHKERRQ(ierr);
1492 
1493     for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1494       PetscObject  obj;
1495       PetscClassId id;
1496       void * const ctx = ctxs ? ctxs[field] : NULL;
1497       PetscInt     Nb, Nc, q, fc;
1498 
1499       ierr = DMGetField(dm, field, NULL, &obj);CHKERRQ(ierr);
1500       ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
1501       if (id == PETSCFE_CLASSID)      {ierr = PetscFEGetNumComponents((PetscFE) obj, &Nc);CHKERRQ(ierr);ierr = PetscFEGetDimension((PetscFE) obj, &Nb);CHKERRQ(ierr);}
1502       else if (id == PETSCFV_CLASSID) {ierr = PetscFVGetNumComponents((PetscFV) obj, &Nc);CHKERRQ(ierr);Nb = 1;}
1503       else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", field);
1504       if (funcs[field]) {
1505         for (q = 0; q < Nq; ++q) {
1506           PetscFEGeom qgeom;
1507 
1508           qgeom.dimEmbed = fegeom.dimEmbed;
1509           qgeom.J        = &fegeom.J[q*coordDim*coordDim];
1510           qgeom.invJ     = &fegeom.invJ[q*coordDim*coordDim];
1511           qgeom.detJ     = &fegeom.detJ[q];
1512           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);
1513           ierr = (*funcs[field])(coordDim, time, &coords[q*coordDim], Nc, funcVal, ctx);
1514           if (ierr) {
1515             PetscErrorCode ierr2;
1516             ierr2 = DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x);CHKERRQ(ierr2);
1517             ierr2 = PetscFree6(funcVal,interpolant,coords,fegeom.detJ,fegeom.J,fegeom.invJ);CHKERRQ(ierr2);
1518             ierr2 = DMRestoreLocalVector(dm, &localX);CHKERRQ(ierr2);
1519             CHKERRQ(ierr);
1520           }
1521           if (id == PETSCFE_CLASSID)      {ierr = PetscFEInterpolate_Static((PetscFE) obj, &x[fieldOffset], &qgeom, q, interpolant);CHKERRQ(ierr);}
1522           else if (id == PETSCFV_CLASSID) {ierr = PetscFVInterpolate_Static((PetscFV) obj, &x[fieldOffset], q, interpolant);CHKERRQ(ierr);}
1523           else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", field);
1524           for (fc = 0; fc < Nc; ++fc) {
1525             const PetscReal wt = quadWeights[q*qNc+(qNc == 1 ? 0 : qc+fc)];
1526             elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc]))*wt*fegeom.detJ[q];
1527           }
1528         }
1529       }
1530       fieldOffset += Nb;
1531       qc          += Nc;
1532     }
1533     ierr = DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x);CHKERRQ(ierr);
1534     ierr = VecSetValue(D, c - cStart, elemDiff, INSERT_VALUES);CHKERRQ(ierr);
1535   }
1536   ierr = PetscFree6(funcVal,interpolant,coords,fegeom.detJ,fegeom.J,fegeom.invJ);CHKERRQ(ierr);
1537   ierr = DMRestoreLocalVector(dm, &localX);CHKERRQ(ierr);
1538   ierr = VecSqrtAbs(D);CHKERRQ(ierr);
1539   PetscFunctionReturn(0);
1540 }
1541 
1542 /*@C
1543   DMPlexComputeGradientClementInterpolant - This function computes the L2 projection of the cellwise gradient of a function u onto P1, and stores it in a Vec.
1544 
1545   Collective on dm
1546 
1547   Input Parameters:
1548 + dm - The DM
1549 - LocX  - The coefficient vector u_h
1550 
1551   Output Parameter:
1552 . locC - A Vec which holds the Clement interpolant of the gradient
1553 
1554   Notes:
1555     Add citation to (Clement, 1975) and definition of the interpolant
1556   \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
1557 
1558   Level: developer
1559 
1560 .seealso: DMProjectFunction(), DMComputeL2Diff(), DMPlexComputeL2FieldDiff(), DMComputeL2GradientDiff()
1561 @*/
1562 PetscErrorCode DMPlexComputeGradientClementInterpolant(DM dm, Vec locX, Vec locC)
1563 {
1564   DM_Plex         *mesh  = (DM_Plex *) dm->data;
1565   PetscInt         debug = mesh->printFEM;
1566   DM               dmC;
1567   PetscSection     section;
1568   PetscQuadrature  quad;
1569   PetscScalar     *interpolant, *gradsum;
1570   PetscFEGeom      fegeom;
1571   PetscReal       *coords;
1572   const PetscReal *quadPoints, *quadWeights;
1573   PetscInt         dim, coordDim, numFields, numComponents = 0, qNc, Nq, cStart, cEnd, vStart, vEnd, v, field, fieldOffset;
1574   PetscErrorCode   ierr;
1575 
1576   PetscFunctionBegin;
1577   ierr = VecGetDM(locC, &dmC);CHKERRQ(ierr);
1578   ierr = VecSet(locC, 0.0);CHKERRQ(ierr);
1579   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
1580   ierr = DMGetCoordinateDim(dm, &coordDim);CHKERRQ(ierr);
1581   fegeom.dimEmbed = coordDim;
1582   ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);
1583   ierr = PetscSectionGetNumFields(section, &numFields);CHKERRQ(ierr);
1584   for (field = 0; field < numFields; ++field) {
1585     PetscObject  obj;
1586     PetscClassId id;
1587     PetscInt     Nc;
1588 
1589     ierr = DMGetField(dm, field, NULL, &obj);CHKERRQ(ierr);
1590     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
1591     if (id == PETSCFE_CLASSID) {
1592       PetscFE fe = (PetscFE) obj;
1593 
1594       ierr = PetscFEGetQuadrature(fe, &quad);CHKERRQ(ierr);
1595       ierr = PetscFEGetNumComponents(fe, &Nc);CHKERRQ(ierr);
1596     } else if (id == PETSCFV_CLASSID) {
1597       PetscFV fv = (PetscFV) obj;
1598 
1599       ierr = PetscFVGetQuadrature(fv, &quad);CHKERRQ(ierr);
1600       ierr = PetscFVGetNumComponents(fv, &Nc);CHKERRQ(ierr);
1601     } else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", field);
1602     numComponents += Nc;
1603   }
1604   ierr = PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights);CHKERRQ(ierr);
1605   if ((qNc != 1) && (qNc != numComponents)) SETERRQ2(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_SIZ, "Quadrature components %D != %D field components", qNc, numComponents);
1606   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);
1607   ierr = DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd);CHKERRQ(ierr);
1608   ierr = DMPlexGetInteriorCellStratum(dm, &cStart, &cEnd);CHKERRQ(ierr);
1609   for (v = vStart; v < vEnd; ++v) {
1610     PetscScalar volsum = 0.0;
1611     PetscInt   *star = NULL;
1612     PetscInt    starSize, st, d, fc;
1613 
1614     ierr = PetscArrayzero(gradsum, coordDim*numComponents);CHKERRQ(ierr);
1615     ierr = DMPlexGetTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star);CHKERRQ(ierr);
1616     for (st = 0; st < starSize*2; st += 2) {
1617       const PetscInt cell = star[st];
1618       PetscScalar   *grad = &gradsum[coordDim*numComponents];
1619       PetscScalar   *x    = NULL;
1620       PetscReal      vol  = 0.0;
1621 
1622       if ((cell < cStart) || (cell >= cEnd)) continue;
1623       ierr = DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ);CHKERRQ(ierr);
1624       ierr = DMPlexVecGetClosure(dm, NULL, locX, cell, NULL, &x);CHKERRQ(ierr);
1625       for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1626         PetscObject  obj;
1627         PetscClassId id;
1628         PetscInt     Nb, Nc, q, qc = 0;
1629 
1630         ierr = PetscArrayzero(grad, coordDim*numComponents);CHKERRQ(ierr);
1631         ierr = DMGetField(dm, field, NULL, &obj);CHKERRQ(ierr);
1632         ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
1633         if (id == PETSCFE_CLASSID)      {ierr = PetscFEGetNumComponents((PetscFE) obj, &Nc);CHKERRQ(ierr);ierr = PetscFEGetDimension((PetscFE) obj, &Nb);CHKERRQ(ierr);}
1634         else if (id == PETSCFV_CLASSID) {ierr = PetscFVGetNumComponents((PetscFV) obj, &Nc);CHKERRQ(ierr);Nb = 1;}
1635         else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", field);
1636         for (q = 0; q < Nq; ++q) {
1637           PetscFEGeom qgeom;
1638 
1639           qgeom.dimEmbed = fegeom.dimEmbed;
1640           qgeom.J        = &fegeom.J[q*coordDim*coordDim];
1641           qgeom.invJ     = &fegeom.invJ[q*coordDim*coordDim];
1642           qgeom.detJ     = &fegeom.detJ[q];
1643           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);
1644           if (ierr) {
1645             PetscErrorCode ierr2;
1646             ierr2 = DMPlexVecRestoreClosure(dm, NULL, locX, cell, NULL, &x);CHKERRQ(ierr2);
1647             ierr2 = DMPlexRestoreTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star);CHKERRQ(ierr2);
1648             ierr2 = PetscFree6(gradsum,interpolant,coords,fegeom.detJ,fegeom.J,fegeom.invJ);CHKERRQ(ierr2);
1649             CHKERRQ(ierr);
1650           }
1651           if (id == PETSCFE_CLASSID)      {ierr = PetscFEInterpolateGradient_Static((PetscFE) obj, &x[fieldOffset], &qgeom, q, interpolant);CHKERRQ(ierr);}
1652           else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", field);
1653           for (fc = 0; fc < Nc; ++fc) {
1654             const PetscReal wt = quadWeights[q*qNc+qc+fc];
1655 
1656             for (d = 0; d < coordDim; ++d) grad[fc*coordDim+d] += interpolant[fc*dim+d]*wt*fegeom.detJ[q];
1657           }
1658           vol += quadWeights[q*qNc]*fegeom.detJ[q];
1659         }
1660         fieldOffset += Nb;
1661         qc          += Nc;
1662       }
1663       ierr = DMPlexVecRestoreClosure(dm, NULL, locX, cell, NULL, &x);CHKERRQ(ierr);
1664       for (fc = 0; fc < numComponents; ++fc) {
1665         for (d = 0; d < coordDim; ++d) {
1666           gradsum[fc*coordDim+d] += grad[fc*coordDim+d];
1667         }
1668       }
1669       volsum += vol;
1670       if (debug) {
1671         ierr = PetscPrintf(PETSC_COMM_SELF, "Cell %D gradient: [", cell);CHKERRQ(ierr);
1672         for (fc = 0; fc < numComponents; ++fc) {
1673           for (d = 0; d < coordDim; ++d) {
1674             if (fc || d > 0) {ierr = PetscPrintf(PETSC_COMM_SELF, ", ");CHKERRQ(ierr);}
1675             ierr = PetscPrintf(PETSC_COMM_SELF, "%g", (double)PetscRealPart(grad[fc*coordDim+d]));CHKERRQ(ierr);
1676           }
1677         }
1678         ierr = PetscPrintf(PETSC_COMM_SELF, "]\n");CHKERRQ(ierr);
1679       }
1680     }
1681     for (fc = 0; fc < numComponents; ++fc) {
1682       for (d = 0; d < coordDim; ++d) gradsum[fc*coordDim+d] /= volsum;
1683     }
1684     ierr = DMPlexRestoreTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star);CHKERRQ(ierr);
1685     ierr = DMPlexVecSetClosure(dmC, NULL, locC, v, gradsum, INSERT_VALUES);CHKERRQ(ierr);
1686   }
1687   ierr = PetscFree6(gradsum,interpolant,coords,fegeom.detJ,fegeom.J,fegeom.invJ);CHKERRQ(ierr);
1688   PetscFunctionReturn(0);
1689 }
1690 
1691 static PetscErrorCode DMPlexComputeIntegral_Internal(DM dm, Vec X, PetscInt cStart, PetscInt cEnd, PetscScalar *cintegral, void *user)
1692 {
1693   DM                 dmAux = NULL;
1694   PetscDS            prob,    probAux = NULL;
1695   PetscSection       section, sectionAux;
1696   Vec                locX,    locA;
1697   PetscInt           dim, numCells = cEnd - cStart, c, f;
1698   PetscBool          useFVM = PETSC_FALSE;
1699   /* DS */
1700   PetscInt           Nf,    totDim,    *uOff, *uOff_x, numConstants;
1701   PetscInt           NfAux, totDimAux, *aOff;
1702   PetscScalar       *u, *a;
1703   const PetscScalar *constants;
1704   /* Geometry */
1705   PetscFEGeom       *cgeomFEM;
1706   DM                 dmGrad;
1707   PetscQuadrature    affineQuad = NULL;
1708   Vec                cellGeometryFVM = NULL, faceGeometryFVM = NULL, locGrad = NULL;
1709   PetscFVCellGeom   *cgeomFVM;
1710   const PetscScalar *lgrad;
1711   PetscInt           maxDegree;
1712   DMField            coordField;
1713   IS                 cellIS;
1714   PetscErrorCode     ierr;
1715 
1716   PetscFunctionBegin;
1717   ierr = DMGetDS(dm, &prob);CHKERRQ(ierr);
1718   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
1719   ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);
1720   ierr = PetscSectionGetNumFields(section, &Nf);CHKERRQ(ierr);
1721   /* Determine which discretizations we have */
1722   for (f = 0; f < Nf; ++f) {
1723     PetscObject  obj;
1724     PetscClassId id;
1725 
1726     ierr = PetscDSGetDiscretization(prob, f, &obj);CHKERRQ(ierr);
1727     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
1728     if (id == PETSCFV_CLASSID) useFVM = PETSC_TRUE;
1729   }
1730   /* Get local solution with boundary values */
1731   ierr = DMGetLocalVector(dm, &locX);CHKERRQ(ierr);
1732   ierr = DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL);CHKERRQ(ierr);
1733   ierr = DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX);CHKERRQ(ierr);
1734   ierr = DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX);CHKERRQ(ierr);
1735   /* Read DS information */
1736   ierr = PetscDSGetTotalDimension(prob, &totDim);CHKERRQ(ierr);
1737   ierr = PetscDSGetComponentOffsets(prob, &uOff);CHKERRQ(ierr);
1738   ierr = PetscDSGetComponentDerivativeOffsets(prob, &uOff_x);CHKERRQ(ierr);
1739   ierr = ISCreateStride(PETSC_COMM_SELF,numCells,cStart,1,&cellIS);CHKERRQ(ierr);
1740   ierr = PetscDSGetConstants(prob, &numConstants, &constants);CHKERRQ(ierr);
1741   /* Read Auxiliary DS information */
1742   ierr = PetscObjectQuery((PetscObject) dm, "dmAux", (PetscObject *) &dmAux);CHKERRQ(ierr);
1743   ierr = PetscObjectQuery((PetscObject) dm, "A", (PetscObject *) &locA);CHKERRQ(ierr);
1744   if (dmAux) {
1745     ierr = DMGetDS(dmAux, &probAux);CHKERRQ(ierr);
1746     ierr = PetscDSGetNumFields(probAux, &NfAux);CHKERRQ(ierr);
1747     ierr = DMGetLocalSection(dmAux, &sectionAux);CHKERRQ(ierr);
1748     ierr = PetscDSGetTotalDimension(probAux, &totDimAux);CHKERRQ(ierr);
1749     ierr = PetscDSGetComponentOffsets(probAux, &aOff);CHKERRQ(ierr);
1750   }
1751   /* Allocate data  arrays */
1752   ierr = PetscCalloc1(numCells*totDim, &u);CHKERRQ(ierr);
1753   if (dmAux) {ierr = PetscMalloc1(numCells*totDimAux, &a);CHKERRQ(ierr);}
1754   /* Read out geometry */
1755   ierr = DMGetCoordinateField(dm,&coordField);CHKERRQ(ierr);
1756   ierr = DMFieldGetDegree(coordField,cellIS,NULL,&maxDegree);CHKERRQ(ierr);
1757   if (maxDegree <= 1) {
1758     ierr = DMFieldCreateDefaultQuadrature(coordField,cellIS,&affineQuad);CHKERRQ(ierr);
1759     if (affineQuad) {
1760       ierr = DMFieldCreateFEGeom(coordField,cellIS,affineQuad,PETSC_FALSE,&cgeomFEM);CHKERRQ(ierr);
1761     }
1762   }
1763   if (useFVM) {
1764     PetscFV   fv = NULL;
1765     Vec       grad;
1766     PetscInt  fStart, fEnd;
1767     PetscBool compGrad;
1768 
1769     for (f = 0; f < Nf; ++f) {
1770       PetscObject  obj;
1771       PetscClassId id;
1772 
1773       ierr = PetscDSGetDiscretization(prob, f, &obj);CHKERRQ(ierr);
1774       ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
1775       if (id == PETSCFV_CLASSID) {fv = (PetscFV) obj; break;}
1776     }
1777     ierr = PetscFVGetComputeGradients(fv, &compGrad);CHKERRQ(ierr);
1778     ierr = PetscFVSetComputeGradients(fv, PETSC_TRUE);CHKERRQ(ierr);
1779     ierr = DMPlexComputeGeometryFVM(dm, &cellGeometryFVM, &faceGeometryFVM);CHKERRQ(ierr);
1780     ierr = DMPlexComputeGradientFVM(dm, fv, faceGeometryFVM, cellGeometryFVM, &dmGrad);CHKERRQ(ierr);
1781     ierr = PetscFVSetComputeGradients(fv, compGrad);CHKERRQ(ierr);
1782     ierr = VecGetArrayRead(cellGeometryFVM, (const PetscScalar **) &cgeomFVM);CHKERRQ(ierr);
1783     /* Reconstruct and limit cell gradients */
1784     ierr = DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd);CHKERRQ(ierr);
1785     ierr = DMGetGlobalVector(dmGrad, &grad);CHKERRQ(ierr);
1786     ierr = DMPlexReconstructGradients_Internal(dm, fv, fStart, fEnd, faceGeometryFVM, cellGeometryFVM, locX, grad);CHKERRQ(ierr);
1787     /* Communicate gradient values */
1788     ierr = DMGetLocalVector(dmGrad, &locGrad);CHKERRQ(ierr);
1789     ierr = DMGlobalToLocalBegin(dmGrad, grad, INSERT_VALUES, locGrad);CHKERRQ(ierr);
1790     ierr = DMGlobalToLocalEnd(dmGrad, grad, INSERT_VALUES, locGrad);CHKERRQ(ierr);
1791     ierr = DMRestoreGlobalVector(dmGrad, &grad);CHKERRQ(ierr);
1792     /* Handle non-essential (e.g. outflow) boundary values */
1793     ierr = DMPlexInsertBoundaryValues(dm, PETSC_FALSE, locX, 0.0, faceGeometryFVM, cellGeometryFVM, locGrad);CHKERRQ(ierr);
1794     ierr = VecGetArrayRead(locGrad, &lgrad);CHKERRQ(ierr);
1795   }
1796   /* Read out data from inputs */
1797   for (c = cStart; c < cEnd; ++c) {
1798     PetscScalar *x = NULL;
1799     PetscInt     i;
1800 
1801     ierr = DMPlexVecGetClosure(dm, section, locX, c, NULL, &x);CHKERRQ(ierr);
1802     for (i = 0; i < totDim; ++i) u[c*totDim+i] = x[i];
1803     ierr = DMPlexVecRestoreClosure(dm, section, locX, c, NULL, &x);CHKERRQ(ierr);
1804     if (dmAux) {
1805       ierr = DMPlexVecGetClosure(dmAux, sectionAux, locA, c, NULL, &x);CHKERRQ(ierr);
1806       for (i = 0; i < totDimAux; ++i) a[c*totDimAux+i] = x[i];
1807       ierr = DMPlexVecRestoreClosure(dmAux, sectionAux, locA, c, NULL, &x);CHKERRQ(ierr);
1808     }
1809   }
1810   /* Do integration for each field */
1811   for (f = 0; f < Nf; ++f) {
1812     PetscObject  obj;
1813     PetscClassId id;
1814     PetscInt     numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;
1815 
1816     ierr = PetscDSGetDiscretization(prob, f, &obj);CHKERRQ(ierr);
1817     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
1818     if (id == PETSCFE_CLASSID) {
1819       PetscFE         fe = (PetscFE) obj;
1820       PetscQuadrature q;
1821       PetscFEGeom     *chunkGeom = NULL;
1822       PetscInt        Nq, Nb;
1823 
1824       ierr = PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches);CHKERRQ(ierr);
1825       ierr = PetscFEGetQuadrature(fe, &q);CHKERRQ(ierr);
1826       ierr = PetscQuadratureGetData(q, NULL, NULL, &Nq, NULL, NULL);CHKERRQ(ierr);
1827       ierr = PetscFEGetDimension(fe, &Nb);CHKERRQ(ierr);
1828       blockSize = Nb*Nq;
1829       batchSize = numBlocks * blockSize;
1830       ierr = PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches);CHKERRQ(ierr);
1831       numChunks = numCells / (numBatches*batchSize);
1832       Ne        = numChunks*numBatches*batchSize;
1833       Nr        = numCells % (numBatches*batchSize);
1834       offset    = numCells - Nr;
1835       if (!affineQuad) {
1836         ierr = DMFieldCreateFEGeom(coordField,cellIS,q,PETSC_FALSE,&cgeomFEM);CHKERRQ(ierr);
1837       }
1838       ierr = PetscFEGeomGetChunk(cgeomFEM,0,offset,&chunkGeom);CHKERRQ(ierr);
1839       ierr = PetscFEIntegrate(prob, f, Ne, chunkGeom, u, probAux, a, cintegral);CHKERRQ(ierr);
1840       ierr = PetscFEGeomGetChunk(cgeomFEM,offset,numCells,&chunkGeom);CHKERRQ(ierr);
1841       ierr = PetscFEIntegrate(prob, f, Nr, chunkGeom, &u[offset*totDim], probAux, &a[offset*totDimAux], &cintegral[offset*Nf]);CHKERRQ(ierr);
1842       ierr = PetscFEGeomRestoreChunk(cgeomFEM,offset,numCells,&chunkGeom);CHKERRQ(ierr);
1843       if (!affineQuad) {
1844         ierr = PetscFEGeomDestroy(&cgeomFEM);CHKERRQ(ierr);
1845       }
1846     } else if (id == PETSCFV_CLASSID) {
1847       PetscInt       foff;
1848       PetscPointFunc obj_func;
1849       PetscScalar    lint;
1850 
1851       ierr = PetscDSGetObjective(prob, f, &obj_func);CHKERRQ(ierr);
1852       ierr = PetscDSGetFieldOffset(prob, f, &foff);CHKERRQ(ierr);
1853       if (obj_func) {
1854         for (c = 0; c < numCells; ++c) {
1855           PetscScalar *u_x;
1856 
1857           ierr = DMPlexPointLocalRead(dmGrad, c, lgrad, &u_x);CHKERRQ(ierr);
1858           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);
1859           cintegral[c*Nf+f] += PetscRealPart(lint)*cgeomFVM[c].volume;
1860         }
1861       }
1862     } else SETERRQ1(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", f);
1863   }
1864   /* Cleanup data arrays */
1865   if (useFVM) {
1866     ierr = VecRestoreArrayRead(locGrad, &lgrad);CHKERRQ(ierr);
1867     ierr = VecRestoreArrayRead(cellGeometryFVM, (const PetscScalar **) &cgeomFVM);CHKERRQ(ierr);
1868     ierr = DMRestoreLocalVector(dmGrad, &locGrad);CHKERRQ(ierr);
1869     ierr = VecDestroy(&faceGeometryFVM);CHKERRQ(ierr);
1870     ierr = VecDestroy(&cellGeometryFVM);CHKERRQ(ierr);
1871     ierr = DMDestroy(&dmGrad);CHKERRQ(ierr);
1872   }
1873   if (dmAux) {ierr = PetscFree(a);CHKERRQ(ierr);}
1874   ierr = PetscFree(u);CHKERRQ(ierr);
1875   /* Cleanup */
1876   if (affineQuad) {
1877     ierr = PetscFEGeomDestroy(&cgeomFEM);CHKERRQ(ierr);
1878   }
1879   ierr = PetscQuadratureDestroy(&affineQuad);CHKERRQ(ierr);
1880   ierr = ISDestroy(&cellIS);CHKERRQ(ierr);
1881   ierr = DMRestoreLocalVector(dm, &locX);CHKERRQ(ierr);
1882   PetscFunctionReturn(0);
1883 }
1884 
1885 /*@
1886   DMPlexComputeIntegralFEM - Form the integral over the domain from the global input X using pointwise functions specified by the user
1887 
1888   Input Parameters:
1889 + dm - The mesh
1890 . X  - Global input vector
1891 - user - The user context
1892 
1893   Output Parameter:
1894 . integral - Integral for each field
1895 
1896   Level: developer
1897 
1898 .seealso: DMPlexComputeResidualFEM()
1899 @*/
1900 PetscErrorCode DMPlexComputeIntegralFEM(DM dm, Vec X, PetscScalar *integral, void *user)
1901 {
1902   DM_Plex       *mesh = (DM_Plex *) dm->data;
1903   PetscScalar   *cintegral, *lintegral;
1904   PetscInt       Nf, f, cellHeight, cStart, cEnd, cEndInterior[4], cell;
1905   PetscErrorCode ierr;
1906 
1907   PetscFunctionBegin;
1908   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1909   PetscValidHeaderSpecific(X, VEC_CLASSID, 2);
1910   PetscValidPointer(integral, 3);
1911   ierr = PetscLogEventBegin(DMPLEX_IntegralFEM,dm,0,0,0);CHKERRQ(ierr);
1912   ierr = DMGetNumFields(dm, &Nf);CHKERRQ(ierr);
1913   ierr = DMPlexGetVTKCellHeight(dm, &cellHeight);CHKERRQ(ierr);
1914   ierr = DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd);CHKERRQ(ierr);
1915   ierr = DMPlexGetHybridBounds(dm, &cEndInterior[0], &cEndInterior[1], &cEndInterior[2], &cEndInterior[3]);CHKERRQ(ierr);
1916   cEnd = cEndInterior[cellHeight] < 0 ? cEnd : cEndInterior[cellHeight];
1917   /* TODO Introduce a loop over large chunks (right now this is a single chunk) */
1918   ierr = PetscCalloc2(Nf, &lintegral, (cEnd-cStart)*Nf, &cintegral);CHKERRQ(ierr);
1919   ierr = DMPlexComputeIntegral_Internal(dm, X, cStart, cEnd, cintegral, user);CHKERRQ(ierr);
1920   /* Sum up values */
1921   for (cell = cStart; cell < cEnd; ++cell) {
1922     const PetscInt c = cell - cStart;
1923 
1924     if (mesh->printFEM > 1) {ierr = DMPrintCellVector(cell, "Cell Integral", Nf, &cintegral[c*Nf]);CHKERRQ(ierr);}
1925     for (f = 0; f < Nf; ++f) lintegral[f] += cintegral[c*Nf+f];
1926   }
1927   ierr = MPIU_Allreduce(lintegral, integral, Nf, MPIU_SCALAR, MPIU_SUM, PetscObjectComm((PetscObject) dm));CHKERRQ(ierr);
1928   if (mesh->printFEM) {
1929     ierr = PetscPrintf(PetscObjectComm((PetscObject) dm), "Integral:");CHKERRQ(ierr);
1930     for (f = 0; f < Nf; ++f) {ierr = PetscPrintf(PetscObjectComm((PetscObject) dm), " %g", (double) PetscRealPart(integral[f]));CHKERRQ(ierr);}
1931     ierr = PetscPrintf(PetscObjectComm((PetscObject) dm), "\n");CHKERRQ(ierr);
1932   }
1933   ierr = PetscFree2(lintegral, cintegral);CHKERRQ(ierr);
1934   ierr = PetscLogEventEnd(DMPLEX_IntegralFEM,dm,0,0,0);CHKERRQ(ierr);
1935   PetscFunctionReturn(0);
1936 }
1937 
1938 /*@
1939   DMPlexComputeCellwiseIntegralFEM - Form the vector of cellwise integrals F from the global input X using pointwise functions specified by the user
1940 
1941   Input Parameters:
1942 + dm - The mesh
1943 . X  - Global input vector
1944 - user - The user context
1945 
1946   Output Parameter:
1947 . integral - Cellwise integrals for each field
1948 
1949   Level: developer
1950 
1951 .seealso: DMPlexComputeResidualFEM()
1952 @*/
1953 PetscErrorCode DMPlexComputeCellwiseIntegralFEM(DM dm, Vec X, Vec F, void *user)
1954 {
1955   DM_Plex       *mesh = (DM_Plex *) dm->data;
1956   DM             dmF;
1957   PetscSection   sectionF;
1958   PetscScalar   *cintegral, *af;
1959   PetscInt       Nf, f, cellHeight, cStart, cEnd, cEndInterior[4], cell;
1960   PetscErrorCode ierr;
1961 
1962   PetscFunctionBegin;
1963   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1964   PetscValidHeaderSpecific(X, VEC_CLASSID, 2);
1965   PetscValidHeaderSpecific(F, VEC_CLASSID, 3);
1966   ierr = PetscLogEventBegin(DMPLEX_IntegralFEM,dm,0,0,0);CHKERRQ(ierr);
1967   ierr = DMGetNumFields(dm, &Nf);CHKERRQ(ierr);
1968   ierr = DMPlexGetVTKCellHeight(dm, &cellHeight);CHKERRQ(ierr);
1969   ierr = DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd);CHKERRQ(ierr);
1970   ierr = DMPlexGetHybridBounds(dm, &cEndInterior[0], &cEndInterior[1], &cEndInterior[2], &cEndInterior[3]);CHKERRQ(ierr);
1971   cEnd = cEndInterior[cellHeight] < 0 ? cEnd : cEndInterior[cellHeight];
1972   /* TODO Introduce a loop over large chunks (right now this is a single chunk) */
1973   ierr = PetscCalloc1((cEnd-cStart)*Nf, &cintegral);CHKERRQ(ierr);
1974   ierr = DMPlexComputeIntegral_Internal(dm, X, cStart, cEnd, cintegral, user);CHKERRQ(ierr);
1975   /* Put values in F*/
1976   ierr = VecGetDM(F, &dmF);CHKERRQ(ierr);
1977   ierr = DMGetLocalSection(dmF, &sectionF);CHKERRQ(ierr);
1978   ierr = VecGetArray(F, &af);CHKERRQ(ierr);
1979   for (cell = cStart; cell < cEnd; ++cell) {
1980     const PetscInt c = cell - cStart;
1981     PetscInt       dof, off;
1982 
1983     if (mesh->printFEM > 1) {ierr = DMPrintCellVector(cell, "Cell Integral", Nf, &cintegral[c*Nf]);CHKERRQ(ierr);}
1984     ierr = PetscSectionGetDof(sectionF, cell, &dof);CHKERRQ(ierr);
1985     ierr = PetscSectionGetOffset(sectionF, cell, &off);CHKERRQ(ierr);
1986     if (dof != Nf) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "The number of cell dofs %D != %D", dof, Nf);
1987     for (f = 0; f < Nf; ++f) af[off+f] = cintegral[c*Nf+f];
1988   }
1989   ierr = VecRestoreArray(F, &af);CHKERRQ(ierr);
1990   ierr = PetscFree(cintegral);CHKERRQ(ierr);
1991   ierr = PetscLogEventEnd(DMPLEX_IntegralFEM,dm,0,0,0);CHKERRQ(ierr);
1992   PetscFunctionReturn(0);
1993 }
1994 
1995 static PetscErrorCode DMPlexComputeBdIntegral_Internal(DM dm, Vec locX, IS pointIS,
1996                                                        void (*func)(PetscInt, PetscInt, PetscInt,
1997                                                                     const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[],
1998                                                                     const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[],
1999                                                                     PetscReal, const PetscReal[], const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]),
2000                                                        PetscScalar *fintegral, void *user)
2001 {
2002   DM                 plex = NULL, plexA = NULL;
2003   DMEnclosureType    encAux;
2004   PetscDS            prob, probAux = NULL;
2005   PetscSection       section, sectionAux = NULL;
2006   Vec                locA = NULL;
2007   DMField            coordField;
2008   PetscInt           Nf,        totDim,        *uOff, *uOff_x;
2009   PetscInt           NfAux = 0, totDimAux = 0, *aOff = NULL;
2010   PetscScalar       *u, *a = NULL;
2011   const PetscScalar *constants;
2012   PetscInt           numConstants, f;
2013   PetscErrorCode     ierr;
2014 
2015   PetscFunctionBegin;
2016   ierr = DMGetCoordinateField(dm, &coordField);CHKERRQ(ierr);
2017   ierr = DMConvert(dm, DMPLEX, &plex);CHKERRQ(ierr);
2018   ierr = DMGetDS(dm, &prob);CHKERRQ(ierr);
2019   ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);
2020   ierr = PetscSectionGetNumFields(section, &Nf);CHKERRQ(ierr);
2021   /* Determine which discretizations we have */
2022   for (f = 0; f < Nf; ++f) {
2023     PetscObject  obj;
2024     PetscClassId id;
2025 
2026     ierr = PetscDSGetDiscretization(prob, f, &obj);CHKERRQ(ierr);
2027     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
2028     if (id == PETSCFV_CLASSID) SETERRQ1(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Not supported for FVM (field %D)", f);
2029   }
2030   /* Read DS information */
2031   ierr = PetscDSGetTotalDimension(prob, &totDim);CHKERRQ(ierr);
2032   ierr = PetscDSGetComponentOffsets(prob, &uOff);CHKERRQ(ierr);
2033   ierr = PetscDSGetComponentDerivativeOffsets(prob, &uOff_x);CHKERRQ(ierr);
2034   ierr = PetscDSGetConstants(prob, &numConstants, &constants);CHKERRQ(ierr);
2035   /* Read Auxiliary DS information */
2036   ierr = PetscObjectQuery((PetscObject) dm, "A", (PetscObject *) &locA);CHKERRQ(ierr);
2037   if (locA) {
2038     DM dmAux;
2039 
2040     ierr = VecGetDM(locA, &dmAux);CHKERRQ(ierr);
2041     ierr = DMGetEnclosureRelation(dmAux, dm, &encAux);CHKERRQ(ierr);
2042     ierr = DMConvert(dmAux, DMPLEX, &plexA);CHKERRQ(ierr);
2043     ierr = DMGetDS(dmAux, &probAux);CHKERRQ(ierr);
2044     ierr = PetscDSGetNumFields(probAux, &NfAux);CHKERRQ(ierr);
2045     ierr = DMGetLocalSection(dmAux, &sectionAux);CHKERRQ(ierr);
2046     ierr = PetscDSGetTotalDimension(probAux, &totDimAux);CHKERRQ(ierr);
2047     ierr = PetscDSGetComponentOffsets(probAux, &aOff);CHKERRQ(ierr);
2048   }
2049   /* Integrate over points */
2050   {
2051     PetscFEGeom    *fgeom, *chunkGeom = NULL;
2052     PetscInt        maxDegree;
2053     PetscQuadrature qGeom = NULL;
2054     const PetscInt *points;
2055     PetscInt        numFaces, face, Nq, field;
2056     PetscInt        numChunks, chunkSize, chunk, Nr, offset;
2057 
2058     ierr = ISGetLocalSize(pointIS, &numFaces);CHKERRQ(ierr);
2059     ierr = ISGetIndices(pointIS, &points);CHKERRQ(ierr);
2060     ierr = PetscCalloc2(numFaces*totDim, &u, locA ? numFaces*totDimAux : 0, &a);CHKERRQ(ierr);
2061     ierr = DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree);CHKERRQ(ierr);
2062     for (field = 0; field < Nf; ++field) {
2063       PetscFE fe;
2064 
2065       ierr = PetscDSGetDiscretization(prob, field, (PetscObject *) &fe);CHKERRQ(ierr);
2066       if (maxDegree <= 1) {ierr = DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom);CHKERRQ(ierr);}
2067       if (!qGeom) {
2068         ierr = PetscFEGetFaceQuadrature(fe, &qGeom);CHKERRQ(ierr);
2069         ierr = PetscObjectReference((PetscObject) qGeom);CHKERRQ(ierr);
2070       }
2071       ierr = PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL);CHKERRQ(ierr);
2072       ierr = DMPlexGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom);CHKERRQ(ierr);
2073       for (face = 0; face < numFaces; ++face) {
2074         const PetscInt point = points[face], *support, *cone;
2075         PetscScalar    *x    = NULL;
2076         PetscInt       i, coneSize, faceLoc;
2077 
2078         ierr = DMPlexGetSupport(dm, point, &support);CHKERRQ(ierr);
2079         ierr = DMPlexGetConeSize(dm, support[0], &coneSize);CHKERRQ(ierr);
2080         ierr = DMPlexGetCone(dm, support[0], &cone);CHKERRQ(ierr);
2081         for (faceLoc = 0; faceLoc < coneSize; ++faceLoc) if (cone[faceLoc] == point) break;
2082         if (faceLoc == coneSize) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find face %D in cone of support[0] %D", face, support[0]);
2083         fgeom->face[face][0] = faceLoc;
2084         ierr = DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x);CHKERRQ(ierr);
2085         for (i = 0; i < totDim; ++i) u[face*totDim+i] = x[i];
2086         ierr = DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x);CHKERRQ(ierr);
2087         if (locA) {
2088           PetscInt subp;
2089           ierr = DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp);CHKERRQ(ierr);
2090           ierr = DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x);CHKERRQ(ierr);
2091           for (i = 0; i < totDimAux; ++i) a[f*totDimAux+i] = x[i];
2092           ierr = DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x);CHKERRQ(ierr);
2093         }
2094       }
2095       /* Get blocking */
2096       {
2097         PetscQuadrature q;
2098         PetscInt        numBatches, batchSize, numBlocks, blockSize;
2099         PetscInt        Nq, Nb;
2100 
2101         ierr = PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches);CHKERRQ(ierr);
2102         ierr = PetscFEGetQuadrature(fe, &q);CHKERRQ(ierr);
2103         ierr = PetscQuadratureGetData(q, NULL, NULL, &Nq, NULL, NULL);CHKERRQ(ierr);
2104         ierr = PetscFEGetDimension(fe, &Nb);CHKERRQ(ierr);
2105         blockSize = Nb*Nq;
2106         batchSize = numBlocks * blockSize;
2107         chunkSize = numBatches*batchSize;
2108         ierr = PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches);CHKERRQ(ierr);
2109         numChunks = numFaces / chunkSize;
2110         Nr        = numFaces % chunkSize;
2111         offset    = numFaces - Nr;
2112       }
2113       /* Do integration for each field */
2114       for (chunk = 0; chunk < numChunks; ++chunk) {
2115         ierr = PetscFEGeomGetChunk(fgeom, chunk*chunkSize, (chunk+1)*chunkSize, &chunkGeom);CHKERRQ(ierr);
2116         ierr = PetscFEIntegrateBd(prob, field, func, chunkSize, chunkGeom, u, probAux, a, fintegral);CHKERRQ(ierr);
2117         ierr = PetscFEGeomRestoreChunk(fgeom, 0, offset, &chunkGeom);CHKERRQ(ierr);
2118       }
2119       ierr = PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom);CHKERRQ(ierr);
2120       ierr = PetscFEIntegrateBd(prob, field, func, Nr, chunkGeom, &u[offset*totDim], probAux, a ? &a[offset*totDimAux] : NULL, &fintegral[offset*Nf]);CHKERRQ(ierr);
2121       ierr = PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom);CHKERRQ(ierr);
2122       /* Cleanup data arrays */
2123       ierr = DMPlexRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom);CHKERRQ(ierr);
2124       ierr = PetscQuadratureDestroy(&qGeom);CHKERRQ(ierr);
2125       ierr = PetscFree2(u, a);CHKERRQ(ierr);
2126       ierr = ISRestoreIndices(pointIS, &points);CHKERRQ(ierr);
2127     }
2128   }
2129   if (plex)  {ierr = DMDestroy(&plex);CHKERRQ(ierr);}
2130   if (plexA) {ierr = DMDestroy(&plexA);CHKERRQ(ierr);}
2131   PetscFunctionReturn(0);
2132 }
2133 
2134 /*@
2135   DMPlexComputeBdIntegral - Form the integral over the specified boundary from the global input X using pointwise functions specified by the user
2136 
2137   Input Parameters:
2138 + dm      - The mesh
2139 . X       - Global input vector
2140 . label   - The boundary DMLabel
2141 . numVals - The number of label values to use, or PETSC_DETERMINE for all values
2142 . vals    - The label values to use, or PETSC_NULL for all values
2143 . func    = The function to integrate along the boundary
2144 - user    - The user context
2145 
2146   Output Parameter:
2147 . integral - Integral for each field
2148 
2149   Level: developer
2150 
2151 .seealso: DMPlexComputeIntegralFEM(), DMPlexComputeBdResidualFEM()
2152 @*/
2153 PetscErrorCode DMPlexComputeBdIntegral(DM dm, Vec X, DMLabel label, PetscInt numVals, const PetscInt vals[],
2154                                        void (*func)(PetscInt, PetscInt, PetscInt,
2155                                                     const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[],
2156                                                     const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[],
2157                                                     PetscReal, const PetscReal[], const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]),
2158                                        PetscScalar *integral, void *user)
2159 {
2160   Vec            locX;
2161   PetscSection   section;
2162   DMLabel        depthLabel;
2163   IS             facetIS;
2164   PetscInt       dim, Nf, f, v;
2165   PetscErrorCode ierr;
2166 
2167   PetscFunctionBegin;
2168   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2169   PetscValidHeaderSpecific(X, VEC_CLASSID, 2);
2170   PetscValidPointer(label, 3);
2171   if (vals) PetscValidPointer(vals, 5);
2172   PetscValidPointer(integral, 6);
2173   ierr = PetscLogEventBegin(DMPLEX_IntegralFEM,dm,0,0,0);CHKERRQ(ierr);
2174   ierr = DMPlexGetDepthLabel(dm, &depthLabel);CHKERRQ(ierr);
2175   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
2176   ierr = DMLabelGetStratumIS(depthLabel, dim-1, &facetIS);CHKERRQ(ierr);
2177   ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);
2178   ierr = PetscSectionGetNumFields(section, &Nf);CHKERRQ(ierr);
2179   /* Get local solution with boundary values */
2180   ierr = DMGetLocalVector(dm, &locX);CHKERRQ(ierr);
2181   ierr = DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL);CHKERRQ(ierr);
2182   ierr = DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX);CHKERRQ(ierr);
2183   ierr = DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX);CHKERRQ(ierr);
2184   /* Loop over label values */
2185   ierr = PetscArrayzero(integral, Nf);CHKERRQ(ierr);
2186   for (v = 0; v < numVals; ++v) {
2187     IS           pointIS;
2188     PetscInt     numFaces, face;
2189     PetscScalar *fintegral;
2190 
2191     ierr = DMLabelGetStratumIS(label, vals[v], &pointIS);CHKERRQ(ierr);
2192     if (!pointIS) continue; /* No points with that id on this process */
2193     {
2194       IS isectIS;
2195 
2196       /* TODO: Special cases of ISIntersect where it is quick to check a priori if one is a superset of the other */
2197       ierr = ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS);CHKERRQ(ierr);
2198       ierr = ISDestroy(&pointIS);CHKERRQ(ierr);
2199       pointIS = isectIS;
2200     }
2201     ierr = ISGetLocalSize(pointIS, &numFaces);CHKERRQ(ierr);
2202     ierr = PetscCalloc1(numFaces*Nf, &fintegral);CHKERRQ(ierr);
2203     ierr = DMPlexComputeBdIntegral_Internal(dm, locX, pointIS, func, fintegral, user);CHKERRQ(ierr);
2204     /* Sum point contributions into integral */
2205     for (f = 0; f < Nf; ++f) for (face = 0; face < numFaces; ++face) integral[f] += fintegral[face*Nf+f];
2206     ierr = PetscFree(fintegral);CHKERRQ(ierr);
2207     ierr = ISDestroy(&pointIS);CHKERRQ(ierr);
2208   }
2209   ierr = DMRestoreLocalVector(dm, &locX);CHKERRQ(ierr);
2210   ierr = ISDestroy(&facetIS);CHKERRQ(ierr);
2211   ierr = PetscLogEventEnd(DMPLEX_IntegralFEM,dm,0,0,0);CHKERRQ(ierr);
2212   PetscFunctionReturn(0);
2213 }
2214 
2215 /*@
2216   DMPlexComputeInterpolatorNested - Form the local portion of the interpolation matrix I from the coarse DM to the uniformly refined DM.
2217 
2218   Input Parameters:
2219 + dmf  - The fine mesh
2220 . dmc  - The coarse mesh
2221 - user - The user context
2222 
2223   Output Parameter:
2224 . In  - The interpolation matrix
2225 
2226   Level: developer
2227 
2228 .seealso: DMPlexComputeInterpolatorGeneral(), DMPlexComputeJacobianFEM()
2229 @*/
2230 PetscErrorCode DMPlexComputeInterpolatorNested(DM dmc, DM dmf, Mat In, void *user)
2231 {
2232   DM_Plex          *mesh  = (DM_Plex *) dmc->data;
2233   const char       *name  = "Interpolator";
2234   PetscDS           cds, rds;
2235   PetscFE          *feRef;
2236   PetscFV          *fvRef;
2237   PetscSection      fsection, fglobalSection;
2238   PetscSection      csection, cglobalSection;
2239   PetscScalar      *elemMat;
2240   PetscInt          dim, Nf, f, fieldI, fieldJ, offsetI, offsetJ, cStart, cEnd, c;
2241   PetscInt          cTotDim, rTotDim = 0;
2242   PetscErrorCode    ierr;
2243 
2244   PetscFunctionBegin;
2245   ierr = PetscLogEventBegin(DMPLEX_InterpolatorFEM,dmc,dmf,0,0);CHKERRQ(ierr);
2246   ierr = DMGetDimension(dmf, &dim);CHKERRQ(ierr);
2247   ierr = DMGetLocalSection(dmf, &fsection);CHKERRQ(ierr);
2248   ierr = DMGetGlobalSection(dmf, &fglobalSection);CHKERRQ(ierr);
2249   ierr = DMGetLocalSection(dmc, &csection);CHKERRQ(ierr);
2250   ierr = DMGetGlobalSection(dmc, &cglobalSection);CHKERRQ(ierr);
2251   ierr = PetscSectionGetNumFields(fsection, &Nf);CHKERRQ(ierr);
2252   ierr = DMPlexGetInteriorCellStratum(dmc, &cStart, &cEnd);CHKERRQ(ierr);
2253   ierr = DMGetDS(dmc, &cds);CHKERRQ(ierr);
2254   ierr = DMGetDS(dmf, &rds);CHKERRQ(ierr);
2255   ierr = PetscCalloc2(Nf, &feRef, Nf, &fvRef);CHKERRQ(ierr);
2256   for (f = 0; f < Nf; ++f) {
2257     PetscObject  obj;
2258     PetscClassId id;
2259     PetscInt     rNb = 0, Nc = 0;
2260 
2261     ierr = PetscDSGetDiscretization(rds, f, &obj);CHKERRQ(ierr);
2262     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
2263     if (id == PETSCFE_CLASSID) {
2264       PetscFE fe = (PetscFE) obj;
2265 
2266       ierr = PetscFERefine(fe, &feRef[f]);CHKERRQ(ierr);
2267       ierr = PetscFEGetDimension(feRef[f], &rNb);CHKERRQ(ierr);
2268       ierr = PetscFEGetNumComponents(fe, &Nc);CHKERRQ(ierr);
2269     } else if (id == PETSCFV_CLASSID) {
2270       PetscFV        fv = (PetscFV) obj;
2271       PetscDualSpace Q;
2272 
2273       ierr = PetscFVRefine(fv, &fvRef[f]);CHKERRQ(ierr);
2274       ierr = PetscFVGetDualSpace(fvRef[f], &Q);CHKERRQ(ierr);
2275       ierr = PetscDualSpaceGetDimension(Q, &rNb);CHKERRQ(ierr);
2276       ierr = PetscFVGetNumComponents(fv, &Nc);CHKERRQ(ierr);
2277     }
2278     rTotDim += rNb;
2279   }
2280   ierr = PetscDSGetTotalDimension(cds, &cTotDim);CHKERRQ(ierr);
2281   ierr = PetscMalloc1(rTotDim*cTotDim,&elemMat);CHKERRQ(ierr);
2282   ierr = PetscArrayzero(elemMat, rTotDim*cTotDim);CHKERRQ(ierr);
2283   for (fieldI = 0, offsetI = 0; fieldI < Nf; ++fieldI) {
2284     PetscDualSpace   Qref;
2285     PetscQuadrature  f;
2286     const PetscReal *qpoints, *qweights;
2287     PetscReal       *points;
2288     PetscInt         npoints = 0, Nc, Np, fpdim, i, k, p, d;
2289 
2290     /* Compose points from all dual basis functionals */
2291     if (feRef[fieldI]) {
2292       ierr = PetscFEGetDualSpace(feRef[fieldI], &Qref);CHKERRQ(ierr);
2293       ierr = PetscFEGetNumComponents(feRef[fieldI], &Nc);CHKERRQ(ierr);
2294     } else {
2295       ierr = PetscFVGetDualSpace(fvRef[fieldI], &Qref);CHKERRQ(ierr);
2296       ierr = PetscFVGetNumComponents(fvRef[fieldI], &Nc);CHKERRQ(ierr);
2297     }
2298     ierr = PetscDualSpaceGetDimension(Qref, &fpdim);CHKERRQ(ierr);
2299     for (i = 0; i < fpdim; ++i) {
2300       ierr = PetscDualSpaceGetFunctional(Qref, i, &f);CHKERRQ(ierr);
2301       ierr = PetscQuadratureGetData(f, NULL, NULL, &Np, NULL, NULL);CHKERRQ(ierr);
2302       npoints += Np;
2303     }
2304     ierr = PetscMalloc1(npoints*dim,&points);CHKERRQ(ierr);
2305     for (i = 0, k = 0; i < fpdim; ++i) {
2306       ierr = PetscDualSpaceGetFunctional(Qref, i, &f);CHKERRQ(ierr);
2307       ierr = PetscQuadratureGetData(f, NULL, NULL, &Np, &qpoints, NULL);CHKERRQ(ierr);
2308       for (p = 0; p < Np; ++p, ++k) for (d = 0; d < dim; ++d) points[k*dim+d] = qpoints[p*dim+d];
2309     }
2310 
2311     for (fieldJ = 0, offsetJ = 0; fieldJ < Nf; ++fieldJ) {
2312       PetscObject  obj;
2313       PetscClassId id;
2314       PetscInt     NcJ = 0, cpdim = 0, j, qNc;
2315 
2316       ierr = PetscDSGetDiscretization(cds, fieldJ, &obj);CHKERRQ(ierr);
2317       ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
2318       if (id == PETSCFE_CLASSID) {
2319         PetscFE           fe = (PetscFE) obj;
2320         PetscTabulation T  = NULL;
2321 
2322         /* Evaluate basis at points */
2323         ierr = PetscFEGetNumComponents(fe, &NcJ);CHKERRQ(ierr);
2324         ierr = PetscFEGetDimension(fe, &cpdim);CHKERRQ(ierr);
2325         /* For now, fields only interpolate themselves */
2326         if (fieldI == fieldJ) {
2327           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);
2328           ierr = PetscFECreateTabulation(fe, 1, npoints, points, 0, &T);CHKERRQ(ierr);
2329           for (i = 0, k = 0; i < fpdim; ++i) {
2330             ierr = PetscDualSpaceGetFunctional(Qref, i, &f);CHKERRQ(ierr);
2331             ierr = PetscQuadratureGetData(f, NULL, &qNc, &Np, NULL, &qweights);CHKERRQ(ierr);
2332             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);
2333             for (p = 0; p < Np; ++p, ++k) {
2334               for (j = 0; j < cpdim; ++j) {
2335                 /*
2336                    cTotDim:            Total columns in element interpolation matrix, sum of number of dual basis functionals in each field
2337                    offsetI, offsetJ:   Offsets into the larger element interpolation matrix for different fields
2338                    fpdim, i, cpdim, j: Dofs for fine and coarse grids, correspond to dual space basis functionals
2339                    qNC, Nc, Ncj, c:    Number of components in this field
2340                    Np, p:              Number of quad points in the fine grid functional i
2341                    k:                  i*Np + p, overall point number for the interpolation
2342                 */
2343                 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];
2344               }
2345             }
2346           }
2347           ierr = PetscTabulationDestroy(&T);CHKERRQ(ierr);CHKERRQ(ierr);
2348         }
2349       } else if (id == PETSCFV_CLASSID) {
2350         PetscFV        fv = (PetscFV) obj;
2351 
2352         /* Evaluate constant function at points */
2353         ierr = PetscFVGetNumComponents(fv, &NcJ);CHKERRQ(ierr);
2354         cpdim = 1;
2355         /* For now, fields only interpolate themselves */
2356         if (fieldI == fieldJ) {
2357           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);
2358           for (i = 0, k = 0; i < fpdim; ++i) {
2359             ierr = PetscDualSpaceGetFunctional(Qref, i, &f);CHKERRQ(ierr);
2360             ierr = PetscQuadratureGetData(f, NULL, &qNc, &Np, NULL, &qweights);CHKERRQ(ierr);
2361             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);
2362             for (p = 0; p < Np; ++p, ++k) {
2363               for (j = 0; j < cpdim; ++j) {
2364                 for (c = 0; c < Nc; ++c) elemMat[(offsetI + i)*cTotDim + offsetJ + j] += 1.0*qweights[p*qNc+c];
2365               }
2366             }
2367           }
2368         }
2369       }
2370       offsetJ += cpdim;
2371     }
2372     offsetI += fpdim;
2373     ierr = PetscFree(points);CHKERRQ(ierr);
2374   }
2375   if (mesh->printFEM > 1) {ierr = DMPrintCellMatrix(0, name, rTotDim, cTotDim, elemMat);CHKERRQ(ierr);}
2376   /* Preallocate matrix */
2377   {
2378     Mat          preallocator;
2379     PetscScalar *vals;
2380     PetscInt    *cellCIndices, *cellFIndices;
2381     PetscInt     locRows, locCols, cell;
2382 
2383     ierr = MatGetLocalSize(In, &locRows, &locCols);CHKERRQ(ierr);
2384     ierr = MatCreate(PetscObjectComm((PetscObject) In), &preallocator);CHKERRQ(ierr);
2385     ierr = MatSetType(preallocator, MATPREALLOCATOR);CHKERRQ(ierr);
2386     ierr = MatSetSizes(preallocator, locRows, locCols, PETSC_DETERMINE, PETSC_DETERMINE);CHKERRQ(ierr);
2387     ierr = MatSetUp(preallocator);CHKERRQ(ierr);
2388     ierr = PetscCalloc3(rTotDim*cTotDim, &vals,cTotDim,&cellCIndices,rTotDim,&cellFIndices);CHKERRQ(ierr);
2389     for (cell = cStart; cell < cEnd; ++cell) {
2390       ierr = DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, cell, cellCIndices, cellFIndices);CHKERRQ(ierr);
2391       ierr = MatSetValues(preallocator, rTotDim, cellFIndices, cTotDim, cellCIndices, vals, INSERT_VALUES);CHKERRQ(ierr);
2392     }
2393     ierr = PetscFree3(vals,cellCIndices,cellFIndices);CHKERRQ(ierr);
2394     ierr = MatAssemblyBegin(preallocator, MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
2395     ierr = MatAssemblyEnd(preallocator, MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
2396     ierr = MatPreallocatorPreallocate(preallocator, PETSC_TRUE, In);CHKERRQ(ierr);
2397     ierr = MatDestroy(&preallocator);CHKERRQ(ierr);
2398   }
2399   /* Fill matrix */
2400   ierr = MatZeroEntries(In);CHKERRQ(ierr);
2401   for (c = cStart; c < cEnd; ++c) {
2402     ierr = DMPlexMatSetClosureRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, In, c, elemMat, INSERT_VALUES);CHKERRQ(ierr);
2403   }
2404   for (f = 0; f < Nf; ++f) {ierr = PetscFEDestroy(&feRef[f]);CHKERRQ(ierr);}
2405   ierr = PetscFree2(feRef,fvRef);CHKERRQ(ierr);
2406   ierr = PetscFree(elemMat);CHKERRQ(ierr);
2407   ierr = MatAssemblyBegin(In, MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
2408   ierr = MatAssemblyEnd(In, MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
2409   if (mesh->printFEM) {
2410     ierr = PetscPrintf(PetscObjectComm((PetscObject)In), "%s:\n", name);CHKERRQ(ierr);
2411     ierr = MatChop(In, 1.0e-10);CHKERRQ(ierr);
2412     ierr = MatView(In, NULL);CHKERRQ(ierr);
2413   }
2414   ierr = PetscLogEventEnd(DMPLEX_InterpolatorFEM,dmc,dmf,0,0);CHKERRQ(ierr);
2415   PetscFunctionReturn(0);
2416 }
2417 
2418 PetscErrorCode DMPlexComputeMassMatrixNested(DM dmc, DM dmf, Mat mass, void *user)
2419 {
2420   SETERRQ(PetscObjectComm((PetscObject) dmc), PETSC_ERR_SUP, "Laziness");
2421 }
2422 
2423 /*@
2424   DMPlexComputeInterpolatorGeneral - Form the local portion of the interpolation matrix I from the coarse DM to a non-nested fine DM.
2425 
2426   Input Parameters:
2427 + dmf  - The fine mesh
2428 . dmc  - The coarse mesh
2429 - user - The user context
2430 
2431   Output Parameter:
2432 . In  - The interpolation matrix
2433 
2434   Level: developer
2435 
2436 .seealso: DMPlexComputeInterpolatorNested(), DMPlexComputeJacobianFEM()
2437 @*/
2438 PetscErrorCode DMPlexComputeInterpolatorGeneral(DM dmc, DM dmf, Mat In, void *user)
2439 {
2440   DM_Plex       *mesh = (DM_Plex *) dmf->data;
2441   const char    *name = "Interpolator";
2442   PetscDS        prob;
2443   PetscSection   fsection, csection, globalFSection, globalCSection;
2444   PetscHSetIJ    ht;
2445   PetscLayout    rLayout;
2446   PetscInt      *dnz, *onz;
2447   PetscInt       locRows, rStart, rEnd;
2448   PetscReal     *x, *v0, *J, *invJ, detJ;
2449   PetscReal     *v0c, *Jc, *invJc, detJc;
2450   PetscScalar   *elemMat;
2451   PetscInt       dim, Nf, field, totDim, cStart, cEnd, cell, ccell;
2452   PetscErrorCode ierr;
2453 
2454   PetscFunctionBegin;
2455   ierr = PetscLogEventBegin(DMPLEX_InterpolatorFEM,dmc,dmf,0,0);CHKERRQ(ierr);
2456   ierr = DMGetCoordinateDim(dmc, &dim);CHKERRQ(ierr);
2457   ierr = DMGetDS(dmc, &prob);CHKERRQ(ierr);
2458   ierr = PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL);CHKERRQ(ierr);
2459   ierr = PetscDSGetNumFields(prob, &Nf);CHKERRQ(ierr);
2460   ierr = PetscMalloc3(dim,&v0,dim*dim,&J,dim*dim,&invJ);CHKERRQ(ierr);
2461   ierr = PetscMalloc3(dim,&v0c,dim*dim,&Jc,dim*dim,&invJc);CHKERRQ(ierr);
2462   ierr = DMGetLocalSection(dmf, &fsection);CHKERRQ(ierr);
2463   ierr = DMGetGlobalSection(dmf, &globalFSection);CHKERRQ(ierr);
2464   ierr = DMGetLocalSection(dmc, &csection);CHKERRQ(ierr);
2465   ierr = DMGetGlobalSection(dmc, &globalCSection);CHKERRQ(ierr);
2466   ierr = DMPlexGetHeightStratum(dmf, 0, &cStart, &cEnd);CHKERRQ(ierr);
2467   ierr = PetscDSGetTotalDimension(prob, &totDim);CHKERRQ(ierr);
2468   ierr = PetscMalloc1(totDim, &elemMat);CHKERRQ(ierr);
2469 
2470   ierr = MatGetLocalSize(In, &locRows, NULL);CHKERRQ(ierr);
2471   ierr = PetscLayoutCreate(PetscObjectComm((PetscObject) In), &rLayout);CHKERRQ(ierr);
2472   ierr = PetscLayoutSetLocalSize(rLayout, locRows);CHKERRQ(ierr);
2473   ierr = PetscLayoutSetBlockSize(rLayout, 1);CHKERRQ(ierr);
2474   ierr = PetscLayoutSetUp(rLayout);CHKERRQ(ierr);
2475   ierr = PetscLayoutGetRange(rLayout, &rStart, &rEnd);CHKERRQ(ierr);
2476   ierr = PetscLayoutDestroy(&rLayout);CHKERRQ(ierr);
2477   ierr = PetscCalloc2(locRows,&dnz,locRows,&onz);CHKERRQ(ierr);
2478   ierr = PetscHSetIJCreate(&ht);CHKERRQ(ierr);
2479   for (field = 0; field < Nf; ++field) {
2480     PetscObject      obj;
2481     PetscClassId     id;
2482     PetscDualSpace   Q = NULL;
2483     PetscQuadrature  f;
2484     const PetscReal *qpoints;
2485     PetscInt         Nc, Np, fpdim, i, d;
2486 
2487     ierr = PetscDSGetDiscretization(prob, field, &obj);CHKERRQ(ierr);
2488     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
2489     if (id == PETSCFE_CLASSID) {
2490       PetscFE fe = (PetscFE) obj;
2491 
2492       ierr = PetscFEGetDualSpace(fe, &Q);CHKERRQ(ierr);
2493       ierr = PetscFEGetNumComponents(fe, &Nc);CHKERRQ(ierr);
2494     } else if (id == PETSCFV_CLASSID) {
2495       PetscFV fv = (PetscFV) obj;
2496 
2497       ierr = PetscFVGetDualSpace(fv, &Q);CHKERRQ(ierr);
2498       Nc   = 1;
2499     }
2500     ierr = PetscDualSpaceGetDimension(Q, &fpdim);CHKERRQ(ierr);
2501     /* For each fine grid cell */
2502     for (cell = cStart; cell < cEnd; ++cell) {
2503       PetscInt *findices,   *cindices;
2504       PetscInt  numFIndices, numCIndices;
2505 
2506       ierr = DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, &numFIndices, &findices, NULL);CHKERRQ(ierr);
2507       ierr = DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ);CHKERRQ(ierr);
2508       if (numFIndices != fpdim) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fine indices %D != %D dual basis vecs", numFIndices, fpdim);
2509       for (i = 0; i < fpdim; ++i) {
2510         Vec             pointVec;
2511         PetscScalar    *pV;
2512         PetscSF         coarseCellSF = NULL;
2513         const PetscSFNode *coarseCells;
2514         PetscInt        numCoarseCells, q, c;
2515 
2516         /* Get points from the dual basis functional quadrature */
2517         ierr = PetscDualSpaceGetFunctional(Q, i, &f);CHKERRQ(ierr);
2518         ierr = PetscQuadratureGetData(f, NULL, NULL, &Np, &qpoints, NULL);CHKERRQ(ierr);
2519         ierr = VecCreateSeq(PETSC_COMM_SELF, Np*dim, &pointVec);CHKERRQ(ierr);
2520         ierr = VecSetBlockSize(pointVec, dim);CHKERRQ(ierr);
2521         ierr = VecGetArray(pointVec, &pV);CHKERRQ(ierr);
2522         for (q = 0; q < Np; ++q) {
2523           const PetscReal xi0[3] = {-1., -1., -1.};
2524 
2525           /* Transform point to real space */
2526           CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q*dim], x);
2527           for (d = 0; d < dim; ++d) pV[q*dim+d] = x[d];
2528         }
2529         ierr = VecRestoreArray(pointVec, &pV);CHKERRQ(ierr);
2530         /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
2531         /* OPT: Pack all quad points from fine cell */
2532         ierr = DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF);CHKERRQ(ierr);
2533         ierr = PetscSFViewFromOptions(coarseCellSF, NULL, "-interp_sf_view");CHKERRQ(ierr);
2534         /* Update preallocation info */
2535         ierr = PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells);CHKERRQ(ierr);
2536         if (numCoarseCells != Np) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_PLIB,"Not all closure points located");
2537         {
2538           PetscHashIJKey key;
2539           PetscBool      missing;
2540 
2541           key.i = findices[i];
2542           if (key.i >= 0) {
2543             /* Get indices for coarse elements */
2544             for (ccell = 0; ccell < numCoarseCells; ++ccell) {
2545               ierr = DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, &numCIndices, &cindices, NULL);CHKERRQ(ierr);
2546               for (c = 0; c < numCIndices; ++c) {
2547                 key.j = cindices[c];
2548                 if (key.j < 0) continue;
2549                 ierr = PetscHSetIJQueryAdd(ht, key, &missing);CHKERRQ(ierr);
2550                 if (missing) {
2551                   if ((key.j >= rStart) && (key.j < rEnd)) ++dnz[key.i-rStart];
2552                   else                                     ++onz[key.i-rStart];
2553                 }
2554               }
2555               ierr = DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, &numCIndices, &cindices, NULL);CHKERRQ(ierr);
2556             }
2557           }
2558         }
2559         ierr = PetscSFDestroy(&coarseCellSF);CHKERRQ(ierr);
2560         ierr = VecDestroy(&pointVec);CHKERRQ(ierr);
2561       }
2562       ierr = DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, &numFIndices, &findices, NULL);CHKERRQ(ierr);
2563     }
2564   }
2565   ierr = PetscHSetIJDestroy(&ht);CHKERRQ(ierr);
2566   ierr = MatXAIJSetPreallocation(In, 1, dnz, onz, NULL, NULL);CHKERRQ(ierr);
2567   ierr = MatSetOption(In, MAT_NEW_NONZERO_ALLOCATION_ERR,PETSC_TRUE);CHKERRQ(ierr);
2568   ierr = PetscFree2(dnz,onz);CHKERRQ(ierr);
2569   for (field = 0; field < Nf; ++field) {
2570     PetscObject       obj;
2571     PetscClassId      id;
2572     PetscDualSpace    Q = NULL;
2573     PetscTabulation T = NULL;
2574     PetscQuadrature   f;
2575     const PetscReal  *qpoints, *qweights;
2576     PetscInt          Nc, qNc, Np, fpdim, i, d;
2577 
2578     ierr = PetscDSGetDiscretization(prob, field, &obj);CHKERRQ(ierr);
2579     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
2580     if (id == PETSCFE_CLASSID) {
2581       PetscFE fe = (PetscFE) obj;
2582 
2583       ierr = PetscFEGetDualSpace(fe, &Q);CHKERRQ(ierr);
2584       ierr = PetscFEGetNumComponents(fe, &Nc);CHKERRQ(ierr);
2585       ierr = PetscFECreateTabulation(fe, 1, 1, x, 0, &T);CHKERRQ(ierr);
2586     } else if (id == PETSCFV_CLASSID) {
2587       PetscFV fv = (PetscFV) obj;
2588 
2589       ierr = PetscFVGetDualSpace(fv, &Q);CHKERRQ(ierr);
2590       Nc   = 1;
2591     } else SETERRQ1(PetscObjectComm((PetscObject)dmc),PETSC_ERR_ARG_WRONG,"Unknown discretization type for field %D",field);
2592     ierr = PetscDualSpaceGetDimension(Q, &fpdim);CHKERRQ(ierr);
2593     /* For each fine grid cell */
2594     for (cell = cStart; cell < cEnd; ++cell) {
2595       PetscInt *findices,   *cindices;
2596       PetscInt  numFIndices, numCIndices;
2597 
2598       ierr = DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, &numFIndices, &findices, NULL);CHKERRQ(ierr);
2599       ierr = DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ);CHKERRQ(ierr);
2600       if (numFIndices != fpdim) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fine indices %D != %D dual basis vecs", numFIndices, fpdim);
2601       for (i = 0; i < fpdim; ++i) {
2602         Vec             pointVec;
2603         PetscScalar    *pV;
2604         PetscSF         coarseCellSF = NULL;
2605         const PetscSFNode *coarseCells;
2606         PetscInt        numCoarseCells, cpdim, q, c, j;
2607 
2608         /* Get points from the dual basis functional quadrature */
2609         ierr = PetscDualSpaceGetFunctional(Q, i, &f);CHKERRQ(ierr);
2610         ierr = PetscQuadratureGetData(f, NULL, &qNc, &Np, &qpoints, &qweights);CHKERRQ(ierr);
2611         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);
2612         ierr = VecCreateSeq(PETSC_COMM_SELF, Np*dim, &pointVec);CHKERRQ(ierr);
2613         ierr = VecSetBlockSize(pointVec, dim);CHKERRQ(ierr);
2614         ierr = VecGetArray(pointVec, &pV);CHKERRQ(ierr);
2615         for (q = 0; q < Np; ++q) {
2616           const PetscReal xi0[3] = {-1., -1., -1.};
2617 
2618           /* Transform point to real space */
2619           CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q*dim], x);
2620           for (d = 0; d < dim; ++d) pV[q*dim+d] = x[d];
2621         }
2622         ierr = VecRestoreArray(pointVec, &pV);CHKERRQ(ierr);
2623         /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
2624         /* OPT: Read this out from preallocation information */
2625         ierr = DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF);CHKERRQ(ierr);
2626         /* Update preallocation info */
2627         ierr = PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells);CHKERRQ(ierr);
2628         if (numCoarseCells != Np) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_PLIB,"Not all closure points located");
2629         ierr = VecGetArray(pointVec, &pV);CHKERRQ(ierr);
2630         for (ccell = 0; ccell < numCoarseCells; ++ccell) {
2631           PetscReal pVReal[3];
2632           const PetscReal xi0[3] = {-1., -1., -1.};
2633 
2634           ierr = DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, &numCIndices, &cindices, NULL);CHKERRQ(ierr);
2635           /* Transform points from real space to coarse reference space */
2636           ierr = DMPlexComputeCellGeometryFEM(dmc, coarseCells[ccell].index, NULL, v0c, Jc, invJc, &detJc);CHKERRQ(ierr);
2637           for (d = 0; d < dim; ++d) pVReal[d] = PetscRealPart(pV[ccell*dim+d]);
2638           CoordinatesRealToRef(dim, dim, xi0, v0c, invJc, pVReal, x);
2639 
2640           if (id == PETSCFE_CLASSID) {
2641             PetscFE fe = (PetscFE) obj;
2642 
2643             /* Evaluate coarse basis on contained point */
2644             ierr = PetscFEGetDimension(fe, &cpdim);CHKERRQ(ierr);
2645             ierr = PetscFEComputeTabulation(fe, 1, x, 0, T);CHKERRQ(ierr);
2646             ierr = PetscArrayzero(elemMat, cpdim);CHKERRQ(ierr);
2647             /* Get elemMat entries by multiplying by weight */
2648             for (j = 0; j < cpdim; ++j) {
2649               for (c = 0; c < Nc; ++c) elemMat[j] += T->T[0][j*Nc + c]*qweights[ccell*qNc + c];
2650             }
2651           } else {
2652             cpdim = 1;
2653             for (j = 0; j < cpdim; ++j) {
2654               for (c = 0; c < Nc; ++c) elemMat[j] += 1.0*qweights[ccell*qNc + c];
2655             }
2656           }
2657           /* Update interpolator */
2658           if (mesh->printFEM > 1) {ierr = DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat);CHKERRQ(ierr);}
2659           if (numCIndices != cpdim) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %D != %D", numCIndices, cpdim);
2660           ierr = MatSetValues(In, 1, &findices[i], numCIndices, cindices, elemMat, INSERT_VALUES);CHKERRQ(ierr);
2661           ierr = DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, &numCIndices, &cindices, NULL);CHKERRQ(ierr);
2662         }
2663         ierr = VecRestoreArray(pointVec, &pV);CHKERRQ(ierr);
2664         ierr = PetscSFDestroy(&coarseCellSF);CHKERRQ(ierr);
2665         ierr = VecDestroy(&pointVec);CHKERRQ(ierr);
2666       }
2667       ierr = DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, &numFIndices, &findices, NULL);CHKERRQ(ierr);
2668     }
2669     if (id == PETSCFE_CLASSID) {ierr = PetscTabulationDestroy(&T);CHKERRQ(ierr);}
2670   }
2671   ierr = PetscFree3(v0,J,invJ);CHKERRQ(ierr);
2672   ierr = PetscFree3(v0c,Jc,invJc);CHKERRQ(ierr);
2673   ierr = PetscFree(elemMat);CHKERRQ(ierr);
2674   ierr = MatAssemblyBegin(In, MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
2675   ierr = MatAssemblyEnd(In, MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
2676   ierr = PetscLogEventEnd(DMPLEX_InterpolatorFEM,dmc,dmf,0,0);CHKERRQ(ierr);
2677   PetscFunctionReturn(0);
2678 }
2679 
2680 /*@
2681   DMPlexComputeMassMatrixGeneral - Form the local portion of the mass matrix M from the coarse DM to a non-nested fine DM.
2682 
2683   Input Parameters:
2684 + dmf  - The fine mesh
2685 . dmc  - The coarse mesh
2686 - user - The user context
2687 
2688   Output Parameter:
2689 . mass  - The mass matrix
2690 
2691   Level: developer
2692 
2693 .seealso: DMPlexComputeMassMatrixNested(), DMPlexComputeInterpolatorNested(), DMPlexComputeInterpolatorGeneral(), DMPlexComputeJacobianFEM()
2694 @*/
2695 PetscErrorCode DMPlexComputeMassMatrixGeneral(DM dmc, DM dmf, Mat mass, void *user)
2696 {
2697   DM_Plex       *mesh = (DM_Plex *) dmf->data;
2698   const char    *name = "Mass Matrix";
2699   PetscDS        prob;
2700   PetscSection   fsection, csection, globalFSection, globalCSection;
2701   PetscHSetIJ    ht;
2702   PetscLayout    rLayout;
2703   PetscInt      *dnz, *onz;
2704   PetscInt       locRows, rStart, rEnd;
2705   PetscReal     *x, *v0, *J, *invJ, detJ;
2706   PetscReal     *v0c, *Jc, *invJc, detJc;
2707   PetscScalar   *elemMat;
2708   PetscInt       dim, Nf, field, totDim, cStart, cEnd, cell, ccell;
2709   PetscErrorCode ierr;
2710 
2711   PetscFunctionBegin;
2712   ierr = DMGetCoordinateDim(dmc, &dim);CHKERRQ(ierr);
2713   ierr = DMGetDS(dmc, &prob);CHKERRQ(ierr);
2714   ierr = PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL);CHKERRQ(ierr);
2715   ierr = PetscDSGetNumFields(prob, &Nf);CHKERRQ(ierr);
2716   ierr = PetscMalloc3(dim,&v0,dim*dim,&J,dim*dim,&invJ);CHKERRQ(ierr);
2717   ierr = PetscMalloc3(dim,&v0c,dim*dim,&Jc,dim*dim,&invJc);CHKERRQ(ierr);
2718   ierr = DMGetLocalSection(dmf, &fsection);CHKERRQ(ierr);
2719   ierr = DMGetGlobalSection(dmf, &globalFSection);CHKERRQ(ierr);
2720   ierr = DMGetLocalSection(dmc, &csection);CHKERRQ(ierr);
2721   ierr = DMGetGlobalSection(dmc, &globalCSection);CHKERRQ(ierr);
2722   ierr = DMPlexGetHeightStratum(dmf, 0, &cStart, &cEnd);CHKERRQ(ierr);
2723   ierr = PetscDSGetTotalDimension(prob, &totDim);CHKERRQ(ierr);
2724   ierr = PetscMalloc1(totDim, &elemMat);CHKERRQ(ierr);
2725 
2726   ierr = MatGetLocalSize(mass, &locRows, NULL);CHKERRQ(ierr);
2727   ierr = PetscLayoutCreate(PetscObjectComm((PetscObject) mass), &rLayout);CHKERRQ(ierr);
2728   ierr = PetscLayoutSetLocalSize(rLayout, locRows);CHKERRQ(ierr);
2729   ierr = PetscLayoutSetBlockSize(rLayout, 1);CHKERRQ(ierr);
2730   ierr = PetscLayoutSetUp(rLayout);CHKERRQ(ierr);
2731   ierr = PetscLayoutGetRange(rLayout, &rStart, &rEnd);CHKERRQ(ierr);
2732   ierr = PetscLayoutDestroy(&rLayout);CHKERRQ(ierr);
2733   ierr = PetscCalloc2(locRows,&dnz,locRows,&onz);CHKERRQ(ierr);
2734   ierr = PetscHSetIJCreate(&ht);CHKERRQ(ierr);
2735   for (field = 0; field < Nf; ++field) {
2736     PetscObject      obj;
2737     PetscClassId     id;
2738     PetscQuadrature  quad;
2739     const PetscReal *qpoints;
2740     PetscInt         Nq, Nc, i, d;
2741 
2742     ierr = PetscDSGetDiscretization(prob, field, &obj);CHKERRQ(ierr);
2743     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
2744     if (id == PETSCFE_CLASSID) {ierr = PetscFEGetQuadrature((PetscFE) obj, &quad);CHKERRQ(ierr);}
2745     else                       {ierr = PetscFVGetQuadrature((PetscFV) obj, &quad);CHKERRQ(ierr);}
2746     ierr = PetscQuadratureGetData(quad, NULL, &Nc, &Nq, &qpoints, NULL);CHKERRQ(ierr);
2747     /* For each fine grid cell */
2748     for (cell = cStart; cell < cEnd; ++cell) {
2749       Vec                pointVec;
2750       PetscScalar       *pV;
2751       PetscSF            coarseCellSF = NULL;
2752       const PetscSFNode *coarseCells;
2753       PetscInt           numCoarseCells, q, c;
2754       PetscInt          *findices,   *cindices;
2755       PetscInt           numFIndices, numCIndices;
2756 
2757       ierr = DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, &numFIndices, &findices, NULL);CHKERRQ(ierr);
2758       ierr = DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ);CHKERRQ(ierr);
2759       /* Get points from the quadrature */
2760       ierr = VecCreateSeq(PETSC_COMM_SELF, Nq*dim, &pointVec);CHKERRQ(ierr);
2761       ierr = VecSetBlockSize(pointVec, dim);CHKERRQ(ierr);
2762       ierr = VecGetArray(pointVec, &pV);CHKERRQ(ierr);
2763       for (q = 0; q < Nq; ++q) {
2764         const PetscReal xi0[3] = {-1., -1., -1.};
2765 
2766         /* Transform point to real space */
2767         CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q*dim], x);
2768         for (d = 0; d < dim; ++d) pV[q*dim+d] = x[d];
2769       }
2770       ierr = VecRestoreArray(pointVec, &pV);CHKERRQ(ierr);
2771       /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
2772       ierr = DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF);CHKERRQ(ierr);
2773       ierr = PetscSFViewFromOptions(coarseCellSF, NULL, "-interp_sf_view");CHKERRQ(ierr);
2774       /* Update preallocation info */
2775       ierr = PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells);CHKERRQ(ierr);
2776       if (numCoarseCells != Nq) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_PLIB,"Not all closure points located");
2777       {
2778         PetscHashIJKey key;
2779         PetscBool      missing;
2780 
2781         for (i = 0; i < numFIndices; ++i) {
2782           key.i = findices[i];
2783           if (key.i >= 0) {
2784             /* Get indices for coarse elements */
2785             for (ccell = 0; ccell < numCoarseCells; ++ccell) {
2786               ierr = DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, &numCIndices, &cindices, NULL);CHKERRQ(ierr);
2787               for (c = 0; c < numCIndices; ++c) {
2788                 key.j = cindices[c];
2789                 if (key.j < 0) continue;
2790                 ierr = PetscHSetIJQueryAdd(ht, key, &missing);CHKERRQ(ierr);
2791                 if (missing) {
2792                   if ((key.j >= rStart) && (key.j < rEnd)) ++dnz[key.i-rStart];
2793                   else                                     ++onz[key.i-rStart];
2794                 }
2795               }
2796               ierr = DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, &numCIndices, &cindices, NULL);CHKERRQ(ierr);
2797             }
2798           }
2799         }
2800       }
2801       ierr = PetscSFDestroy(&coarseCellSF);CHKERRQ(ierr);
2802       ierr = VecDestroy(&pointVec);CHKERRQ(ierr);
2803       ierr = DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, &numFIndices, &findices, NULL);CHKERRQ(ierr);
2804     }
2805   }
2806   ierr = PetscHSetIJDestroy(&ht);CHKERRQ(ierr);
2807   ierr = MatXAIJSetPreallocation(mass, 1, dnz, onz, NULL, NULL);CHKERRQ(ierr);
2808   ierr = MatSetOption(mass, MAT_NEW_NONZERO_ALLOCATION_ERR,PETSC_TRUE);CHKERRQ(ierr);
2809   ierr = PetscFree2(dnz,onz);CHKERRQ(ierr);
2810   for (field = 0; field < Nf; ++field) {
2811     PetscObject       obj;
2812     PetscClassId      id;
2813     PetscTabulation T, Tfine;
2814     PetscQuadrature   quad;
2815     const PetscReal  *qpoints, *qweights;
2816     PetscInt          Nq, Nc, i, d;
2817 
2818     ierr = PetscDSGetDiscretization(prob, field, &obj);CHKERRQ(ierr);
2819     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
2820     if (id == PETSCFE_CLASSID) {
2821       ierr = PetscFEGetQuadrature((PetscFE) obj, &quad);CHKERRQ(ierr);
2822       ierr = PetscFEGetCellTabulation((PetscFE) obj, &Tfine);CHKERRQ(ierr);
2823       ierr = PetscFECreateTabulation((PetscFE) obj, 1, 1, x, 0, &T);CHKERRQ(ierr);
2824     } else {
2825       ierr = PetscFVGetQuadrature((PetscFV) obj, &quad);CHKERRQ(ierr);
2826     }
2827     ierr = PetscQuadratureGetData(quad, NULL, &Nc, &Nq, &qpoints, &qweights);CHKERRQ(ierr);
2828     /* For each fine grid cell */
2829     for (cell = cStart; cell < cEnd; ++cell) {
2830       Vec                pointVec;
2831       PetscScalar       *pV;
2832       PetscSF            coarseCellSF = NULL;
2833       const PetscSFNode *coarseCells;
2834       PetscInt           numCoarseCells, cpdim, q, c, j;
2835       PetscInt          *findices,   *cindices;
2836       PetscInt           numFIndices, numCIndices;
2837 
2838       ierr = DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, &numFIndices, &findices, NULL);CHKERRQ(ierr);
2839       ierr = DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ);CHKERRQ(ierr);
2840       /* Get points from the quadrature */
2841       ierr = VecCreateSeq(PETSC_COMM_SELF, Nq*dim, &pointVec);CHKERRQ(ierr);
2842       ierr = VecSetBlockSize(pointVec, dim);CHKERRQ(ierr);
2843       ierr = VecGetArray(pointVec, &pV);CHKERRQ(ierr);
2844       for (q = 0; q < Nq; ++q) {
2845         const PetscReal xi0[3] = {-1., -1., -1.};
2846 
2847         /* Transform point to real space */
2848         CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q*dim], x);
2849         for (d = 0; d < dim; ++d) pV[q*dim+d] = x[d];
2850       }
2851       ierr = VecRestoreArray(pointVec, &pV);CHKERRQ(ierr);
2852       /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
2853       ierr = DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF);CHKERRQ(ierr);
2854       /* Update matrix */
2855       ierr = PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells);CHKERRQ(ierr);
2856       if (numCoarseCells != Nq) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_PLIB,"Not all closure points located");
2857       ierr = VecGetArray(pointVec, &pV);CHKERRQ(ierr);
2858       for (ccell = 0; ccell < numCoarseCells; ++ccell) {
2859         PetscReal pVReal[3];
2860         const PetscReal xi0[3] = {-1., -1., -1.};
2861 
2862 
2863         ierr = DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, &numCIndices, &cindices, NULL);CHKERRQ(ierr);
2864         /* Transform points from real space to coarse reference space */
2865         ierr = DMPlexComputeCellGeometryFEM(dmc, coarseCells[ccell].index, NULL, v0c, Jc, invJc, &detJc);CHKERRQ(ierr);
2866         for (d = 0; d < dim; ++d) pVReal[d] = PetscRealPart(pV[ccell*dim+d]);
2867         CoordinatesRealToRef(dim, dim, xi0, v0c, invJc, pVReal, x);
2868 
2869         if (id == PETSCFE_CLASSID) {
2870           PetscFE fe = (PetscFE) obj;
2871 
2872           /* Evaluate coarse basis on contained point */
2873           ierr = PetscFEGetDimension(fe, &cpdim);CHKERRQ(ierr);
2874           ierr = PetscFEComputeTabulation(fe, 1, x, 0, T);CHKERRQ(ierr);
2875           /* Get elemMat entries by multiplying by weight */
2876           for (i = 0; i < numFIndices; ++i) {
2877             ierr = PetscArrayzero(elemMat, cpdim);CHKERRQ(ierr);
2878             for (j = 0; j < cpdim; ++j) {
2879               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;
2880             }
2881             /* Update interpolator */
2882             if (mesh->printFEM > 1) {ierr = DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat);CHKERRQ(ierr);}
2883             if (numCIndices != cpdim) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %D != %D", numCIndices, cpdim);
2884             ierr = MatSetValues(mass, 1, &findices[i], numCIndices, cindices, elemMat, ADD_VALUES);CHKERRQ(ierr);
2885           }
2886         } else {
2887           cpdim = 1;
2888           for (i = 0; i < numFIndices; ++i) {
2889             ierr = PetscArrayzero(elemMat, cpdim);CHKERRQ(ierr);
2890             for (j = 0; j < cpdim; ++j) {
2891               for (c = 0; c < Nc; ++c) elemMat[j] += 1.0*1.0*qweights[ccell*Nc + c]*detJ;
2892             }
2893             /* Update interpolator */
2894             if (mesh->printFEM > 1) {ierr = DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat);CHKERRQ(ierr);}
2895             ierr = PetscPrintf(PETSC_COMM_SELF, "Nq: %D %D Nf: %D %D Nc: %D %D\n", ccell, Nq, i, numFIndices, j, numCIndices);CHKERRQ(ierr);
2896             if (numCIndices != cpdim) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %D != %D", numCIndices, cpdim);
2897             ierr = MatSetValues(mass, 1, &findices[i], numCIndices, cindices, elemMat, ADD_VALUES);CHKERRQ(ierr);
2898           }
2899         }
2900         ierr = DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, &numCIndices, &cindices, NULL);CHKERRQ(ierr);
2901       }
2902       ierr = VecRestoreArray(pointVec, &pV);CHKERRQ(ierr);
2903       ierr = PetscSFDestroy(&coarseCellSF);CHKERRQ(ierr);
2904       ierr = VecDestroy(&pointVec);CHKERRQ(ierr);
2905       ierr = DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, &numFIndices, &findices, NULL);CHKERRQ(ierr);
2906     }
2907     if (id == PETSCFE_CLASSID) {ierr = PetscTabulationDestroy(&T);CHKERRQ(ierr);}
2908   }
2909   ierr = PetscFree3(v0,J,invJ);CHKERRQ(ierr);
2910   ierr = PetscFree3(v0c,Jc,invJc);CHKERRQ(ierr);
2911   ierr = PetscFree(elemMat);CHKERRQ(ierr);
2912   ierr = MatAssemblyBegin(mass, MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
2913   ierr = MatAssemblyEnd(mass, MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
2914   PetscFunctionReturn(0);
2915 }
2916 
2917 /*@
2918   DMPlexComputeInjectorFEM - Compute a mapping from coarse unknowns to fine unknowns
2919 
2920   Input Parameters:
2921 + dmc  - The coarse mesh
2922 - dmf  - The fine mesh
2923 - user - The user context
2924 
2925   Output Parameter:
2926 . sc   - The mapping
2927 
2928   Level: developer
2929 
2930 .seealso: DMPlexComputeInterpolatorNested(), DMPlexComputeJacobianFEM()
2931 @*/
2932 PetscErrorCode DMPlexComputeInjectorFEM(DM dmc, DM dmf, VecScatter *sc, void *user)
2933 {
2934   PetscDS        prob;
2935   PetscFE       *feRef;
2936   PetscFV       *fvRef;
2937   Vec            fv, cv;
2938   IS             fis, cis;
2939   PetscSection   fsection, fglobalSection, csection, cglobalSection;
2940   PetscInt      *cmap, *cellCIndices, *cellFIndices, *cindices, *findices;
2941   PetscInt       cTotDim, fTotDim = 0, Nf, f, field, cStart, cEnd, c, dim, d, startC, endC, offsetC, offsetF, m;
2942   PetscBool     *needAvg;
2943   PetscErrorCode ierr;
2944 
2945   PetscFunctionBegin;
2946   ierr = PetscLogEventBegin(DMPLEX_InjectorFEM,dmc,dmf,0,0);CHKERRQ(ierr);
2947   ierr = DMGetDimension(dmf, &dim);CHKERRQ(ierr);
2948   ierr = DMGetLocalSection(dmf, &fsection);CHKERRQ(ierr);
2949   ierr = DMGetGlobalSection(dmf, &fglobalSection);CHKERRQ(ierr);
2950   ierr = DMGetLocalSection(dmc, &csection);CHKERRQ(ierr);
2951   ierr = DMGetGlobalSection(dmc, &cglobalSection);CHKERRQ(ierr);
2952   ierr = PetscSectionGetNumFields(fsection, &Nf);CHKERRQ(ierr);
2953   ierr = DMPlexGetInteriorCellStratum(dmc, &cStart, &cEnd);CHKERRQ(ierr);
2954   ierr = DMGetDS(dmc, &prob);CHKERRQ(ierr);
2955   ierr = PetscCalloc3(Nf,&feRef,Nf,&fvRef,Nf,&needAvg);CHKERRQ(ierr);
2956   for (f = 0; f < Nf; ++f) {
2957     PetscObject  obj;
2958     PetscClassId id;
2959     PetscInt     fNb = 0, Nc = 0;
2960 
2961     ierr = PetscDSGetDiscretization(prob, f, &obj);CHKERRQ(ierr);
2962     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
2963     if (id == PETSCFE_CLASSID) {
2964       PetscFE    fe = (PetscFE) obj;
2965       PetscSpace sp;
2966       PetscInt   maxDegree;
2967 
2968       ierr = PetscFERefine(fe, &feRef[f]);CHKERRQ(ierr);
2969       ierr = PetscFEGetDimension(feRef[f], &fNb);CHKERRQ(ierr);
2970       ierr = PetscFEGetNumComponents(fe, &Nc);CHKERRQ(ierr);
2971       ierr = PetscFEGetBasisSpace(fe, &sp);CHKERRQ(ierr);
2972       ierr = PetscSpaceGetDegree(sp, NULL, &maxDegree);CHKERRQ(ierr);
2973       if (!maxDegree) needAvg[f] = PETSC_TRUE;
2974     } else if (id == PETSCFV_CLASSID) {
2975       PetscFV        fv = (PetscFV) obj;
2976       PetscDualSpace Q;
2977 
2978       ierr = PetscFVRefine(fv, &fvRef[f]);CHKERRQ(ierr);
2979       ierr = PetscFVGetDualSpace(fvRef[f], &Q);CHKERRQ(ierr);
2980       ierr = PetscDualSpaceGetDimension(Q, &fNb);CHKERRQ(ierr);
2981       ierr = PetscFVGetNumComponents(fv, &Nc);CHKERRQ(ierr);
2982       needAvg[f] = PETSC_TRUE;
2983     }
2984     fTotDim += fNb;
2985   }
2986   ierr = PetscDSGetTotalDimension(prob, &cTotDim);CHKERRQ(ierr);
2987   ierr = PetscMalloc1(cTotDim,&cmap);CHKERRQ(ierr);
2988   for (field = 0, offsetC = 0, offsetF = 0; field < Nf; ++field) {
2989     PetscFE        feC;
2990     PetscFV        fvC;
2991     PetscDualSpace QF, QC;
2992     PetscInt       order = -1, NcF, NcC, fpdim, cpdim;
2993 
2994     if (feRef[field]) {
2995       ierr = PetscDSGetDiscretization(prob, field, (PetscObject *) &feC);CHKERRQ(ierr);
2996       ierr = PetscFEGetNumComponents(feC, &NcC);CHKERRQ(ierr);
2997       ierr = PetscFEGetNumComponents(feRef[field], &NcF);CHKERRQ(ierr);
2998       ierr = PetscFEGetDualSpace(feRef[field], &QF);CHKERRQ(ierr);
2999       ierr = PetscDualSpaceGetOrder(QF, &order);CHKERRQ(ierr);
3000       ierr = PetscDualSpaceGetDimension(QF, &fpdim);CHKERRQ(ierr);
3001       ierr = PetscFEGetDualSpace(feC, &QC);CHKERRQ(ierr);
3002       ierr = PetscDualSpaceGetDimension(QC, &cpdim);CHKERRQ(ierr);
3003     } else {
3004       ierr = PetscDSGetDiscretization(prob, field, (PetscObject *) &fvC);CHKERRQ(ierr);
3005       ierr = PetscFVGetNumComponents(fvC, &NcC);CHKERRQ(ierr);
3006       ierr = PetscFVGetNumComponents(fvRef[field], &NcF);CHKERRQ(ierr);
3007       ierr = PetscFVGetDualSpace(fvRef[field], &QF);CHKERRQ(ierr);
3008       ierr = PetscDualSpaceGetDimension(QF, &fpdim);CHKERRQ(ierr);
3009       ierr = PetscFVGetDualSpace(fvC, &QC);CHKERRQ(ierr);
3010       ierr = PetscDualSpaceGetDimension(QC, &cpdim);CHKERRQ(ierr);
3011     }
3012     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);
3013     for (c = 0; c < cpdim; ++c) {
3014       PetscQuadrature  cfunc;
3015       const PetscReal *cqpoints, *cqweights;
3016       PetscInt         NqcC, NpC;
3017       PetscBool        found = PETSC_FALSE;
3018 
3019       ierr = PetscDualSpaceGetFunctional(QC, c, &cfunc);CHKERRQ(ierr);
3020       ierr = PetscQuadratureGetData(cfunc, NULL, &NqcC, &NpC, &cqpoints, &cqweights);CHKERRQ(ierr);
3021       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);
3022       if (NpC != 1 && feRef[field]) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Do not know how to do injection for moments");
3023       for (f = 0; f < fpdim; ++f) {
3024         PetscQuadrature  ffunc;
3025         const PetscReal *fqpoints, *fqweights;
3026         PetscReal        sum = 0.0;
3027         PetscInt         NqcF, NpF;
3028 
3029         ierr = PetscDualSpaceGetFunctional(QF, f, &ffunc);CHKERRQ(ierr);
3030         ierr = PetscQuadratureGetData(ffunc, NULL, &NqcF, &NpF, &fqpoints, &fqweights);CHKERRQ(ierr);
3031         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);
3032         if (NpC != NpF) continue;
3033         for (d = 0; d < dim; ++d) sum += PetscAbsReal(cqpoints[d] - fqpoints[d]);
3034         if (sum > 1.0e-9) continue;
3035         for (d = 0; d < NcC; ++d) sum += PetscAbsReal(cqweights[d]*fqweights[d]);
3036         if (sum < 1.0e-9) continue;
3037         cmap[offsetC+c] = offsetF+f;
3038         found = PETSC_TRUE;
3039         break;
3040       }
3041       if (!found) {
3042         /* TODO We really want the average here, but some asshole put VecScatter in the interface */
3043         if (fvRef[field] || (feRef[field] && order == 0)) {
3044           cmap[offsetC+c] = offsetF+0;
3045         } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Could not locate matching functional for injection");
3046       }
3047     }
3048     offsetC += cpdim;
3049     offsetF += fpdim;
3050   }
3051   for (f = 0; f < Nf; ++f) {ierr = PetscFEDestroy(&feRef[f]);CHKERRQ(ierr);ierr = PetscFVDestroy(&fvRef[f]);CHKERRQ(ierr);}
3052   ierr = PetscFree3(feRef,fvRef,needAvg);CHKERRQ(ierr);
3053 
3054   ierr = DMGetGlobalVector(dmf, &fv);CHKERRQ(ierr);
3055   ierr = DMGetGlobalVector(dmc, &cv);CHKERRQ(ierr);
3056   ierr = VecGetOwnershipRange(cv, &startC, &endC);CHKERRQ(ierr);
3057   ierr = PetscSectionGetConstrainedStorageSize(cglobalSection, &m);CHKERRQ(ierr);
3058   ierr = PetscMalloc2(cTotDim,&cellCIndices,fTotDim,&cellFIndices);CHKERRQ(ierr);
3059   ierr = PetscMalloc1(m,&cindices);CHKERRQ(ierr);
3060   ierr = PetscMalloc1(m,&findices);CHKERRQ(ierr);
3061   for (d = 0; d < m; ++d) cindices[d] = findices[d] = -1;
3062   for (c = cStart; c < cEnd; ++c) {
3063     ierr = DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, c, cellCIndices, cellFIndices);CHKERRQ(ierr);
3064     for (d = 0; d < cTotDim; ++d) {
3065       if ((cellCIndices[d] < startC) || (cellCIndices[d] >= endC)) continue;
3066       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]]);
3067       cindices[cellCIndices[d]-startC] = cellCIndices[d];
3068       findices[cellCIndices[d]-startC] = cellFIndices[cmap[d]];
3069     }
3070   }
3071   ierr = PetscFree(cmap);CHKERRQ(ierr);
3072   ierr = PetscFree2(cellCIndices,cellFIndices);CHKERRQ(ierr);
3073 
3074   ierr = ISCreateGeneral(PETSC_COMM_SELF, m, cindices, PETSC_OWN_POINTER, &cis);CHKERRQ(ierr);
3075   ierr = ISCreateGeneral(PETSC_COMM_SELF, m, findices, PETSC_OWN_POINTER, &fis);CHKERRQ(ierr);
3076   ierr = VecScatterCreate(cv, cis, fv, fis, sc);CHKERRQ(ierr);
3077   ierr = ISDestroy(&cis);CHKERRQ(ierr);
3078   ierr = ISDestroy(&fis);CHKERRQ(ierr);
3079   ierr = DMRestoreGlobalVector(dmf, &fv);CHKERRQ(ierr);
3080   ierr = DMRestoreGlobalVector(dmc, &cv);CHKERRQ(ierr);
3081   ierr = PetscLogEventEnd(DMPLEX_InjectorFEM,dmc,dmf,0,0);CHKERRQ(ierr);
3082   PetscFunctionReturn(0);
3083 }
3084 
3085 /*@C
3086   DMPlexGetCellFields - Retrieve the field values values for a chunk of cells
3087 
3088   Input Parameters:
3089 + dm     - The DM
3090 . cellIS - The cells to include
3091 . locX   - A local vector with the solution fields
3092 . locX_t - A local vector with solution field time derivatives, or NULL
3093 - locA   - A local vector with auxiliary fields, or NULL
3094 
3095   Output Parameters:
3096 + u   - The field coefficients
3097 . u_t - The fields derivative coefficients
3098 - a   - The auxiliary field coefficients
3099 
3100   Level: developer
3101 
3102 .seealso: DMPlexGetFaceFields()
3103 @*/
3104 PetscErrorCode DMPlexGetCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
3105 {
3106   DM              plex, plexA = NULL;
3107   DMEnclosureType encAux;
3108   PetscSection    section, sectionAux;
3109   PetscDS         prob;
3110   const PetscInt *cells;
3111   PetscInt        cStart, cEnd, numCells, totDim, totDimAux, c;
3112   PetscErrorCode  ierr;
3113 
3114   PetscFunctionBegin;
3115   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3116   PetscValidHeaderSpecific(locX, VEC_CLASSID, 4);
3117   if (locX_t) {PetscValidHeaderSpecific(locX_t, VEC_CLASSID, 5);}
3118   if (locA)   {PetscValidHeaderSpecific(locA, VEC_CLASSID, 6);}
3119   PetscValidPointer(u, 7);
3120   PetscValidPointer(u_t, 8);
3121   PetscValidPointer(a, 9);
3122   ierr = DMPlexConvertPlex(dm, &plex, PETSC_FALSE);CHKERRQ(ierr);
3123   ierr = ISGetPointRange(cellIS, &cStart, &cEnd, &cells);CHKERRQ(ierr);
3124   ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);
3125   ierr = DMGetCellDS(dm, cStart, &prob);CHKERRQ(ierr);
3126   ierr = PetscDSGetTotalDimension(prob, &totDim);CHKERRQ(ierr);
3127   if (locA) {
3128     DM      dmAux;
3129     PetscDS probAux;
3130 
3131     ierr = VecGetDM(locA, &dmAux);CHKERRQ(ierr);
3132     ierr = DMGetEnclosureRelation(dmAux, dm, &encAux);CHKERRQ(ierr);
3133     ierr = DMPlexConvertPlex(dmAux, &plexA, PETSC_FALSE);CHKERRQ(ierr);
3134     ierr = DMGetLocalSection(dmAux, &sectionAux);CHKERRQ(ierr);
3135     ierr = DMGetDS(dmAux, &probAux);CHKERRQ(ierr);
3136     ierr = PetscDSGetTotalDimension(probAux, &totDimAux);CHKERRQ(ierr);
3137   }
3138   numCells = cEnd - cStart;
3139   ierr = DMGetWorkArray(dm, numCells*totDim, MPIU_SCALAR, u);CHKERRQ(ierr);
3140   if (locX_t) {ierr = DMGetWorkArray(dm, numCells*totDim, MPIU_SCALAR, u_t);CHKERRQ(ierr);} else {*u_t = NULL;}
3141   if (locA)   {ierr = DMGetWorkArray(dm, numCells*totDimAux, MPIU_SCALAR, a);CHKERRQ(ierr);} else {*a = NULL;}
3142   for (c = cStart; c < cEnd; ++c) {
3143     const PetscInt cell = cells ? cells[c] : c;
3144     const PetscInt cind = c - cStart;
3145     PetscScalar   *x = NULL, *x_t = NULL, *ul = *u, *ul_t = *u_t, *al = *a;
3146     PetscInt       i;
3147 
3148     ierr = DMPlexVecGetClosure(plex, section, locX, cell, NULL, &x);CHKERRQ(ierr);
3149     for (i = 0; i < totDim; ++i) ul[cind*totDim+i] = x[i];
3150     ierr = DMPlexVecRestoreClosure(plex, section, locX, cell, NULL, &x);CHKERRQ(ierr);
3151     if (locX_t) {
3152       ierr = DMPlexVecGetClosure(plex, section, locX_t, cell, NULL, &x_t);CHKERRQ(ierr);
3153       for (i = 0; i < totDim; ++i) ul_t[cind*totDim+i] = x_t[i];
3154       ierr = DMPlexVecRestoreClosure(plex, section, locX_t, cell, NULL, &x_t);CHKERRQ(ierr);
3155     }
3156     if (locA) {
3157       PetscInt subcell;
3158       ierr = DMGetEnclosurePoint(plexA, dm, encAux, cell, &subcell);CHKERRQ(ierr);
3159       ierr = DMPlexVecGetClosure(plexA, sectionAux, locA, subcell, NULL, &x);CHKERRQ(ierr);
3160       for (i = 0; i < totDimAux; ++i) al[cind*totDimAux+i] = x[i];
3161       ierr = DMPlexVecRestoreClosure(plexA, sectionAux, locA, subcell, NULL, &x);CHKERRQ(ierr);
3162     }
3163   }
3164   ierr = DMDestroy(&plex);CHKERRQ(ierr);
3165   if (locA) {ierr = DMDestroy(&plexA);CHKERRQ(ierr);}
3166   ierr = ISRestorePointRange(cellIS, &cStart, &cEnd, &cells);CHKERRQ(ierr);
3167   PetscFunctionReturn(0);
3168 }
3169 
3170 /*@C
3171   DMPlexRestoreCellFields - Restore the field values values for a chunk of cells
3172 
3173   Input Parameters:
3174 + dm     - The DM
3175 . cellIS - The cells to include
3176 . locX   - A local vector with the solution fields
3177 . locX_t - A local vector with solution field time derivatives, or NULL
3178 - locA   - A local vector with auxiliary fields, or NULL
3179 
3180   Output Parameters:
3181 + u   - The field coefficients
3182 . u_t - The fields derivative coefficients
3183 - a   - The auxiliary field coefficients
3184 
3185   Level: developer
3186 
3187 .seealso: DMPlexGetFaceFields()
3188 @*/
3189 PetscErrorCode DMPlexRestoreCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
3190 {
3191   PetscErrorCode ierr;
3192 
3193   PetscFunctionBegin;
3194   ierr = DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u);CHKERRQ(ierr);
3195   if (locX_t) {ierr = DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u_t);CHKERRQ(ierr);}
3196   if (locA)   {ierr = DMRestoreWorkArray(dm, 0, MPIU_SCALAR, a);CHKERRQ(ierr);}
3197   PetscFunctionReturn(0);
3198 }
3199 
3200 /*@C
3201   DMPlexGetFaceFields - Retrieve the field values values for a chunk of faces
3202 
3203   Input Parameters:
3204 + dm     - The DM
3205 . fStart - The first face to include
3206 . fEnd   - The first face to exclude
3207 . locX   - A local vector with the solution fields
3208 . locX_t - A local vector with solution field time derivatives, or NULL
3209 . faceGeometry - A local vector with face geometry
3210 . cellGeometry - A local vector with cell geometry
3211 - locaGrad - A local vector with field gradients, or NULL
3212 
3213   Output Parameters:
3214 + Nface - The number of faces with field values
3215 . uL - The field values at the left side of the face
3216 - uR - The field values at the right side of the face
3217 
3218   Level: developer
3219 
3220 .seealso: DMPlexGetCellFields()
3221 @*/
3222 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)
3223 {
3224   DM                 dmFace, dmCell, dmGrad = NULL;
3225   PetscSection       section;
3226   PetscDS            prob;
3227   DMLabel            ghostLabel;
3228   const PetscScalar *facegeom, *cellgeom, *x, *lgrad;
3229   PetscBool         *isFE;
3230   PetscInt           dim, Nf, f, Nc, numFaces = fEnd - fStart, iface, face;
3231   PetscErrorCode     ierr;
3232 
3233   PetscFunctionBegin;
3234   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3235   PetscValidHeaderSpecific(locX, VEC_CLASSID, 4);
3236   if (locX_t) {PetscValidHeaderSpecific(locX_t, VEC_CLASSID, 5);}
3237   PetscValidHeaderSpecific(faceGeometry, VEC_CLASSID, 6);
3238   PetscValidHeaderSpecific(cellGeometry, VEC_CLASSID, 7);
3239   if (locGrad) {PetscValidHeaderSpecific(locGrad, VEC_CLASSID, 8);}
3240   PetscValidPointer(uL, 9);
3241   PetscValidPointer(uR, 10);
3242   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
3243   ierr = DMGetDS(dm, &prob);CHKERRQ(ierr);
3244   ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);
3245   ierr = PetscDSGetNumFields(prob, &Nf);CHKERRQ(ierr);
3246   ierr = PetscDSGetTotalComponents(prob, &Nc);CHKERRQ(ierr);
3247   ierr = PetscMalloc1(Nf, &isFE);CHKERRQ(ierr);
3248   for (f = 0; f < Nf; ++f) {
3249     PetscObject  obj;
3250     PetscClassId id;
3251 
3252     ierr = PetscDSGetDiscretization(prob, f, &obj);CHKERRQ(ierr);
3253     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
3254     if (id == PETSCFE_CLASSID)      {isFE[f] = PETSC_TRUE;}
3255     else if (id == PETSCFV_CLASSID) {isFE[f] = PETSC_FALSE;}
3256     else                            {isFE[f] = PETSC_FALSE;}
3257   }
3258   ierr = DMGetLabel(dm, "ghost", &ghostLabel);CHKERRQ(ierr);
3259   ierr = VecGetArrayRead(locX, &x);CHKERRQ(ierr);
3260   ierr = VecGetDM(faceGeometry, &dmFace);CHKERRQ(ierr);
3261   ierr = VecGetArrayRead(faceGeometry, &facegeom);CHKERRQ(ierr);
3262   ierr = VecGetDM(cellGeometry, &dmCell);CHKERRQ(ierr);
3263   ierr = VecGetArrayRead(cellGeometry, &cellgeom);CHKERRQ(ierr);
3264   if (locGrad) {
3265     ierr = VecGetDM(locGrad, &dmGrad);CHKERRQ(ierr);
3266     ierr = VecGetArrayRead(locGrad, &lgrad);CHKERRQ(ierr);
3267   }
3268   ierr = DMGetWorkArray(dm, numFaces*Nc, MPIU_SCALAR, uL);CHKERRQ(ierr);
3269   ierr = DMGetWorkArray(dm, numFaces*Nc, MPIU_SCALAR, uR);CHKERRQ(ierr);
3270   /* Right now just eat the extra work for FE (could make a cell loop) */
3271   for (face = fStart, iface = 0; face < fEnd; ++face) {
3272     const PetscInt        *cells;
3273     PetscFVFaceGeom       *fg;
3274     PetscFVCellGeom       *cgL, *cgR;
3275     PetscScalar           *xL, *xR, *gL, *gR;
3276     PetscScalar           *uLl = *uL, *uRl = *uR;
3277     PetscInt               ghost, nsupp, nchild;
3278 
3279     ierr = DMLabelGetValue(ghostLabel, face, &ghost);CHKERRQ(ierr);
3280     ierr = DMPlexGetSupportSize(dm, face, &nsupp);CHKERRQ(ierr);
3281     ierr = DMPlexGetTreeChildren(dm, face, &nchild, NULL);CHKERRQ(ierr);
3282     if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
3283     ierr = DMPlexPointLocalRead(dmFace, face, facegeom, &fg);CHKERRQ(ierr);
3284     ierr = DMPlexGetSupport(dm, face, &cells);CHKERRQ(ierr);
3285     ierr = DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL);CHKERRQ(ierr);
3286     ierr = DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR);CHKERRQ(ierr);
3287     for (f = 0; f < Nf; ++f) {
3288       PetscInt off;
3289 
3290       ierr = PetscDSGetComponentOffset(prob, f, &off);CHKERRQ(ierr);
3291       if (isFE[f]) {
3292         const PetscInt *cone;
3293         PetscInt        comp, coneSizeL, coneSizeR, faceLocL, faceLocR, ldof, rdof, d;
3294 
3295         xL = xR = NULL;
3296         ierr = PetscSectionGetFieldComponents(section, f, &comp);CHKERRQ(ierr);
3297         ierr = DMPlexVecGetClosure(dm, section, locX, cells[0], &ldof, (PetscScalar **) &xL);CHKERRQ(ierr);
3298         ierr = DMPlexVecGetClosure(dm, section, locX, cells[1], &rdof, (PetscScalar **) &xR);CHKERRQ(ierr);
3299         ierr = DMPlexGetCone(dm, cells[0], &cone);CHKERRQ(ierr);
3300         ierr = DMPlexGetConeSize(dm, cells[0], &coneSizeL);CHKERRQ(ierr);
3301         for (faceLocL = 0; faceLocL < coneSizeL; ++faceLocL) if (cone[faceLocL] == face) break;
3302         ierr = DMPlexGetCone(dm, cells[1], &cone);CHKERRQ(ierr);
3303         ierr = DMPlexGetConeSize(dm, cells[1], &coneSizeR);CHKERRQ(ierr);
3304         for (faceLocR = 0; faceLocR < coneSizeR; ++faceLocR) if (cone[faceLocR] == face) break;
3305         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]);
3306         /* Check that FEM field has values in the right cell (sometimes its an FV ghost cell) */
3307         /* TODO: this is a hack that might not be right for nonconforming */
3308         if (faceLocL < coneSizeL) {
3309           ierr = PetscFEEvaluateFaceFields_Internal(prob, f, faceLocL, xL, &uLl[iface*Nc+off]);CHKERRQ(ierr);
3310           if (rdof == ldof && faceLocR < coneSizeR) {ierr = PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface*Nc+off]);CHKERRQ(ierr);}
3311           else              {for(d = 0; d < comp; ++d) uRl[iface*Nc+off+d] = uLl[iface*Nc+off+d];}
3312         }
3313         else {
3314           ierr = PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface*Nc+off]);CHKERRQ(ierr);
3315           ierr = PetscSectionGetFieldComponents(section, f, &comp);CHKERRQ(ierr);
3316           for(d = 0; d < comp; ++d) uLl[iface*Nc+off+d] = uRl[iface*Nc+off+d];
3317         }
3318         ierr = DMPlexVecRestoreClosure(dm, section, locX, cells[0], &ldof, (PetscScalar **) &xL);CHKERRQ(ierr);
3319         ierr = DMPlexVecRestoreClosure(dm, section, locX, cells[1], &rdof, (PetscScalar **) &xR);CHKERRQ(ierr);
3320       } else {
3321         PetscFV  fv;
3322         PetscInt numComp, c;
3323 
3324         ierr = PetscDSGetDiscretization(prob, f, (PetscObject *) &fv);CHKERRQ(ierr);
3325         ierr = PetscFVGetNumComponents(fv, &numComp);CHKERRQ(ierr);
3326         ierr = DMPlexPointLocalFieldRead(dm, cells[0], f, x, &xL);CHKERRQ(ierr);
3327         ierr = DMPlexPointLocalFieldRead(dm, cells[1], f, x, &xR);CHKERRQ(ierr);
3328         if (dmGrad) {
3329           PetscReal dxL[3], dxR[3];
3330 
3331           ierr = DMPlexPointLocalRead(dmGrad, cells[0], lgrad, &gL);CHKERRQ(ierr);
3332           ierr = DMPlexPointLocalRead(dmGrad, cells[1], lgrad, &gR);CHKERRQ(ierr);
3333           DMPlex_WaxpyD_Internal(dim, -1, cgL->centroid, fg->centroid, dxL);
3334           DMPlex_WaxpyD_Internal(dim, -1, cgR->centroid, fg->centroid, dxR);
3335           for (c = 0; c < numComp; ++c) {
3336             uLl[iface*Nc+off+c] = xL[c] + DMPlex_DotD_Internal(dim, &gL[c*dim], dxL);
3337             uRl[iface*Nc+off+c] = xR[c] + DMPlex_DotD_Internal(dim, &gR[c*dim], dxR);
3338           }
3339         } else {
3340           for (c = 0; c < numComp; ++c) {
3341             uLl[iface*Nc+off+c] = xL[c];
3342             uRl[iface*Nc+off+c] = xR[c];
3343           }
3344         }
3345       }
3346     }
3347     ++iface;
3348   }
3349   *Nface = iface;
3350   ierr = VecRestoreArrayRead(locX, &x);CHKERRQ(ierr);
3351   ierr = VecRestoreArrayRead(faceGeometry, &facegeom);CHKERRQ(ierr);
3352   ierr = VecRestoreArrayRead(cellGeometry, &cellgeom);CHKERRQ(ierr);
3353   if (locGrad) {
3354     ierr = VecRestoreArrayRead(locGrad, &lgrad);CHKERRQ(ierr);
3355   }
3356   ierr = PetscFree(isFE);CHKERRQ(ierr);
3357   PetscFunctionReturn(0);
3358 }
3359 
3360 /*@C
3361   DMPlexRestoreFaceFields - Restore the field values values for a chunk of faces
3362 
3363   Input Parameters:
3364 + dm     - The DM
3365 . fStart - The first face to include
3366 . fEnd   - The first face to exclude
3367 . locX   - A local vector with the solution fields
3368 . locX_t - A local vector with solution field time derivatives, or NULL
3369 . faceGeometry - A local vector with face geometry
3370 . cellGeometry - A local vector with cell geometry
3371 - locaGrad - A local vector with field gradients, or NULL
3372 
3373   Output Parameters:
3374 + Nface - The number of faces with field values
3375 . uL - The field values at the left side of the face
3376 - uR - The field values at the right side of the face
3377 
3378   Level: developer
3379 
3380 .seealso: DMPlexGetFaceFields()
3381 @*/
3382 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)
3383 {
3384   PetscErrorCode ierr;
3385 
3386   PetscFunctionBegin;
3387   ierr = DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uL);CHKERRQ(ierr);
3388   ierr = DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uR);CHKERRQ(ierr);
3389   PetscFunctionReturn(0);
3390 }
3391 
3392 /*@C
3393   DMPlexGetFaceGeometry - Retrieve the geometric values for a chunk of faces
3394 
3395   Input Parameters:
3396 + dm     - The DM
3397 . fStart - The first face to include
3398 . fEnd   - The first face to exclude
3399 . faceGeometry - A local vector with face geometry
3400 - cellGeometry - A local vector with cell geometry
3401 
3402   Output Parameters:
3403 + Nface - The number of faces with field values
3404 . fgeom - The extract the face centroid and normal
3405 - vol   - The cell volume
3406 
3407   Level: developer
3408 
3409 .seealso: DMPlexGetCellFields()
3410 @*/
3411 PetscErrorCode DMPlexGetFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom **fgeom, PetscReal **vol)
3412 {
3413   DM                 dmFace, dmCell;
3414   DMLabel            ghostLabel;
3415   const PetscScalar *facegeom, *cellgeom;
3416   PetscInt           dim, numFaces = fEnd - fStart, iface, face;
3417   PetscErrorCode     ierr;
3418 
3419   PetscFunctionBegin;
3420   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3421   PetscValidHeaderSpecific(faceGeometry, VEC_CLASSID, 4);
3422   PetscValidHeaderSpecific(cellGeometry, VEC_CLASSID, 5);
3423   PetscValidPointer(fgeom, 6);
3424   PetscValidPointer(vol, 7);
3425   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
3426   ierr = DMGetLabel(dm, "ghost", &ghostLabel);CHKERRQ(ierr);
3427   ierr = VecGetDM(faceGeometry, &dmFace);CHKERRQ(ierr);
3428   ierr = VecGetArrayRead(faceGeometry, &facegeom);CHKERRQ(ierr);
3429   ierr = VecGetDM(cellGeometry, &dmCell);CHKERRQ(ierr);
3430   ierr = VecGetArrayRead(cellGeometry, &cellgeom);CHKERRQ(ierr);
3431   ierr = PetscMalloc1(numFaces, fgeom);CHKERRQ(ierr);
3432   ierr = DMGetWorkArray(dm, numFaces*2, MPIU_SCALAR, vol);CHKERRQ(ierr);
3433   for (face = fStart, iface = 0; face < fEnd; ++face) {
3434     const PetscInt        *cells;
3435     PetscFVFaceGeom       *fg;
3436     PetscFVCellGeom       *cgL, *cgR;
3437     PetscFVFaceGeom       *fgeoml = *fgeom;
3438     PetscReal             *voll   = *vol;
3439     PetscInt               ghost, d, nchild, nsupp;
3440 
3441     ierr = DMLabelGetValue(ghostLabel, face, &ghost);CHKERRQ(ierr);
3442     ierr = DMPlexGetSupportSize(dm, face, &nsupp);CHKERRQ(ierr);
3443     ierr = DMPlexGetTreeChildren(dm, face, &nchild, NULL);CHKERRQ(ierr);
3444     if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
3445     ierr = DMPlexPointLocalRead(dmFace, face, facegeom, &fg);CHKERRQ(ierr);
3446     ierr = DMPlexGetSupport(dm, face, &cells);CHKERRQ(ierr);
3447     ierr = DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL);CHKERRQ(ierr);
3448     ierr = DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR);CHKERRQ(ierr);
3449     for (d = 0; d < dim; ++d) {
3450       fgeoml[iface].centroid[d] = fg->centroid[d];
3451       fgeoml[iface].normal[d]   = fg->normal[d];
3452     }
3453     voll[iface*2+0] = cgL->volume;
3454     voll[iface*2+1] = cgR->volume;
3455     ++iface;
3456   }
3457   *Nface = iface;
3458   ierr = VecRestoreArrayRead(faceGeometry, &facegeom);CHKERRQ(ierr);
3459   ierr = VecRestoreArrayRead(cellGeometry, &cellgeom);CHKERRQ(ierr);
3460   PetscFunctionReturn(0);
3461 }
3462 
3463 /*@C
3464   DMPlexRestoreFaceGeometry - Restore the field values values for a chunk of faces
3465 
3466   Input Parameters:
3467 + dm     - The DM
3468 . fStart - The first face to include
3469 . fEnd   - The first face to exclude
3470 . faceGeometry - A local vector with face geometry
3471 - cellGeometry - A local vector with cell geometry
3472 
3473   Output Parameters:
3474 + Nface - The number of faces with field values
3475 . fgeom - The extract the face centroid and normal
3476 - vol   - The cell volume
3477 
3478   Level: developer
3479 
3480 .seealso: DMPlexGetFaceFields()
3481 @*/
3482 PetscErrorCode DMPlexRestoreFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom **fgeom, PetscReal **vol)
3483 {
3484   PetscErrorCode ierr;
3485 
3486   PetscFunctionBegin;
3487   ierr = PetscFree(*fgeom);CHKERRQ(ierr);
3488   ierr = DMRestoreWorkArray(dm, 0, MPIU_REAL, vol);CHKERRQ(ierr);
3489   PetscFunctionReturn(0);
3490 }
3491 
3492 PetscErrorCode DMSNESGetFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
3493 {
3494   char            composeStr[33] = {0};
3495   PetscObjectId   id;
3496   PetscContainer  container;
3497   PetscErrorCode  ierr;
3498 
3499   PetscFunctionBegin;
3500   ierr = PetscObjectGetId((PetscObject)quad,&id);CHKERRQ(ierr);
3501   ierr = PetscSNPrintf(composeStr, 32, "DMSNESGetFEGeom_%x\n", id);CHKERRQ(ierr);
3502   ierr = PetscObjectQuery((PetscObject) pointIS, composeStr, (PetscObject *) &container);CHKERRQ(ierr);
3503   if (container) {
3504     ierr = PetscContainerGetPointer(container, (void **) geom);CHKERRQ(ierr);
3505   } else {
3506     ierr = DMFieldCreateFEGeom(coordField, pointIS, quad, faceData, geom);CHKERRQ(ierr);
3507     ierr = PetscContainerCreate(PETSC_COMM_SELF,&container);CHKERRQ(ierr);
3508     ierr = PetscContainerSetPointer(container, (void *) *geom);CHKERRQ(ierr);
3509     ierr = PetscContainerSetUserDestroy(container, PetscContainerUserDestroy_PetscFEGeom);CHKERRQ(ierr);
3510     ierr = PetscObjectCompose((PetscObject) pointIS, composeStr, (PetscObject) container);CHKERRQ(ierr);
3511     ierr = PetscContainerDestroy(&container);CHKERRQ(ierr);
3512   }
3513   PetscFunctionReturn(0);
3514 }
3515 
3516 PetscErrorCode DMSNESRestoreFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
3517 {
3518   PetscFunctionBegin;
3519   *geom = NULL;
3520   PetscFunctionReturn(0);
3521 }
3522 
3523 PetscErrorCode DMPlexComputeResidual_Patch_Internal(DM dm, PetscSection section, IS cellIS, PetscReal t, Vec locX, Vec locX_t, Vec locF, void *user)
3524 {
3525   DM_Plex         *mesh       = (DM_Plex *) dm->data;
3526   const char      *name       = "Residual";
3527   DM               dmAux      = NULL;
3528   DMLabel          ghostLabel = NULL;
3529   PetscDS          prob       = NULL;
3530   PetscDS          probAux    = NULL;
3531   PetscBool        useFEM     = PETSC_FALSE;
3532   PetscBool        isImplicit = (locX_t || t == PETSC_MIN_REAL) ? PETSC_TRUE : PETSC_FALSE;
3533   DMField          coordField = NULL;
3534   Vec              locA;
3535   PetscScalar     *u = NULL, *u_t, *a, *uL = NULL, *uR = NULL;
3536   IS               chunkIS;
3537   const PetscInt  *cells;
3538   PetscInt         cStart, cEnd, numCells;
3539   PetscInt         Nf, f, totDim, totDimAux, numChunks, cellChunkSize, chunk, fStart, fEnd;
3540   PetscInt         maxDegree = PETSC_MAX_INT;
3541   PetscQuadrature  affineQuad = NULL, *quads = NULL;
3542   PetscFEGeom     *affineGeom = NULL, **geoms = NULL;
3543   PetscErrorCode   ierr;
3544 
3545   PetscFunctionBegin;
3546   ierr = PetscLogEventBegin(DMPLEX_ResidualFEM,dm,0,0,0);CHKERRQ(ierr);
3547   /* FEM+FVM */
3548   /* 1: Get sizes from dm and dmAux */
3549   ierr = DMGetLabel(dm, "ghost", &ghostLabel);CHKERRQ(ierr);
3550   ierr = DMGetDS(dm, &prob);CHKERRQ(ierr);
3551   ierr = PetscDSGetNumFields(prob, &Nf);CHKERRQ(ierr);
3552   ierr = PetscDSGetTotalDimension(prob, &totDim);CHKERRQ(ierr);
3553   ierr = PetscObjectQuery((PetscObject) dm, "A", (PetscObject *) &locA);CHKERRQ(ierr);
3554   if (locA) {
3555     ierr = VecGetDM(locA, &dmAux);CHKERRQ(ierr);
3556     ierr = DMGetDS(dmAux, &probAux);CHKERRQ(ierr);
3557     ierr = PetscDSGetTotalDimension(probAux, &totDimAux);CHKERRQ(ierr);
3558   }
3559   /* 2: Get geometric data */
3560   for (f = 0; f < Nf; ++f) {
3561     PetscObject  obj;
3562     PetscClassId id;
3563     PetscBool    fimp;
3564 
3565     ierr = PetscDSGetImplicit(prob, f, &fimp);CHKERRQ(ierr);
3566     if (isImplicit != fimp) continue;
3567     ierr = PetscDSGetDiscretization(prob, f, &obj);CHKERRQ(ierr);
3568     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
3569     if (id == PETSCFE_CLASSID) {useFEM = PETSC_TRUE;}
3570     if (id == PETSCFV_CLASSID) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONG,"Use of FVM with PCPATCH not yet implemented");
3571   }
3572   if (useFEM) {
3573     ierr = DMGetCoordinateField(dm, &coordField);CHKERRQ(ierr);
3574     ierr = DMFieldGetDegree(coordField,cellIS,NULL,&maxDegree);CHKERRQ(ierr);
3575     if (maxDegree <= 1) {
3576       ierr = DMFieldCreateDefaultQuadrature(coordField,cellIS,&affineQuad);CHKERRQ(ierr);
3577       if (affineQuad) {
3578         ierr = DMSNESGetFEGeom(coordField,cellIS,affineQuad,PETSC_FALSE,&affineGeom);CHKERRQ(ierr);
3579       }
3580     } else {
3581       ierr = PetscCalloc2(Nf,&quads,Nf,&geoms);CHKERRQ(ierr);
3582       for (f = 0; f < Nf; ++f) {
3583         PetscObject  obj;
3584         PetscClassId id;
3585         PetscBool    fimp;
3586 
3587         ierr = PetscDSGetImplicit(prob, f, &fimp);CHKERRQ(ierr);
3588         if (isImplicit != fimp) continue;
3589         ierr = PetscDSGetDiscretization(prob, f, &obj);CHKERRQ(ierr);
3590         ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
3591         if (id == PETSCFE_CLASSID) {
3592           PetscFE fe = (PetscFE) obj;
3593 
3594           ierr = PetscFEGetQuadrature(fe, &quads[f]);CHKERRQ(ierr);
3595           ierr = PetscObjectReference((PetscObject)quads[f]);CHKERRQ(ierr);
3596           ierr = DMSNESGetFEGeom(coordField,cellIS,quads[f],PETSC_FALSE,&geoms[f]);CHKERRQ(ierr);
3597         }
3598       }
3599     }
3600   }
3601   /* Loop over chunks */
3602   ierr = ISGetPointRange(cellIS, &cStart, &cEnd, &cells);CHKERRQ(ierr);
3603   ierr = DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd);CHKERRQ(ierr);
3604   if (useFEM) {ierr = ISCreate(PETSC_COMM_SELF, &chunkIS);CHKERRQ(ierr);}
3605   numCells      = cEnd - cStart;
3606   numChunks     = 1;
3607   cellChunkSize = numCells/numChunks;
3608   numChunks     = PetscMin(1,numCells);
3609   for (chunk = 0; chunk < numChunks; ++chunk) {
3610     PetscScalar     *elemVec, *fluxL = NULL, *fluxR = NULL;
3611     PetscReal       *vol = NULL;
3612     PetscFVFaceGeom *fgeom = NULL;
3613     PetscInt         cS = cStart+chunk*cellChunkSize, cE = PetscMin(cS+cellChunkSize, cEnd), numCells = cE - cS, c;
3614     PetscInt         numFaces = 0;
3615 
3616     /* Extract field coefficients */
3617     if (useFEM) {
3618       ierr = ISGetPointSubrange(chunkIS, cS, cE, cells);CHKERRQ(ierr);
3619       ierr = DMPlexGetCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a);CHKERRQ(ierr);
3620       ierr = DMGetWorkArray(dm, numCells*totDim, MPIU_SCALAR, &elemVec);CHKERRQ(ierr);
3621       ierr = PetscArrayzero(elemVec, numCells*totDim);CHKERRQ(ierr);
3622     }
3623     /* TODO We will interlace both our field coefficients (u, u_t, uL, uR, etc.) and our output (elemVec, fL, fR). I think this works */
3624     /* Loop over fields */
3625     for (f = 0; f < Nf; ++f) {
3626       PetscObject  obj;
3627       PetscClassId id;
3628       PetscBool    fimp;
3629       PetscInt     numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;
3630 
3631       ierr = PetscDSGetImplicit(prob, f, &fimp);CHKERRQ(ierr);
3632       if (isImplicit != fimp) continue;
3633       ierr = PetscDSGetDiscretization(prob, f, &obj);CHKERRQ(ierr);
3634       ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
3635       if (id == PETSCFE_CLASSID) {
3636         PetscFE         fe = (PetscFE) obj;
3637         PetscFEGeom    *geom = affineGeom ? affineGeom : geoms[f];
3638         PetscFEGeom    *chunkGeom = NULL;
3639         PetscQuadrature quad = affineQuad ? affineQuad : quads[f];
3640         PetscInt        Nq, Nb;
3641 
3642         ierr = PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches);CHKERRQ(ierr);
3643         ierr = PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL);CHKERRQ(ierr);
3644         ierr = PetscFEGetDimension(fe, &Nb);CHKERRQ(ierr);
3645         blockSize = Nb;
3646         batchSize = numBlocks * blockSize;
3647         ierr      = PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches);CHKERRQ(ierr);
3648         numChunks = numCells / (numBatches*batchSize);
3649         Ne        = numChunks*numBatches*batchSize;
3650         Nr        = numCells % (numBatches*batchSize);
3651         offset    = numCells - Nr;
3652         /* Integrate FE residual to get elemVec (need fields at quadrature points) */
3653         /*   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) */
3654         ierr = PetscFEGeomGetChunk(geom,0,offset,&chunkGeom);CHKERRQ(ierr);
3655         ierr = PetscFEIntegrateResidual(prob, f, Ne, chunkGeom, u, u_t, probAux, a, t, elemVec);CHKERRQ(ierr);
3656         ierr = PetscFEGeomGetChunk(geom,offset,numCells,&chunkGeom);CHKERRQ(ierr);
3657         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);
3658         ierr = PetscFEGeomRestoreChunk(geom,offset,numCells,&chunkGeom);CHKERRQ(ierr);
3659       } else if (id == PETSCFV_CLASSID) {
3660         PetscFV fv = (PetscFV) obj;
3661 
3662         Ne = numFaces;
3663         /* Riemann solve over faces (need fields at face centroids) */
3664         /*   We need to evaluate FE fields at those coordinates */
3665         ierr = PetscFVIntegrateRHSFunction(fv, prob, f, Ne, fgeom, vol, uL, uR, fluxL, fluxR);CHKERRQ(ierr);
3666       } else SETERRQ1(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", f);
3667     }
3668     /* Loop over domain */
3669     if (useFEM) {
3670       /* Add elemVec to locX */
3671       for (c = cS; c < cE; ++c) {
3672         const PetscInt cell = cells ? cells[c] : c;
3673         const PetscInt cind = c - cStart;
3674 
3675         if (mesh->printFEM > 1) {ierr = DMPrintCellVector(cell, name, totDim, &elemVec[cind*totDim]);CHKERRQ(ierr);}
3676         if (ghostLabel) {
3677           PetscInt ghostVal;
3678 
3679           ierr = DMLabelGetValue(ghostLabel,cell,&ghostVal);CHKERRQ(ierr);
3680           if (ghostVal > 0) continue;
3681         }
3682         ierr = DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind*totDim], ADD_ALL_VALUES);CHKERRQ(ierr);
3683       }
3684     }
3685     /* Handle time derivative */
3686     if (locX_t) {
3687       PetscScalar *x_t, *fa;
3688 
3689       ierr = VecGetArray(locF, &fa);CHKERRQ(ierr);
3690       ierr = VecGetArray(locX_t, &x_t);CHKERRQ(ierr);
3691       for (f = 0; f < Nf; ++f) {
3692         PetscFV      fv;
3693         PetscObject  obj;
3694         PetscClassId id;
3695         PetscInt     pdim, d;
3696 
3697         ierr = PetscDSGetDiscretization(prob, f, &obj);CHKERRQ(ierr);
3698         ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
3699         if (id != PETSCFV_CLASSID) continue;
3700         fv   = (PetscFV) obj;
3701         ierr = PetscFVGetNumComponents(fv, &pdim);CHKERRQ(ierr);
3702         for (c = cS; c < cE; ++c) {
3703           const PetscInt cell = cells ? cells[c] : c;
3704           PetscScalar   *u_t, *r;
3705 
3706           if (ghostLabel) {
3707             PetscInt ghostVal;
3708 
3709             ierr = DMLabelGetValue(ghostLabel, cell, &ghostVal);CHKERRQ(ierr);
3710             if (ghostVal > 0) continue;
3711           }
3712           ierr = DMPlexPointLocalFieldRead(dm, cell, f, x_t, &u_t);CHKERRQ(ierr);
3713           ierr = DMPlexPointLocalFieldRef(dm, cell, f, fa, &r);CHKERRQ(ierr);
3714           for (d = 0; d < pdim; ++d) r[d] += u_t[d];
3715         }
3716       }
3717       ierr = VecRestoreArray(locX_t, &x_t);CHKERRQ(ierr);
3718       ierr = VecRestoreArray(locF, &fa);CHKERRQ(ierr);
3719     }
3720     if (useFEM) {
3721       ierr = DMPlexRestoreCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a);CHKERRQ(ierr);
3722       ierr = DMRestoreWorkArray(dm, numCells*totDim, MPIU_SCALAR, &elemVec);CHKERRQ(ierr);
3723     }
3724   }
3725   if (useFEM) {ierr = ISDestroy(&chunkIS);CHKERRQ(ierr);}
3726   ierr = ISRestorePointRange(cellIS, &cStart, &cEnd, &cells);CHKERRQ(ierr);
3727   /* TODO Could include boundary residual here (see DMPlexComputeResidual_Internal) */
3728   if (useFEM) {
3729     if (maxDegree <= 1) {
3730       ierr = DMSNESRestoreFEGeom(coordField,cellIS,affineQuad,PETSC_FALSE,&affineGeom);CHKERRQ(ierr);
3731       ierr = PetscQuadratureDestroy(&affineQuad);CHKERRQ(ierr);
3732     } else {
3733       for (f = 0; f < Nf; ++f) {
3734         ierr = DMSNESRestoreFEGeom(coordField,cellIS,quads[f],PETSC_FALSE,&geoms[f]);CHKERRQ(ierr);
3735         ierr = PetscQuadratureDestroy(&quads[f]);CHKERRQ(ierr);
3736       }
3737       ierr = PetscFree2(quads,geoms);CHKERRQ(ierr);
3738     }
3739   }
3740   ierr = PetscLogEventEnd(DMPLEX_ResidualFEM,dm,0,0,0);CHKERRQ(ierr);
3741   PetscFunctionReturn(0);
3742 }
3743 
3744 /*
3745   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
3746 
3747   X   - The local solution vector
3748   X_t - The local solution time derviative vector, or NULL
3749 */
3750 PetscErrorCode DMPlexComputeJacobian_Patch_Internal(DM dm, PetscSection section, PetscSection globalSection, IS cellIS,
3751                                                     PetscReal t, PetscReal X_tShift, Vec X, Vec X_t, Mat Jac, Mat JacP, void *ctx)
3752 {
3753   DM_Plex         *mesh  = (DM_Plex *) dm->data;
3754   const char      *name = "Jacobian", *nameP = "JacobianPre";
3755   DM               dmAux = NULL;
3756   PetscDS          prob,   probAux = NULL;
3757   PetscSection     sectionAux = NULL;
3758   Vec              A;
3759   DMField          coordField;
3760   PetscFEGeom     *cgeomFEM;
3761   PetscQuadrature  qGeom = NULL;
3762   Mat              J = Jac, JP = JacP;
3763   PetscScalar     *work, *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL, *elemMatP = NULL, *elemMatD = NULL;
3764   PetscBool        hasJac, hasPrec, hasDyn, assembleJac, isMatIS, isMatISP, *isFE, hasFV = PETSC_FALSE;
3765   const PetscInt  *cells;
3766   PetscInt         Nf, fieldI, fieldJ, maxDegree, numCells, cStart, cEnd, numChunks, chunkSize, chunk, totDim, totDimAux = 0, sz, wsz, off = 0, offCell = 0;
3767   PetscErrorCode   ierr;
3768 
3769   PetscFunctionBegin;
3770   CHKMEMQ;
3771   ierr = ISGetLocalSize(cellIS, &numCells);CHKERRQ(ierr);
3772   ierr = ISGetPointRange(cellIS, &cStart, &cEnd, &cells);CHKERRQ(ierr);
3773   ierr = PetscLogEventBegin(DMPLEX_JacobianFEM,dm,0,0,0);CHKERRQ(ierr);
3774   ierr = DMGetDS(dm, &prob);CHKERRQ(ierr);
3775   ierr = PetscObjectQuery((PetscObject) dm, "dmAux", (PetscObject *) &dmAux);CHKERRQ(ierr);
3776   ierr = PetscObjectQuery((PetscObject) dm, "A", (PetscObject *) &A);CHKERRQ(ierr);
3777   if (dmAux) {
3778     ierr = DMGetLocalSection(dmAux, &sectionAux);CHKERRQ(ierr);
3779     ierr = DMGetDS(dmAux, &probAux);CHKERRQ(ierr);
3780   }
3781   /* Get flags */
3782   ierr = PetscDSGetNumFields(prob, &Nf);CHKERRQ(ierr);
3783   ierr = DMGetWorkArray(dm, Nf, MPIU_BOOL, &isFE);CHKERRQ(ierr);
3784   for (fieldI = 0; fieldI < Nf; ++fieldI) {
3785     PetscObject  disc;
3786     PetscClassId id;
3787     ierr = PetscDSGetDiscretization(prob, fieldI, &disc);CHKERRQ(ierr);
3788     ierr = PetscObjectGetClassId(disc, &id);CHKERRQ(ierr);
3789     if (id == PETSCFE_CLASSID)      {isFE[fieldI] = PETSC_TRUE;}
3790     else if (id == PETSCFV_CLASSID) {hasFV = PETSC_TRUE; isFE[fieldI] = PETSC_FALSE;}
3791   }
3792   ierr = PetscDSHasJacobian(prob, &hasJac);CHKERRQ(ierr);
3793   ierr = PetscDSHasJacobianPreconditioner(prob, &hasPrec);CHKERRQ(ierr);
3794   ierr = PetscDSHasDynamicJacobian(prob, &hasDyn);CHKERRQ(ierr);
3795   assembleJac = hasJac && hasPrec && (Jac != JacP) ? PETSC_TRUE : PETSC_FALSE;
3796   hasDyn      = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
3797   ierr = PetscObjectTypeCompare((PetscObject) Jac,  MATIS, &isMatIS);CHKERRQ(ierr);
3798   ierr = PetscObjectTypeCompare((PetscObject) JacP, MATIS, &isMatISP);CHKERRQ(ierr);
3799   /* Setup input data and temp arrays (should be DMGetWorkArray) */
3800   if (isMatISP || isMatISP) {ierr = DMPlexGetSubdomainSection(dm, &globalSection);CHKERRQ(ierr);}
3801   if (isMatIS)  {ierr = MatISGetLocalMat(Jac,  &J);CHKERRQ(ierr);}
3802   if (isMatISP) {ierr = MatISGetLocalMat(JacP, &JP);CHKERRQ(ierr);}
3803   if (hasFV)    {ierr = MatSetOption(JP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE);CHKERRQ(ierr);} /* No allocated space for FV stuff, so ignore the zero entries */
3804   ierr = PetscObjectQuery((PetscObject) dm, "dmAux", (PetscObject *) &dmAux);CHKERRQ(ierr);
3805   ierr = PetscObjectQuery((PetscObject) dm, "A", (PetscObject *) &A);CHKERRQ(ierr);
3806   ierr = PetscDSGetTotalDimension(prob, &totDim);CHKERRQ(ierr);
3807   if (probAux) {ierr = PetscDSGetTotalDimension(probAux, &totDimAux);CHKERRQ(ierr);}
3808   CHKMEMQ;
3809   /* Compute batch sizes */
3810   if (isFE[0]) {
3811     PetscFE         fe;
3812     PetscQuadrature q;
3813     PetscInt        numQuadPoints, numBatches, batchSize, numBlocks, blockSize, Nb;
3814 
3815     ierr = PetscDSGetDiscretization(prob, 0, (PetscObject *) &fe);CHKERRQ(ierr);
3816     ierr = PetscFEGetQuadrature(fe, &q);CHKERRQ(ierr);
3817     ierr = PetscQuadratureGetData(q, NULL, NULL, &numQuadPoints, NULL, NULL);CHKERRQ(ierr);
3818     ierr = PetscFEGetDimension(fe, &Nb);CHKERRQ(ierr);
3819     ierr = PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches);CHKERRQ(ierr);
3820     blockSize = Nb*numQuadPoints;
3821     batchSize = numBlocks  * blockSize;
3822     chunkSize = numBatches * batchSize;
3823     numChunks = numCells / chunkSize + numCells % chunkSize;
3824     ierr = PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches);CHKERRQ(ierr);
3825   } else {
3826     chunkSize = numCells;
3827     numChunks = 1;
3828   }
3829   /* Get work space */
3830   wsz  = (((X?1:0) + (X_t?1:0) + (dmAux?1:0))*totDim + ((hasJac?1:0) + (hasPrec?1:0) + (hasDyn?1:0))*totDim*totDim)*chunkSize;
3831   ierr = DMGetWorkArray(dm, wsz, MPIU_SCALAR, &work);CHKERRQ(ierr);
3832   ierr = PetscArrayzero(work, wsz);CHKERRQ(ierr);
3833   off      = 0;
3834   u        = X       ? (sz = chunkSize*totDim,        off += sz, work+off-sz) : NULL;
3835   u_t      = X_t     ? (sz = chunkSize*totDim,        off += sz, work+off-sz) : NULL;
3836   a        = dmAux   ? (sz = chunkSize*totDimAux,     off += sz, work+off-sz) : NULL;
3837   elemMat  = hasJac  ? (sz = chunkSize*totDim*totDim, off += sz, work+off-sz) : NULL;
3838   elemMatP = hasPrec ? (sz = chunkSize*totDim*totDim, off += sz, work+off-sz) : NULL;
3839   elemMatD = hasDyn  ? (sz = chunkSize*totDim*totDim, off += sz, work+off-sz) : NULL;
3840   if (off != wsz) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Error is workspace size %D should be %D", off, wsz);
3841   /* Setup geometry */
3842   ierr = DMGetCoordinateField(dm, &coordField);CHKERRQ(ierr);
3843   ierr = DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree);CHKERRQ(ierr);
3844   if (maxDegree <= 1) {ierr = DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom);CHKERRQ(ierr);}
3845   if (!qGeom) {
3846     PetscFE fe;
3847 
3848     ierr = PetscDSGetDiscretization(prob, 0, (PetscObject *) &fe);CHKERRQ(ierr);
3849     ierr = PetscFEGetQuadrature(fe, &qGeom);CHKERRQ(ierr);
3850     ierr = PetscObjectReference((PetscObject) qGeom);CHKERRQ(ierr);
3851   }
3852   ierr = DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM);CHKERRQ(ierr);
3853   /* Compute volume integrals */
3854   if (assembleJac) {ierr = MatZeroEntries(J);CHKERRQ(ierr);}
3855   ierr = MatZeroEntries(JP);CHKERRQ(ierr);
3856   for (chunk = 0; chunk < numChunks; ++chunk, offCell += chunkSize) {
3857     const PetscInt   Ncell = PetscMin(chunkSize, numCells - offCell);
3858     PetscInt         c;
3859 
3860     /* Extract values */
3861     for (c = 0; c < Ncell; ++c) {
3862       const PetscInt cell = cells ? cells[c+offCell] : c+offCell;
3863       PetscScalar   *x = NULL,  *x_t = NULL;
3864       PetscInt       i;
3865 
3866       if (X) {
3867         ierr = DMPlexVecGetClosure(dm, section, X, cell, NULL, &x);CHKERRQ(ierr);
3868         for (i = 0; i < totDim; ++i) u[c*totDim+i] = x[i];
3869         ierr = DMPlexVecRestoreClosure(dm, section, X, cell, NULL, &x);CHKERRQ(ierr);
3870       }
3871       if (X_t) {
3872         ierr = DMPlexVecGetClosure(dm, section, X_t, cell, NULL, &x_t);CHKERRQ(ierr);
3873         for (i = 0; i < totDim; ++i) u_t[c*totDim+i] = x_t[i];
3874         ierr = DMPlexVecRestoreClosure(dm, section, X_t, cell, NULL, &x_t);CHKERRQ(ierr);
3875       }
3876       if (dmAux) {
3877         ierr = DMPlexVecGetClosure(dmAux, sectionAux, A, cell, NULL, &x);CHKERRQ(ierr);
3878         for (i = 0; i < totDimAux; ++i) a[c*totDimAux+i] = x[i];
3879         ierr = DMPlexVecRestoreClosure(dmAux, sectionAux, A, cell, NULL, &x);CHKERRQ(ierr);
3880       }
3881     }
3882     CHKMEMQ;
3883     for (fieldI = 0; fieldI < Nf; ++fieldI) {
3884       PetscFE fe;
3885       ierr = PetscDSGetDiscretization(prob, fieldI, (PetscObject *) &fe);CHKERRQ(ierr);
3886       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
3887         if (hasJac)  {ierr = PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN,     fieldI, fieldJ, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMat);CHKERRQ(ierr);}
3888         if (hasPrec) {ierr = PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, fieldI, fieldJ, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatP);CHKERRQ(ierr);}
3889         if (hasDyn)  {ierr = PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, fieldI, fieldJ, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatD);CHKERRQ(ierr);}
3890       }
3891       /* For finite volume, add the identity */
3892       if (!isFE[fieldI]) {
3893         PetscFV  fv;
3894         PetscInt eOffset = 0, Nc, fc, foff;
3895 
3896         ierr = PetscDSGetFieldOffset(prob, fieldI, &foff);CHKERRQ(ierr);
3897         ierr = PetscDSGetDiscretization(prob, fieldI, (PetscObject *) &fv);CHKERRQ(ierr);
3898         ierr = PetscFVGetNumComponents(fv, &Nc);CHKERRQ(ierr);
3899         for (c = 0; c < chunkSize; ++c, eOffset += totDim*totDim) {
3900           for (fc = 0; fc < Nc; ++fc) {
3901             const PetscInt i = foff + fc;
3902             if (hasJac)  {elemMat [eOffset+i*totDim+i] = 1.0;}
3903             if (hasPrec) {elemMatP[eOffset+i*totDim+i] = 1.0;}
3904           }
3905         }
3906       }
3907     }
3908     CHKMEMQ;
3909     /*   Add contribution from X_t */
3910     if (hasDyn) {for (c = 0; c < chunkSize*totDim*totDim; ++c) elemMat[c] += X_tShift*elemMatD[c];}
3911     /* Insert values into matrix */
3912     for (c = 0; c < Ncell; ++c) {
3913       const PetscInt cell = cells ? cells[c+offCell] : c+offCell;
3914       if (mesh->printFEM > 1) {
3915         if (hasJac)  {ierr = DMPrintCellMatrix(cell, name,  totDim, totDim, &elemMat[(c-cStart)*totDim*totDim]);CHKERRQ(ierr);}
3916         if (hasPrec) {ierr = DMPrintCellMatrix(cell, nameP, totDim, totDim, &elemMatP[(c-cStart)*totDim*totDim]);CHKERRQ(ierr);}
3917       }
3918       if (assembleJac) {ierr = DMPlexMatSetClosure(dm, section, globalSection, Jac, cell, &elemMat[(c-cStart)*totDim*totDim], ADD_VALUES);CHKERRQ(ierr);}
3919       ierr = DMPlexMatSetClosure(dm, section, globalSection, JP, cell, &elemMat[(c-cStart)*totDim*totDim], ADD_VALUES);CHKERRQ(ierr);
3920     }
3921     CHKMEMQ;
3922   }
3923   /* Cleanup */
3924   ierr = DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM);CHKERRQ(ierr);
3925   ierr = PetscQuadratureDestroy(&qGeom);CHKERRQ(ierr);
3926   if (hasFV) {ierr = MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE);CHKERRQ(ierr);}
3927   ierr = DMRestoreWorkArray(dm, Nf, MPIU_BOOL, &isFE);CHKERRQ(ierr);
3928   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);
3929   /* Compute boundary integrals */
3930   /* ierr = DMPlexComputeBdJacobian_Internal(dm, X, X_t, t, X_tShift, Jac, JacP, ctx);CHKERRQ(ierr); */
3931   /* Assemble matrix */
3932   if (assembleJac) {ierr = MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);ierr = MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);}
3933   ierr = MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);ierr = MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
3934   ierr = PetscLogEventEnd(DMPLEX_JacobianFEM,dm,0,0,0);CHKERRQ(ierr);
3935   CHKMEMQ;
3936   PetscFunctionReturn(0);
3937 }
3938