xref: /petsc/src/dm/impls/plex/plexfem.c (revision c73d2cf668e6709a5c9d6d1d49ca0c86e2ced9fc)
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   PetscDS            prob, probAux = NULL;
2004   PetscSection       section, sectionAux = NULL;
2005   Vec                locA = NULL;
2006   DMField            coordField;
2007   PetscInt           Nf,        totDim,        *uOff, *uOff_x;
2008   PetscInt           NfAux = 0, totDimAux = 0, *aOff = NULL;
2009   PetscScalar       *u, *a = NULL;
2010   const PetscScalar *constants;
2011   PetscInt           numConstants, f;
2012   PetscErrorCode     ierr;
2013 
2014   PetscFunctionBegin;
2015   ierr = DMGetCoordinateField(dm, &coordField);CHKERRQ(ierr);
2016   ierr = DMConvert(dm, DMPLEX, &plex);CHKERRQ(ierr);
2017   ierr = DMGetDS(dm, &prob);CHKERRQ(ierr);
2018   ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);
2019   ierr = PetscSectionGetNumFields(section, &Nf);CHKERRQ(ierr);
2020   /* Determine which discretizations we have */
2021   for (f = 0; f < Nf; ++f) {
2022     PetscObject  obj;
2023     PetscClassId id;
2024 
2025     ierr = PetscDSGetDiscretization(prob, f, &obj);CHKERRQ(ierr);
2026     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
2027     if (id == PETSCFV_CLASSID) SETERRQ1(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Not supported for FVM (field %D)", f);
2028   }
2029   /* Read DS information */
2030   ierr = PetscDSGetTotalDimension(prob, &totDim);CHKERRQ(ierr);
2031   ierr = PetscDSGetComponentOffsets(prob, &uOff);CHKERRQ(ierr);
2032   ierr = PetscDSGetComponentDerivativeOffsets(prob, &uOff_x);CHKERRQ(ierr);
2033   ierr = PetscDSGetConstants(prob, &numConstants, &constants);CHKERRQ(ierr);
2034   /* Read Auxiliary DS information */
2035   ierr = PetscObjectQuery((PetscObject) dm, "A", (PetscObject *) &locA);CHKERRQ(ierr);
2036   if (locA) {
2037     DM dmAux;
2038 
2039     ierr = VecGetDM(locA, &dmAux);CHKERRQ(ierr);
2040     ierr = DMConvert(dmAux, DMPLEX, &plexA);CHKERRQ(ierr);
2041     ierr = DMGetDS(dmAux, &probAux);CHKERRQ(ierr);
2042     ierr = PetscDSGetNumFields(probAux, &NfAux);CHKERRQ(ierr);
2043     ierr = DMGetLocalSection(dmAux, &sectionAux);CHKERRQ(ierr);
2044     ierr = PetscDSGetTotalDimension(probAux, &totDimAux);CHKERRQ(ierr);
2045     ierr = PetscDSGetComponentOffsets(probAux, &aOff);CHKERRQ(ierr);
2046   }
2047   /* Integrate over points */
2048   {
2049     PetscFEGeom    *fgeom, *chunkGeom = NULL;
2050     PetscInt        maxDegree;
2051     PetscQuadrature qGeom = NULL;
2052     const PetscInt *points;
2053     PetscInt        numFaces, face, Nq, field;
2054     PetscInt        numChunks, chunkSize, chunk, Nr, offset;
2055 
2056     ierr = ISGetLocalSize(pointIS, &numFaces);CHKERRQ(ierr);
2057     ierr = ISGetIndices(pointIS, &points);CHKERRQ(ierr);
2058     ierr = PetscCalloc2(numFaces*totDim, &u, locA ? numFaces*totDimAux : 0, &a);CHKERRQ(ierr);
2059     ierr = DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree);CHKERRQ(ierr);
2060     for (field = 0; field < Nf; ++field) {
2061       PetscFE fe;
2062 
2063       ierr = PetscDSGetDiscretization(prob, field, (PetscObject *) &fe);CHKERRQ(ierr);
2064       if (maxDegree <= 1) {ierr = DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom);CHKERRQ(ierr);}
2065       if (!qGeom) {
2066         ierr = PetscFEGetFaceQuadrature(fe, &qGeom);CHKERRQ(ierr);
2067         ierr = PetscObjectReference((PetscObject) qGeom);CHKERRQ(ierr);
2068       }
2069       ierr = PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL);CHKERRQ(ierr);
2070       ierr = DMPlexGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom);CHKERRQ(ierr);
2071       for (face = 0; face < numFaces; ++face) {
2072         const PetscInt point = points[face], *support, *cone;
2073         PetscScalar    *x    = NULL;
2074         PetscInt       i, coneSize, faceLoc;
2075 
2076         ierr = DMPlexGetSupport(dm, point, &support);CHKERRQ(ierr);
2077         ierr = DMPlexGetConeSize(dm, support[0], &coneSize);CHKERRQ(ierr);
2078         ierr = DMPlexGetCone(dm, support[0], &cone);CHKERRQ(ierr);
2079         for (faceLoc = 0; faceLoc < coneSize; ++faceLoc) if (cone[faceLoc] == point) break;
2080         if (faceLoc == coneSize) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find face %D in cone of support[0] %D", face, support[0]);
2081         fgeom->face[face][0] = faceLoc;
2082         ierr = DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x);CHKERRQ(ierr);
2083         for (i = 0; i < totDim; ++i) u[face*totDim+i] = x[i];
2084         ierr = DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x);CHKERRQ(ierr);
2085         if (locA) {
2086           PetscInt subp;
2087           ierr = DMPlexGetSubpoint(plexA, support[0], &subp);CHKERRQ(ierr);
2088           ierr = DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x);CHKERRQ(ierr);
2089           for (i = 0; i < totDimAux; ++i) a[f*totDimAux+i] = x[i];
2090           ierr = DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x);CHKERRQ(ierr);
2091         }
2092       }
2093       /* Get blocking */
2094       {
2095         PetscQuadrature q;
2096         PetscInt        numBatches, batchSize, numBlocks, blockSize;
2097         PetscInt        Nq, Nb;
2098 
2099         ierr = PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches);CHKERRQ(ierr);
2100         ierr = PetscFEGetQuadrature(fe, &q);CHKERRQ(ierr);
2101         ierr = PetscQuadratureGetData(q, NULL, NULL, &Nq, NULL, NULL);CHKERRQ(ierr);
2102         ierr = PetscFEGetDimension(fe, &Nb);CHKERRQ(ierr);
2103         blockSize = Nb*Nq;
2104         batchSize = numBlocks * blockSize;
2105         chunkSize = numBatches*batchSize;
2106         ierr = PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches);CHKERRQ(ierr);
2107         numChunks = numFaces / chunkSize;
2108         Nr        = numFaces % chunkSize;
2109         offset    = numFaces - Nr;
2110       }
2111       /* Do integration for each field */
2112       for (chunk = 0; chunk < numChunks; ++chunk) {
2113         ierr = PetscFEGeomGetChunk(fgeom, chunk*chunkSize, (chunk+1)*chunkSize, &chunkGeom);CHKERRQ(ierr);
2114         ierr = PetscFEIntegrateBd(prob, field, func, chunkSize, chunkGeom, u, probAux, a, fintegral);CHKERRQ(ierr);
2115         ierr = PetscFEGeomRestoreChunk(fgeom, 0, offset, &chunkGeom);CHKERRQ(ierr);
2116       }
2117       ierr = PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom);CHKERRQ(ierr);
2118       ierr = PetscFEIntegrateBd(prob, field, func, Nr, chunkGeom, &u[offset*totDim], probAux, a ? &a[offset*totDimAux] : NULL, &fintegral[offset*Nf]);CHKERRQ(ierr);
2119       ierr = PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom);CHKERRQ(ierr);
2120       /* Cleanup data arrays */
2121       ierr = DMPlexRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom);CHKERRQ(ierr);
2122       ierr = PetscQuadratureDestroy(&qGeom);CHKERRQ(ierr);
2123       ierr = PetscFree2(u, a);CHKERRQ(ierr);
2124       ierr = ISRestoreIndices(pointIS, &points);CHKERRQ(ierr);
2125     }
2126   }
2127   if (plex)  {ierr = DMDestroy(&plex);CHKERRQ(ierr);}
2128   if (plexA) {ierr = DMDestroy(&plexA);CHKERRQ(ierr);}
2129   PetscFunctionReturn(0);
2130 }
2131 
2132 /*@
2133   DMPlexComputeBdIntegral - Form the integral over the specified boundary from the global input X using pointwise functions specified by the user
2134 
2135   Input Parameters:
2136 + dm      - The mesh
2137 . X       - Global input vector
2138 . label   - The boundary DMLabel
2139 . numVals - The number of label values to use, or PETSC_DETERMINE for all values
2140 . vals    - The label values to use, or PETSC_NULL for all values
2141 . func    = The function to integrate along the boundary
2142 - user    - The user context
2143 
2144   Output Parameter:
2145 . integral - Integral for each field
2146 
2147   Level: developer
2148 
2149 .seealso: DMPlexComputeIntegralFEM(), DMPlexComputeBdResidualFEM()
2150 @*/
2151 PetscErrorCode DMPlexComputeBdIntegral(DM dm, Vec X, DMLabel label, PetscInt numVals, const PetscInt vals[],
2152                                        void (*func)(PetscInt, PetscInt, PetscInt,
2153                                                     const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[],
2154                                                     const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[],
2155                                                     PetscReal, const PetscReal[], const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]),
2156                                        PetscScalar *integral, void *user)
2157 {
2158   Vec            locX;
2159   PetscSection   section;
2160   DMLabel        depthLabel;
2161   IS             facetIS;
2162   PetscInt       dim, Nf, f, v;
2163   PetscErrorCode ierr;
2164 
2165   PetscFunctionBegin;
2166   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2167   PetscValidHeaderSpecific(X, VEC_CLASSID, 2);
2168   PetscValidPointer(label, 3);
2169   if (vals) PetscValidPointer(vals, 5);
2170   PetscValidPointer(integral, 6);
2171   ierr = PetscLogEventBegin(DMPLEX_IntegralFEM,dm,0,0,0);CHKERRQ(ierr);
2172   ierr = DMPlexGetDepthLabel(dm, &depthLabel);CHKERRQ(ierr);
2173   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
2174   ierr = DMLabelGetStratumIS(depthLabel, dim-1, &facetIS);CHKERRQ(ierr);
2175   ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);
2176   ierr = PetscSectionGetNumFields(section, &Nf);CHKERRQ(ierr);
2177   /* Get local solution with boundary values */
2178   ierr = DMGetLocalVector(dm, &locX);CHKERRQ(ierr);
2179   ierr = DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL);CHKERRQ(ierr);
2180   ierr = DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX);CHKERRQ(ierr);
2181   ierr = DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX);CHKERRQ(ierr);
2182   /* Loop over label values */
2183   ierr = PetscArrayzero(integral, Nf);CHKERRQ(ierr);
2184   for (v = 0; v < numVals; ++v) {
2185     IS           pointIS;
2186     PetscInt     numFaces, face;
2187     PetscScalar *fintegral;
2188 
2189     ierr = DMLabelGetStratumIS(label, vals[v], &pointIS);CHKERRQ(ierr);
2190     if (!pointIS) continue; /* No points with that id on this process */
2191     {
2192       IS isectIS;
2193 
2194       /* TODO: Special cases of ISIntersect where it is quick to check a priori if one is a superset of the other */
2195       ierr = ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS);CHKERRQ(ierr);
2196       ierr = ISDestroy(&pointIS);CHKERRQ(ierr);
2197       pointIS = isectIS;
2198     }
2199     ierr = ISGetLocalSize(pointIS, &numFaces);CHKERRQ(ierr);
2200     ierr = PetscCalloc1(numFaces*Nf, &fintegral);CHKERRQ(ierr);
2201     ierr = DMPlexComputeBdIntegral_Internal(dm, locX, pointIS, func, fintegral, user);CHKERRQ(ierr);
2202     /* Sum point contributions into integral */
2203     for (f = 0; f < Nf; ++f) for (face = 0; face < numFaces; ++face) integral[f] += fintegral[face*Nf+f];
2204     ierr = PetscFree(fintegral);CHKERRQ(ierr);
2205     ierr = ISDestroy(&pointIS);CHKERRQ(ierr);
2206   }
2207   ierr = DMRestoreLocalVector(dm, &locX);CHKERRQ(ierr);
2208   ierr = ISDestroy(&facetIS);CHKERRQ(ierr);
2209   ierr = PetscLogEventEnd(DMPLEX_IntegralFEM,dm,0,0,0);CHKERRQ(ierr);
2210   PetscFunctionReturn(0);
2211 }
2212 
2213 /*@
2214   DMPlexComputeInterpolatorNested - Form the local portion of the interpolation matrix I from the coarse DM to the uniformly refined DM.
2215 
2216   Input Parameters:
2217 + dmf  - The fine mesh
2218 . dmc  - The coarse mesh
2219 - user - The user context
2220 
2221   Output Parameter:
2222 . In  - The interpolation matrix
2223 
2224   Level: developer
2225 
2226 .seealso: DMPlexComputeInterpolatorGeneral(), DMPlexComputeJacobianFEM()
2227 @*/
2228 PetscErrorCode DMPlexComputeInterpolatorNested(DM dmc, DM dmf, Mat In, void *user)
2229 {
2230   DM_Plex          *mesh  = (DM_Plex *) dmc->data;
2231   const char       *name  = "Interpolator";
2232   PetscDS           prob;
2233   PetscFE          *feRef;
2234   PetscFV          *fvRef;
2235   PetscSection      fsection, fglobalSection;
2236   PetscSection      csection, cglobalSection;
2237   PetscScalar      *elemMat;
2238   PetscInt          dim, Nf, f, fieldI, fieldJ, offsetI, offsetJ, cStart, cEnd, c;
2239   PetscInt          cTotDim, rTotDim = 0;
2240   PetscErrorCode    ierr;
2241 
2242   PetscFunctionBegin;
2243   ierr = PetscLogEventBegin(DMPLEX_InterpolatorFEM,dmc,dmf,0,0);CHKERRQ(ierr);
2244   ierr = DMGetDimension(dmf, &dim);CHKERRQ(ierr);
2245   ierr = DMGetLocalSection(dmf, &fsection);CHKERRQ(ierr);
2246   ierr = DMGetGlobalSection(dmf, &fglobalSection);CHKERRQ(ierr);
2247   ierr = DMGetLocalSection(dmc, &csection);CHKERRQ(ierr);
2248   ierr = DMGetGlobalSection(dmc, &cglobalSection);CHKERRQ(ierr);
2249   ierr = PetscSectionGetNumFields(fsection, &Nf);CHKERRQ(ierr);
2250   ierr = DMPlexGetInteriorCellStratum(dmc, &cStart, &cEnd);CHKERRQ(ierr);
2251   ierr = DMGetDS(dmf, &prob);CHKERRQ(ierr);
2252   ierr = PetscCalloc2(Nf,&feRef,Nf,&fvRef);CHKERRQ(ierr);
2253   for (f = 0; f < Nf; ++f) {
2254     PetscObject  obj;
2255     PetscClassId id;
2256     PetscInt     rNb = 0, Nc = 0;
2257 
2258     ierr = PetscDSGetDiscretization(prob, f, &obj);CHKERRQ(ierr);
2259     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
2260     if (id == PETSCFE_CLASSID) {
2261       PetscFE fe = (PetscFE) obj;
2262 
2263       ierr = PetscFERefine(fe, &feRef[f]);CHKERRQ(ierr);
2264       ierr = PetscFEGetDimension(feRef[f], &rNb);CHKERRQ(ierr);
2265       ierr = PetscFEGetNumComponents(fe, &Nc);CHKERRQ(ierr);
2266     } else if (id == PETSCFV_CLASSID) {
2267       PetscFV        fv = (PetscFV) obj;
2268       PetscDualSpace Q;
2269 
2270       ierr = PetscFVRefine(fv, &fvRef[f]);CHKERRQ(ierr);
2271       ierr = PetscFVGetDualSpace(fvRef[f], &Q);CHKERRQ(ierr);
2272       ierr = PetscDualSpaceGetDimension(Q, &rNb);CHKERRQ(ierr);
2273       ierr = PetscFVGetNumComponents(fv, &Nc);CHKERRQ(ierr);
2274     }
2275     rTotDim += rNb;
2276   }
2277   ierr = PetscDSGetTotalDimension(prob, &cTotDim);CHKERRQ(ierr);
2278   ierr = PetscMalloc1(rTotDim*cTotDim,&elemMat);CHKERRQ(ierr);
2279   ierr = PetscArrayzero(elemMat, rTotDim*cTotDim);CHKERRQ(ierr);
2280   for (fieldI = 0, offsetI = 0; fieldI < Nf; ++fieldI) {
2281     PetscDualSpace   Qref;
2282     PetscQuadrature  f;
2283     const PetscReal *qpoints, *qweights;
2284     PetscReal       *points;
2285     PetscInt         npoints = 0, Nc, Np, fpdim, i, k, p, d;
2286 
2287     /* Compose points from all dual basis functionals */
2288     if (feRef[fieldI]) {
2289       ierr = PetscFEGetDualSpace(feRef[fieldI], &Qref);CHKERRQ(ierr);
2290       ierr = PetscFEGetNumComponents(feRef[fieldI], &Nc);CHKERRQ(ierr);
2291     } else {
2292       ierr = PetscFVGetDualSpace(fvRef[fieldI], &Qref);CHKERRQ(ierr);
2293       ierr = PetscFVGetNumComponents(fvRef[fieldI], &Nc);CHKERRQ(ierr);
2294     }
2295     ierr = PetscDualSpaceGetDimension(Qref, &fpdim);CHKERRQ(ierr);
2296     for (i = 0; i < fpdim; ++i) {
2297       ierr = PetscDualSpaceGetFunctional(Qref, i, &f);CHKERRQ(ierr);
2298       ierr = PetscQuadratureGetData(f, NULL, NULL, &Np, NULL, NULL);CHKERRQ(ierr);
2299       npoints += Np;
2300     }
2301     ierr = PetscMalloc1(npoints*dim,&points);CHKERRQ(ierr);
2302     for (i = 0, k = 0; i < fpdim; ++i) {
2303       ierr = PetscDualSpaceGetFunctional(Qref, i, &f);CHKERRQ(ierr);
2304       ierr = PetscQuadratureGetData(f, NULL, NULL, &Np, &qpoints, NULL);CHKERRQ(ierr);
2305       for (p = 0; p < Np; ++p, ++k) for (d = 0; d < dim; ++d) points[k*dim+d] = qpoints[p*dim+d];
2306     }
2307 
2308     for (fieldJ = 0, offsetJ = 0; fieldJ < Nf; ++fieldJ) {
2309       PetscObject  obj;
2310       PetscClassId id;
2311       PetscReal   *B;
2312       PetscInt     NcJ = 0, cpdim = 0, j, qNc;
2313 
2314       ierr = PetscDSGetDiscretization(prob, fieldJ, &obj);CHKERRQ(ierr);
2315       ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
2316       if (id == PETSCFE_CLASSID) {
2317         PetscFE fe = (PetscFE) obj;
2318 
2319         /* Evaluate basis at points */
2320         ierr = PetscFEGetNumComponents(fe, &NcJ);CHKERRQ(ierr);
2321         ierr = PetscFEGetDimension(fe, &cpdim);CHKERRQ(ierr);
2322         /* For now, fields only interpolate themselves */
2323         if (fieldI == fieldJ) {
2324           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);
2325           ierr = PetscFEGetTabulation(fe, npoints, points, &B, NULL, NULL);CHKERRQ(ierr);
2326           for (i = 0, k = 0; i < fpdim; ++i) {
2327             ierr = PetscDualSpaceGetFunctional(Qref, i, &f);CHKERRQ(ierr);
2328             ierr = PetscQuadratureGetData(f, NULL, &qNc, &Np, NULL, &qweights);CHKERRQ(ierr);
2329             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);
2330             for (p = 0; p < Np; ++p, ++k) {
2331               for (j = 0; j < cpdim; ++j) {
2332                 /*
2333                    cTotDim:            Total columns in element interpolation matrix, sum of number of dual basis functionals in each field
2334                    offsetI, offsetJ:   Offsets into the larger element interpolation matrix for different fields
2335                    fpdim, i, cpdim, j: Dofs for fine and coarse grids, correspond to dual space basis functionals
2336                    qNC, Nc, Ncj, c:    Number of components in this field
2337                    Np, p:              Number of quad points in the fine grid functional i
2338                    k:                  i*Np + p, overall point number for the interpolation
2339                 */
2340                 for (c = 0; c < Nc; ++c) elemMat[(offsetI + i)*cTotDim + offsetJ + j] += B[k*cpdim*NcJ+j*Nc+c]*qweights[p*qNc+c];
2341               }
2342             }
2343           }
2344           ierr = PetscFERestoreTabulation(fe, npoints, points, &B, NULL, NULL);CHKERRQ(ierr);CHKERRQ(ierr);
2345         }
2346       } else if (id == PETSCFV_CLASSID) {
2347         PetscFV        fv = (PetscFV) obj;
2348 
2349         /* Evaluate constant function at points */
2350         ierr = PetscFVGetNumComponents(fv, &NcJ);CHKERRQ(ierr);
2351         cpdim = 1;
2352         /* For now, fields only interpolate themselves */
2353         if (fieldI == fieldJ) {
2354           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);
2355           for (i = 0, k = 0; i < fpdim; ++i) {
2356             ierr = PetscDualSpaceGetFunctional(Qref, i, &f);CHKERRQ(ierr);
2357             ierr = PetscQuadratureGetData(f, NULL, &qNc, &Np, NULL, &qweights);CHKERRQ(ierr);
2358             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);
2359             for (p = 0; p < Np; ++p, ++k) {
2360               for (j = 0; j < cpdim; ++j) {
2361                 for (c = 0; c < Nc; ++c) elemMat[(offsetI + i)*cTotDim + offsetJ + j] += 1.0*qweights[p*qNc+c];
2362               }
2363             }
2364           }
2365         }
2366       }
2367       offsetJ += cpdim;
2368     }
2369     offsetI += fpdim;
2370     ierr = PetscFree(points);CHKERRQ(ierr);
2371   }
2372   if (mesh->printFEM > 1) {ierr = DMPrintCellMatrix(0, name, rTotDim, cTotDim, elemMat);CHKERRQ(ierr);}
2373   /* Preallocate matrix */
2374   {
2375     Mat          preallocator;
2376     PetscScalar *vals;
2377     PetscInt    *cellCIndices, *cellFIndices;
2378     PetscInt     locRows, locCols, cell;
2379 
2380     ierr = MatGetLocalSize(In, &locRows, &locCols);CHKERRQ(ierr);
2381     ierr = MatCreate(PetscObjectComm((PetscObject) In), &preallocator);CHKERRQ(ierr);
2382     ierr = MatSetType(preallocator, MATPREALLOCATOR);CHKERRQ(ierr);
2383     ierr = MatSetSizes(preallocator, locRows, locCols, PETSC_DETERMINE, PETSC_DETERMINE);CHKERRQ(ierr);
2384     ierr = MatSetUp(preallocator);CHKERRQ(ierr);
2385     ierr = PetscCalloc3(rTotDim*cTotDim, &vals,cTotDim,&cellCIndices,rTotDim,&cellFIndices);CHKERRQ(ierr);
2386     for (cell = cStart; cell < cEnd; ++cell) {
2387       ierr = DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, cell, cellCIndices, cellFIndices);CHKERRQ(ierr);
2388       ierr = MatSetValues(preallocator, rTotDim, cellFIndices, cTotDim, cellCIndices, vals, INSERT_VALUES);CHKERRQ(ierr);
2389     }
2390     ierr = PetscFree3(vals,cellCIndices,cellFIndices);CHKERRQ(ierr);
2391     ierr = MatAssemblyBegin(preallocator, MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
2392     ierr = MatAssemblyEnd(preallocator, MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
2393     ierr = MatPreallocatorPreallocate(preallocator, PETSC_TRUE, In);CHKERRQ(ierr);
2394     ierr = MatDestroy(&preallocator);CHKERRQ(ierr);
2395   }
2396   /* Fill matrix */
2397   ierr = MatZeroEntries(In);CHKERRQ(ierr);
2398   for (c = cStart; c < cEnd; ++c) {
2399     ierr = DMPlexMatSetClosureRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, In, c, elemMat, INSERT_VALUES);CHKERRQ(ierr);
2400   }
2401   for (f = 0; f < Nf; ++f) {ierr = PetscFEDestroy(&feRef[f]);CHKERRQ(ierr);}
2402   ierr = PetscFree2(feRef,fvRef);CHKERRQ(ierr);
2403   ierr = PetscFree(elemMat);CHKERRQ(ierr);
2404   ierr = MatAssemblyBegin(In, MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
2405   ierr = MatAssemblyEnd(In, MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
2406   if (mesh->printFEM) {
2407     ierr = PetscPrintf(PetscObjectComm((PetscObject)In), "%s:\n", name);CHKERRQ(ierr);
2408     ierr = MatChop(In, 1.0e-10);CHKERRQ(ierr);
2409     ierr = MatView(In, NULL);CHKERRQ(ierr);
2410   }
2411   ierr = PetscLogEventEnd(DMPLEX_InterpolatorFEM,dmc,dmf,0,0);CHKERRQ(ierr);
2412   PetscFunctionReturn(0);
2413 }
2414 
2415 PetscErrorCode DMPlexComputeMassMatrixNested(DM dmc, DM dmf, Mat mass, void *user)
2416 {
2417   SETERRQ(PetscObjectComm((PetscObject) dmc), PETSC_ERR_SUP, "Laziness");
2418 }
2419 
2420 /*@
2421   DMPlexComputeInterpolatorGeneral - Form the local portion of the interpolation matrix I from the coarse DM to a non-nested fine DM.
2422 
2423   Input Parameters:
2424 + dmf  - The fine mesh
2425 . dmc  - The coarse mesh
2426 - user - The user context
2427 
2428   Output Parameter:
2429 . In  - The interpolation matrix
2430 
2431   Level: developer
2432 
2433 .seealso: DMPlexComputeInterpolatorNested(), DMPlexComputeJacobianFEM()
2434 @*/
2435 PetscErrorCode DMPlexComputeInterpolatorGeneral(DM dmc, DM dmf, Mat In, void *user)
2436 {
2437   DM_Plex       *mesh = (DM_Plex *) dmf->data;
2438   const char    *name = "Interpolator";
2439   PetscDS        prob;
2440   PetscSection   fsection, csection, globalFSection, globalCSection;
2441   PetscHSetIJ    ht;
2442   PetscLayout    rLayout;
2443   PetscInt      *dnz, *onz;
2444   PetscInt       locRows, rStart, rEnd;
2445   PetscReal     *x, *v0, *J, *invJ, detJ;
2446   PetscReal     *v0c, *Jc, *invJc, detJc;
2447   PetscScalar   *elemMat;
2448   PetscInt       dim, Nf, field, totDim, cStart, cEnd, cell, ccell;
2449   PetscErrorCode ierr;
2450 
2451   PetscFunctionBegin;
2452   ierr = PetscLogEventBegin(DMPLEX_InterpolatorFEM,dmc,dmf,0,0);CHKERRQ(ierr);
2453   ierr = DMGetCoordinateDim(dmc, &dim);CHKERRQ(ierr);
2454   ierr = DMGetDS(dmc, &prob);CHKERRQ(ierr);
2455   ierr = PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL);CHKERRQ(ierr);
2456   ierr = PetscDSGetNumFields(prob, &Nf);CHKERRQ(ierr);
2457   ierr = PetscMalloc3(dim,&v0,dim*dim,&J,dim*dim,&invJ);CHKERRQ(ierr);
2458   ierr = PetscMalloc3(dim,&v0c,dim*dim,&Jc,dim*dim,&invJc);CHKERRQ(ierr);
2459   ierr = DMGetLocalSection(dmf, &fsection);CHKERRQ(ierr);
2460   ierr = DMGetGlobalSection(dmf, &globalFSection);CHKERRQ(ierr);
2461   ierr = DMGetLocalSection(dmc, &csection);CHKERRQ(ierr);
2462   ierr = DMGetGlobalSection(dmc, &globalCSection);CHKERRQ(ierr);
2463   ierr = DMPlexGetHeightStratum(dmf, 0, &cStart, &cEnd);CHKERRQ(ierr);
2464   ierr = PetscDSGetTotalDimension(prob, &totDim);CHKERRQ(ierr);
2465   ierr = PetscMalloc1(totDim, &elemMat);CHKERRQ(ierr);
2466 
2467   ierr = MatGetLocalSize(In, &locRows, NULL);CHKERRQ(ierr);
2468   ierr = PetscLayoutCreate(PetscObjectComm((PetscObject) In), &rLayout);CHKERRQ(ierr);
2469   ierr = PetscLayoutSetLocalSize(rLayout, locRows);CHKERRQ(ierr);
2470   ierr = PetscLayoutSetBlockSize(rLayout, 1);CHKERRQ(ierr);
2471   ierr = PetscLayoutSetUp(rLayout);CHKERRQ(ierr);
2472   ierr = PetscLayoutGetRange(rLayout, &rStart, &rEnd);CHKERRQ(ierr);
2473   ierr = PetscLayoutDestroy(&rLayout);CHKERRQ(ierr);
2474   ierr = PetscCalloc2(locRows,&dnz,locRows,&onz);CHKERRQ(ierr);
2475   ierr = PetscHSetIJCreate(&ht);CHKERRQ(ierr);
2476   for (field = 0; field < Nf; ++field) {
2477     PetscObject      obj;
2478     PetscClassId     id;
2479     PetscDualSpace   Q = NULL;
2480     PetscQuadrature  f;
2481     const PetscReal *qpoints;
2482     PetscInt         Nc, Np, fpdim, i, d;
2483 
2484     ierr = PetscDSGetDiscretization(prob, field, &obj);CHKERRQ(ierr);
2485     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
2486     if (id == PETSCFE_CLASSID) {
2487       PetscFE fe = (PetscFE) obj;
2488 
2489       ierr = PetscFEGetDualSpace(fe, &Q);CHKERRQ(ierr);
2490       ierr = PetscFEGetNumComponents(fe, &Nc);CHKERRQ(ierr);
2491     } else if (id == PETSCFV_CLASSID) {
2492       PetscFV fv = (PetscFV) obj;
2493 
2494       ierr = PetscFVGetDualSpace(fv, &Q);CHKERRQ(ierr);
2495       Nc   = 1;
2496     }
2497     ierr = PetscDualSpaceGetDimension(Q, &fpdim);CHKERRQ(ierr);
2498     /* For each fine grid cell */
2499     for (cell = cStart; cell < cEnd; ++cell) {
2500       PetscInt *findices,   *cindices;
2501       PetscInt  numFIndices, numCIndices;
2502 
2503       ierr = DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, &numFIndices, &findices, NULL);CHKERRQ(ierr);
2504       ierr = DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ);CHKERRQ(ierr);
2505       if (numFIndices != fpdim) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fine indices %D != %D dual basis vecs", numFIndices, fpdim);
2506       for (i = 0; i < fpdim; ++i) {
2507         Vec             pointVec;
2508         PetscScalar    *pV;
2509         PetscSF         coarseCellSF = NULL;
2510         const PetscSFNode *coarseCells;
2511         PetscInt        numCoarseCells, q, c;
2512 
2513         /* Get points from the dual basis functional quadrature */
2514         ierr = PetscDualSpaceGetFunctional(Q, i, &f);CHKERRQ(ierr);
2515         ierr = PetscQuadratureGetData(f, NULL, NULL, &Np, &qpoints, NULL);CHKERRQ(ierr);
2516         ierr = VecCreateSeq(PETSC_COMM_SELF, Np*dim, &pointVec);CHKERRQ(ierr);
2517         ierr = VecSetBlockSize(pointVec, dim);CHKERRQ(ierr);
2518         ierr = VecGetArray(pointVec, &pV);CHKERRQ(ierr);
2519         for (q = 0; q < Np; ++q) {
2520           const PetscReal xi0[3] = {-1., -1., -1.};
2521 
2522           /* Transform point to real space */
2523           CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q*dim], x);
2524           for (d = 0; d < dim; ++d) pV[q*dim+d] = x[d];
2525         }
2526         ierr = VecRestoreArray(pointVec, &pV);CHKERRQ(ierr);
2527         /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
2528         /* OPT: Pack all quad points from fine cell */
2529         ierr = DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF);CHKERRQ(ierr);
2530         ierr = PetscSFViewFromOptions(coarseCellSF, NULL, "-interp_sf_view");CHKERRQ(ierr);
2531         /* Update preallocation info */
2532         ierr = PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells);CHKERRQ(ierr);
2533         if (numCoarseCells != Np) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_PLIB,"Not all closure points located");
2534         {
2535           PetscHashIJKey key;
2536           PetscBool      missing;
2537 
2538           key.i = findices[i];
2539           if (key.i >= 0) {
2540             /* Get indices for coarse elements */
2541             for (ccell = 0; ccell < numCoarseCells; ++ccell) {
2542               ierr = DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, &numCIndices, &cindices, NULL);CHKERRQ(ierr);
2543               for (c = 0; c < numCIndices; ++c) {
2544                 key.j = cindices[c];
2545                 if (key.j < 0) continue;
2546                 ierr = PetscHSetIJQueryAdd(ht, key, &missing);CHKERRQ(ierr);
2547                 if (missing) {
2548                   if ((key.j >= rStart) && (key.j < rEnd)) ++dnz[key.i-rStart];
2549                   else                                     ++onz[key.i-rStart];
2550                 }
2551               }
2552               ierr = DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, &numCIndices, &cindices, NULL);CHKERRQ(ierr);
2553             }
2554           }
2555         }
2556         ierr = PetscSFDestroy(&coarseCellSF);CHKERRQ(ierr);
2557         ierr = VecDestroy(&pointVec);CHKERRQ(ierr);
2558       }
2559       ierr = DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, &numFIndices, &findices, NULL);CHKERRQ(ierr);
2560     }
2561   }
2562   ierr = PetscHSetIJDestroy(&ht);CHKERRQ(ierr);
2563   ierr = MatXAIJSetPreallocation(In, 1, dnz, onz, NULL, NULL);CHKERRQ(ierr);
2564   ierr = MatSetOption(In, MAT_NEW_NONZERO_ALLOCATION_ERR,PETSC_TRUE);CHKERRQ(ierr);
2565   ierr = PetscFree2(dnz,onz);CHKERRQ(ierr);
2566   for (field = 0; field < Nf; ++field) {
2567     PetscObject      obj;
2568     PetscClassId     id;
2569     PetscDualSpace   Q = NULL;
2570     PetscQuadrature  f;
2571     const PetscReal *qpoints, *qweights;
2572     PetscInt         Nc, qNc, Np, fpdim, i, d;
2573 
2574     ierr = PetscDSGetDiscretization(prob, field, &obj);CHKERRQ(ierr);
2575     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
2576     if (id == PETSCFE_CLASSID) {
2577       PetscFE fe = (PetscFE) obj;
2578 
2579       ierr = PetscFEGetDualSpace(fe, &Q);CHKERRQ(ierr);
2580       ierr = PetscFEGetNumComponents(fe, &Nc);CHKERRQ(ierr);
2581     } else if (id == PETSCFV_CLASSID) {
2582       PetscFV fv = (PetscFV) obj;
2583 
2584       ierr = PetscFVGetDualSpace(fv, &Q);CHKERRQ(ierr);
2585       Nc   = 1;
2586     } else SETERRQ1(PetscObjectComm((PetscObject)dmc),PETSC_ERR_ARG_WRONG,"Unknown discretization type for field %D",field);
2587     ierr = PetscDualSpaceGetDimension(Q, &fpdim);CHKERRQ(ierr);
2588     /* For each fine grid cell */
2589     for (cell = cStart; cell < cEnd; ++cell) {
2590       PetscInt *findices,   *cindices;
2591       PetscInt  numFIndices, numCIndices;
2592 
2593       ierr = DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, &numFIndices, &findices, NULL);CHKERRQ(ierr);
2594       ierr = DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ);CHKERRQ(ierr);
2595       if (numFIndices != fpdim) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fine indices %D != %D dual basis vecs", numFIndices, fpdim);
2596       for (i = 0; i < fpdim; ++i) {
2597         Vec             pointVec;
2598         PetscScalar    *pV;
2599         PetscSF         coarseCellSF = NULL;
2600         const PetscSFNode *coarseCells;
2601         PetscInt        numCoarseCells, cpdim, q, c, j;
2602 
2603         /* Get points from the dual basis functional quadrature */
2604         ierr = PetscDualSpaceGetFunctional(Q, i, &f);CHKERRQ(ierr);
2605         ierr = PetscQuadratureGetData(f, NULL, &qNc, &Np, &qpoints, &qweights);CHKERRQ(ierr);
2606         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);
2607         ierr = VecCreateSeq(PETSC_COMM_SELF, Np*dim, &pointVec);CHKERRQ(ierr);
2608         ierr = VecSetBlockSize(pointVec, dim);CHKERRQ(ierr);
2609         ierr = VecGetArray(pointVec, &pV);CHKERRQ(ierr);
2610         for (q = 0; q < Np; ++q) {
2611           const PetscReal xi0[3] = {-1., -1., -1.};
2612 
2613           /* Transform point to real space */
2614           CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q*dim], x);
2615           for (d = 0; d < dim; ++d) pV[q*dim+d] = x[d];
2616         }
2617         ierr = VecRestoreArray(pointVec, &pV);CHKERRQ(ierr);
2618         /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
2619         /* OPT: Read this out from preallocation information */
2620         ierr = DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF);CHKERRQ(ierr);
2621         /* Update preallocation info */
2622         ierr = PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells);CHKERRQ(ierr);
2623         if (numCoarseCells != Np) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_PLIB,"Not all closure points located");
2624         ierr = VecGetArray(pointVec, &pV);CHKERRQ(ierr);
2625         for (ccell = 0; ccell < numCoarseCells; ++ccell) {
2626           PetscReal pVReal[3];
2627           const PetscReal xi0[3] = {-1., -1., -1.};
2628 
2629           ierr = DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, &numCIndices, &cindices, NULL);CHKERRQ(ierr);
2630           /* Transform points from real space to coarse reference space */
2631           ierr = DMPlexComputeCellGeometryFEM(dmc, coarseCells[ccell].index, NULL, v0c, Jc, invJc, &detJc);CHKERRQ(ierr);
2632           for (d = 0; d < dim; ++d) pVReal[d] = PetscRealPart(pV[ccell*dim+d]);
2633           CoordinatesRealToRef(dim, dim, xi0, v0c, invJc, pVReal, x);
2634 
2635           if (id == PETSCFE_CLASSID) {
2636             PetscFE    fe = (PetscFE) obj;
2637             PetscReal *B;
2638 
2639             /* Evaluate coarse basis on contained point */
2640             ierr = PetscFEGetDimension(fe, &cpdim);CHKERRQ(ierr);
2641             ierr = PetscFEGetTabulation(fe, 1, x, &B, NULL, NULL);CHKERRQ(ierr);
2642             ierr = PetscArrayzero(elemMat, cpdim);CHKERRQ(ierr);
2643             /* Get elemMat entries by multiplying by weight */
2644             for (j = 0; j < cpdim; ++j) {
2645               for (c = 0; c < Nc; ++c) elemMat[j] += B[j*Nc + c]*qweights[ccell*qNc + c];
2646             }
2647             ierr = PetscFERestoreTabulation(fe, 1, x, &B, NULL, NULL);CHKERRQ(ierr);CHKERRQ(ierr);
2648           } else {
2649             cpdim = 1;
2650             for (j = 0; j < cpdim; ++j) {
2651               for (c = 0; c < Nc; ++c) elemMat[j] += 1.0*qweights[ccell*qNc + c];
2652             }
2653           }
2654           /* Update interpolator */
2655           if (mesh->printFEM > 1) {ierr = DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat);CHKERRQ(ierr);}
2656           if (numCIndices != cpdim) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %D != %D", numCIndices, cpdim);
2657           ierr = MatSetValues(In, 1, &findices[i], numCIndices, cindices, elemMat, INSERT_VALUES);CHKERRQ(ierr);
2658           ierr = DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, &numCIndices, &cindices, NULL);CHKERRQ(ierr);
2659         }
2660         ierr = VecRestoreArray(pointVec, &pV);CHKERRQ(ierr);
2661         ierr = PetscSFDestroy(&coarseCellSF);CHKERRQ(ierr);
2662         ierr = VecDestroy(&pointVec);CHKERRQ(ierr);
2663       }
2664       ierr = DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, &numFIndices, &findices, NULL);CHKERRQ(ierr);
2665     }
2666   }
2667   ierr = PetscFree3(v0,J,invJ);CHKERRQ(ierr);
2668   ierr = PetscFree3(v0c,Jc,invJc);CHKERRQ(ierr);
2669   ierr = PetscFree(elemMat);CHKERRQ(ierr);
2670   ierr = MatAssemblyBegin(In, MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
2671   ierr = MatAssemblyEnd(In, MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
2672   ierr = PetscLogEventEnd(DMPLEX_InterpolatorFEM,dmc,dmf,0,0);CHKERRQ(ierr);
2673   PetscFunctionReturn(0);
2674 }
2675 
2676 /*@
2677   DMPlexComputeMassMatrixGeneral - Form the local portion of the mass matrix M from the coarse DM to a non-nested fine DM.
2678 
2679   Input Parameters:
2680 + dmf  - The fine mesh
2681 . dmc  - The coarse mesh
2682 - user - The user context
2683 
2684   Output Parameter:
2685 . mass  - The mass matrix
2686 
2687   Level: developer
2688 
2689 .seealso: DMPlexComputeMassMatrixNested(), DMPlexComputeInterpolatorNested(), DMPlexComputeInterpolatorGeneral(), DMPlexComputeJacobianFEM()
2690 @*/
2691 PetscErrorCode DMPlexComputeMassMatrixGeneral(DM dmc, DM dmf, Mat mass, void *user)
2692 {
2693   DM_Plex       *mesh = (DM_Plex *) dmf->data;
2694   const char    *name = "Mass Matrix";
2695   PetscDS        prob;
2696   PetscSection   fsection, csection, globalFSection, globalCSection;
2697   PetscHSetIJ    ht;
2698   PetscLayout    rLayout;
2699   PetscInt      *dnz, *onz;
2700   PetscInt       locRows, rStart, rEnd;
2701   PetscReal     *x, *v0, *J, *invJ, detJ;
2702   PetscReal     *v0c, *Jc, *invJc, detJc;
2703   PetscScalar   *elemMat;
2704   PetscInt       dim, Nf, field, totDim, cStart, cEnd, cell, ccell;
2705   PetscErrorCode ierr;
2706 
2707   PetscFunctionBegin;
2708   ierr = DMGetCoordinateDim(dmc, &dim);CHKERRQ(ierr);
2709   ierr = DMGetDS(dmc, &prob);CHKERRQ(ierr);
2710   ierr = PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL);CHKERRQ(ierr);
2711   ierr = PetscDSGetNumFields(prob, &Nf);CHKERRQ(ierr);
2712   ierr = PetscMalloc3(dim,&v0,dim*dim,&J,dim*dim,&invJ);CHKERRQ(ierr);
2713   ierr = PetscMalloc3(dim,&v0c,dim*dim,&Jc,dim*dim,&invJc);CHKERRQ(ierr);
2714   ierr = DMGetLocalSection(dmf, &fsection);CHKERRQ(ierr);
2715   ierr = DMGetGlobalSection(dmf, &globalFSection);CHKERRQ(ierr);
2716   ierr = DMGetLocalSection(dmc, &csection);CHKERRQ(ierr);
2717   ierr = DMGetGlobalSection(dmc, &globalCSection);CHKERRQ(ierr);
2718   ierr = DMPlexGetHeightStratum(dmf, 0, &cStart, &cEnd);CHKERRQ(ierr);
2719   ierr = PetscDSGetTotalDimension(prob, &totDim);CHKERRQ(ierr);
2720   ierr = PetscMalloc1(totDim, &elemMat);CHKERRQ(ierr);
2721 
2722   ierr = MatGetLocalSize(mass, &locRows, NULL);CHKERRQ(ierr);
2723   ierr = PetscLayoutCreate(PetscObjectComm((PetscObject) mass), &rLayout);CHKERRQ(ierr);
2724   ierr = PetscLayoutSetLocalSize(rLayout, locRows);CHKERRQ(ierr);
2725   ierr = PetscLayoutSetBlockSize(rLayout, 1);CHKERRQ(ierr);
2726   ierr = PetscLayoutSetUp(rLayout);CHKERRQ(ierr);
2727   ierr = PetscLayoutGetRange(rLayout, &rStart, &rEnd);CHKERRQ(ierr);
2728   ierr = PetscLayoutDestroy(&rLayout);CHKERRQ(ierr);
2729   ierr = PetscCalloc2(locRows,&dnz,locRows,&onz);CHKERRQ(ierr);
2730   ierr = PetscHSetIJCreate(&ht);CHKERRQ(ierr);
2731   for (field = 0; field < Nf; ++field) {
2732     PetscObject      obj;
2733     PetscClassId     id;
2734     PetscQuadrature  quad;
2735     const PetscReal *qpoints;
2736     PetscInt         Nq, Nc, i, d;
2737 
2738     ierr = PetscDSGetDiscretization(prob, field, &obj);CHKERRQ(ierr);
2739     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
2740     if (id == PETSCFE_CLASSID) {ierr = PetscFEGetQuadrature((PetscFE) obj, &quad);CHKERRQ(ierr);}
2741     else                       {ierr = PetscFVGetQuadrature((PetscFV) obj, &quad);CHKERRQ(ierr);}
2742     ierr = PetscQuadratureGetData(quad, NULL, &Nc, &Nq, &qpoints, NULL);CHKERRQ(ierr);
2743     /* For each fine grid cell */
2744     for (cell = cStart; cell < cEnd; ++cell) {
2745       Vec                pointVec;
2746       PetscScalar       *pV;
2747       PetscSF            coarseCellSF = NULL;
2748       const PetscSFNode *coarseCells;
2749       PetscInt           numCoarseCells, q, c;
2750       PetscInt          *findices,   *cindices;
2751       PetscInt           numFIndices, numCIndices;
2752 
2753       ierr = DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, &numFIndices, &findices, NULL);CHKERRQ(ierr);
2754       ierr = DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ);CHKERRQ(ierr);
2755       /* Get points from the quadrature */
2756       ierr = VecCreateSeq(PETSC_COMM_SELF, Nq*dim, &pointVec);CHKERRQ(ierr);
2757       ierr = VecSetBlockSize(pointVec, dim);CHKERRQ(ierr);
2758       ierr = VecGetArray(pointVec, &pV);CHKERRQ(ierr);
2759       for (q = 0; q < Nq; ++q) {
2760         const PetscReal xi0[3] = {-1., -1., -1.};
2761 
2762         /* Transform point to real space */
2763         CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q*dim], x);
2764         for (d = 0; d < dim; ++d) pV[q*dim+d] = x[d];
2765       }
2766       ierr = VecRestoreArray(pointVec, &pV);CHKERRQ(ierr);
2767       /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
2768       ierr = DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF);CHKERRQ(ierr);
2769       ierr = PetscSFViewFromOptions(coarseCellSF, NULL, "-interp_sf_view");CHKERRQ(ierr);
2770       /* Update preallocation info */
2771       ierr = PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells);CHKERRQ(ierr);
2772       if (numCoarseCells != Nq) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_PLIB,"Not all closure points located");
2773       {
2774         PetscHashIJKey key;
2775         PetscBool      missing;
2776 
2777         for (i = 0; i < numFIndices; ++i) {
2778           key.i = findices[i];
2779           if (key.i >= 0) {
2780             /* Get indices for coarse elements */
2781             for (ccell = 0; ccell < numCoarseCells; ++ccell) {
2782               ierr = DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, &numCIndices, &cindices, NULL);CHKERRQ(ierr);
2783               for (c = 0; c < numCIndices; ++c) {
2784                 key.j = cindices[c];
2785                 if (key.j < 0) continue;
2786                 ierr = PetscHSetIJQueryAdd(ht, key, &missing);CHKERRQ(ierr);
2787                 if (missing) {
2788                   if ((key.j >= rStart) && (key.j < rEnd)) ++dnz[key.i-rStart];
2789                   else                                     ++onz[key.i-rStart];
2790                 }
2791               }
2792               ierr = DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, &numCIndices, &cindices, NULL);CHKERRQ(ierr);
2793             }
2794           }
2795         }
2796       }
2797       ierr = PetscSFDestroy(&coarseCellSF);CHKERRQ(ierr);
2798       ierr = VecDestroy(&pointVec);CHKERRQ(ierr);
2799       ierr = DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, &numFIndices, &findices, NULL);CHKERRQ(ierr);
2800     }
2801   }
2802   ierr = PetscHSetIJDestroy(&ht);CHKERRQ(ierr);
2803   ierr = MatXAIJSetPreallocation(mass, 1, dnz, onz, NULL, NULL);CHKERRQ(ierr);
2804   ierr = MatSetOption(mass, MAT_NEW_NONZERO_ALLOCATION_ERR,PETSC_TRUE);CHKERRQ(ierr);
2805   ierr = PetscFree2(dnz,onz);CHKERRQ(ierr);
2806   for (field = 0; field < Nf; ++field) {
2807     PetscObject      obj;
2808     PetscClassId     id;
2809     PetscQuadrature  quad;
2810     PetscReal       *Bfine;
2811     const PetscReal *qpoints, *qweights;
2812     PetscInt         Nq, Nc, i, d;
2813 
2814     ierr = PetscDSGetDiscretization(prob, field, &obj);CHKERRQ(ierr);
2815     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
2816     if (id == PETSCFE_CLASSID) {ierr = PetscFEGetQuadrature((PetscFE) obj, &quad);CHKERRQ(ierr);ierr = PetscFEGetDefaultTabulation((PetscFE) obj, &Bfine, NULL, NULL);CHKERRQ(ierr);}
2817     else                       {ierr = PetscFVGetQuadrature((PetscFV) obj, &quad);CHKERRQ(ierr);}
2818     ierr = PetscQuadratureGetData(quad, NULL, &Nc, &Nq, &qpoints, &qweights);CHKERRQ(ierr);
2819     /* For each fine grid cell */
2820     for (cell = cStart; cell < cEnd; ++cell) {
2821       Vec                pointVec;
2822       PetscScalar       *pV;
2823       PetscSF            coarseCellSF = NULL;
2824       const PetscSFNode *coarseCells;
2825       PetscInt           numCoarseCells, cpdim, q, c, j;
2826       PetscInt          *findices,   *cindices;
2827       PetscInt           numFIndices, numCIndices;
2828 
2829       ierr = DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, &numFIndices, &findices, NULL);CHKERRQ(ierr);
2830       ierr = DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ);CHKERRQ(ierr);
2831       /* Get points from the quadrature */
2832       ierr = VecCreateSeq(PETSC_COMM_SELF, Nq*dim, &pointVec);CHKERRQ(ierr);
2833       ierr = VecSetBlockSize(pointVec, dim);CHKERRQ(ierr);
2834       ierr = VecGetArray(pointVec, &pV);CHKERRQ(ierr);
2835       for (q = 0; q < Nq; ++q) {
2836         const PetscReal xi0[3] = {-1., -1., -1.};
2837 
2838         /* Transform point to real space */
2839         CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q*dim], x);
2840         for (d = 0; d < dim; ++d) pV[q*dim+d] = x[d];
2841       }
2842       ierr = VecRestoreArray(pointVec, &pV);CHKERRQ(ierr);
2843       /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
2844       ierr = DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF);CHKERRQ(ierr);
2845       /* Update matrix */
2846       ierr = PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells);CHKERRQ(ierr);
2847       if (numCoarseCells != Nq) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_PLIB,"Not all closure points located");
2848       ierr = VecGetArray(pointVec, &pV);CHKERRQ(ierr);
2849       for (ccell = 0; ccell < numCoarseCells; ++ccell) {
2850         PetscReal pVReal[3];
2851         const PetscReal xi0[3] = {-1., -1., -1.};
2852 
2853 
2854         ierr = DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, &numCIndices, &cindices, NULL);CHKERRQ(ierr);
2855         /* Transform points from real space to coarse reference space */
2856         ierr = DMPlexComputeCellGeometryFEM(dmc, coarseCells[ccell].index, NULL, v0c, Jc, invJc, &detJc);CHKERRQ(ierr);
2857         for (d = 0; d < dim; ++d) pVReal[d] = PetscRealPart(pV[ccell*dim+d]);
2858         CoordinatesRealToRef(dim, dim, xi0, v0c, invJc, pVReal, x);
2859 
2860         if (id == PETSCFE_CLASSID) {
2861           PetscFE    fe = (PetscFE) obj;
2862           PetscReal *B;
2863 
2864           /* Evaluate coarse basis on contained point */
2865           ierr = PetscFEGetDimension(fe, &cpdim);CHKERRQ(ierr);
2866           ierr = PetscFEGetTabulation(fe, 1, x, &B, NULL, NULL);CHKERRQ(ierr);
2867           /* Get elemMat entries by multiplying by weight */
2868           for (i = 0; i < numFIndices; ++i) {
2869             ierr = PetscArrayzero(elemMat, cpdim);CHKERRQ(ierr);
2870             for (j = 0; j < cpdim; ++j) {
2871               for (c = 0; c < Nc; ++c) elemMat[j] += B[j*Nc + c]*Bfine[(ccell*numFIndices + i)*Nc + c]*qweights[ccell*Nc + c]*detJ;
2872             }
2873             /* Update interpolator */
2874             if (mesh->printFEM > 1) {ierr = DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat);CHKERRQ(ierr);}
2875             if (numCIndices != cpdim) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %D != %D", numCIndices, cpdim);
2876             ierr = MatSetValues(mass, 1, &findices[i], numCIndices, cindices, elemMat, ADD_VALUES);CHKERRQ(ierr);
2877           }
2878           ierr = PetscFERestoreTabulation(fe, 1, x, &B, NULL, NULL);CHKERRQ(ierr);CHKERRQ(ierr);
2879         } else {
2880           cpdim = 1;
2881           for (i = 0; i < numFIndices; ++i) {
2882             ierr = PetscArrayzero(elemMat, cpdim);CHKERRQ(ierr);
2883             for (j = 0; j < cpdim; ++j) {
2884               for (c = 0; c < Nc; ++c) elemMat[j] += 1.0*1.0*qweights[ccell*Nc + c]*detJ;
2885             }
2886             /* Update interpolator */
2887             if (mesh->printFEM > 1) {ierr = DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat);CHKERRQ(ierr);}
2888             ierr = PetscPrintf(PETSC_COMM_SELF, "Nq: %D %D Nf: %D %D Nc: %D %D\n", ccell, Nq, i, numFIndices, j, numCIndices);CHKERRQ(ierr);
2889             if (numCIndices != cpdim) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %D != %D", numCIndices, cpdim);
2890             ierr = MatSetValues(mass, 1, &findices[i], numCIndices, cindices, elemMat, ADD_VALUES);CHKERRQ(ierr);
2891           }
2892         }
2893         ierr = DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, &numCIndices, &cindices, NULL);CHKERRQ(ierr);
2894       }
2895       ierr = VecRestoreArray(pointVec, &pV);CHKERRQ(ierr);
2896       ierr = PetscSFDestroy(&coarseCellSF);CHKERRQ(ierr);
2897       ierr = VecDestroy(&pointVec);CHKERRQ(ierr);
2898       ierr = DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, &numFIndices, &findices, NULL);CHKERRQ(ierr);
2899     }
2900   }
2901   ierr = PetscFree3(v0,J,invJ);CHKERRQ(ierr);
2902   ierr = PetscFree3(v0c,Jc,invJc);CHKERRQ(ierr);
2903   ierr = PetscFree(elemMat);CHKERRQ(ierr);
2904   ierr = MatAssemblyBegin(mass, MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
2905   ierr = MatAssemblyEnd(mass, MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
2906   PetscFunctionReturn(0);
2907 }
2908 
2909 /*@
2910   DMPlexComputeInjectorFEM - Compute a mapping from coarse unknowns to fine unknowns
2911 
2912   Input Parameters:
2913 + dmc  - The coarse mesh
2914 - dmf  - The fine mesh
2915 - user - The user context
2916 
2917   Output Parameter:
2918 . sc   - The mapping
2919 
2920   Level: developer
2921 
2922 .seealso: DMPlexComputeInterpolatorNested(), DMPlexComputeJacobianFEM()
2923 @*/
2924 PetscErrorCode DMPlexComputeInjectorFEM(DM dmc, DM dmf, VecScatter *sc, void *user)
2925 {
2926   PetscDS        prob;
2927   PetscFE       *feRef;
2928   PetscFV       *fvRef;
2929   Vec            fv, cv;
2930   IS             fis, cis;
2931   PetscSection   fsection, fglobalSection, csection, cglobalSection;
2932   PetscInt      *cmap, *cellCIndices, *cellFIndices, *cindices, *findices;
2933   PetscInt       cTotDim, fTotDim = 0, Nf, f, field, cStart, cEnd, c, dim, d, startC, endC, offsetC, offsetF, m;
2934   PetscBool     *needAvg;
2935   PetscErrorCode ierr;
2936 
2937   PetscFunctionBegin;
2938   ierr = PetscLogEventBegin(DMPLEX_InjectorFEM,dmc,dmf,0,0);CHKERRQ(ierr);
2939   ierr = DMGetDimension(dmf, &dim);CHKERRQ(ierr);
2940   ierr = DMGetLocalSection(dmf, &fsection);CHKERRQ(ierr);
2941   ierr = DMGetGlobalSection(dmf, &fglobalSection);CHKERRQ(ierr);
2942   ierr = DMGetLocalSection(dmc, &csection);CHKERRQ(ierr);
2943   ierr = DMGetGlobalSection(dmc, &cglobalSection);CHKERRQ(ierr);
2944   ierr = PetscSectionGetNumFields(fsection, &Nf);CHKERRQ(ierr);
2945   ierr = DMPlexGetInteriorCellStratum(dmc, &cStart, &cEnd);CHKERRQ(ierr);
2946   ierr = DMGetDS(dmc, &prob);CHKERRQ(ierr);
2947   ierr = PetscCalloc3(Nf,&feRef,Nf,&fvRef,Nf,&needAvg);CHKERRQ(ierr);
2948   for (f = 0; f < Nf; ++f) {
2949     PetscObject  obj;
2950     PetscClassId id;
2951     PetscInt     fNb = 0, Nc = 0;
2952 
2953     ierr = PetscDSGetDiscretization(prob, f, &obj);CHKERRQ(ierr);
2954     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
2955     if (id == PETSCFE_CLASSID) {
2956       PetscFE    fe = (PetscFE) obj;
2957       PetscSpace sp;
2958       PetscInt   maxDegree;
2959 
2960       ierr = PetscFERefine(fe, &feRef[f]);CHKERRQ(ierr);
2961       ierr = PetscFEGetDimension(feRef[f], &fNb);CHKERRQ(ierr);
2962       ierr = PetscFEGetNumComponents(fe, &Nc);CHKERRQ(ierr);
2963       ierr = PetscFEGetBasisSpace(fe, &sp);CHKERRQ(ierr);
2964       ierr = PetscSpaceGetDegree(sp, NULL, &maxDegree);CHKERRQ(ierr);
2965       if (!maxDegree) needAvg[f] = PETSC_TRUE;
2966     } else if (id == PETSCFV_CLASSID) {
2967       PetscFV        fv = (PetscFV) obj;
2968       PetscDualSpace Q;
2969 
2970       ierr = PetscFVRefine(fv, &fvRef[f]);CHKERRQ(ierr);
2971       ierr = PetscFVGetDualSpace(fvRef[f], &Q);CHKERRQ(ierr);
2972       ierr = PetscDualSpaceGetDimension(Q, &fNb);CHKERRQ(ierr);
2973       ierr = PetscFVGetNumComponents(fv, &Nc);CHKERRQ(ierr);
2974       needAvg[f] = PETSC_TRUE;
2975     }
2976     fTotDim += fNb;
2977   }
2978   ierr = PetscDSGetTotalDimension(prob, &cTotDim);CHKERRQ(ierr);
2979   ierr = PetscMalloc1(cTotDim,&cmap);CHKERRQ(ierr);
2980   for (field = 0, offsetC = 0, offsetF = 0; field < Nf; ++field) {
2981     PetscFE        feC;
2982     PetscFV        fvC;
2983     PetscDualSpace QF, QC;
2984     PetscInt       order = -1, NcF, NcC, fpdim, cpdim;
2985 
2986     if (feRef[field]) {
2987       ierr = PetscDSGetDiscretization(prob, field, (PetscObject *) &feC);CHKERRQ(ierr);
2988       ierr = PetscFEGetNumComponents(feC, &NcC);CHKERRQ(ierr);
2989       ierr = PetscFEGetNumComponents(feRef[field], &NcF);CHKERRQ(ierr);
2990       ierr = PetscFEGetDualSpace(feRef[field], &QF);CHKERRQ(ierr);
2991       ierr = PetscDualSpaceGetOrder(QF, &order);CHKERRQ(ierr);
2992       ierr = PetscDualSpaceGetDimension(QF, &fpdim);CHKERRQ(ierr);
2993       ierr = PetscFEGetDualSpace(feC, &QC);CHKERRQ(ierr);
2994       ierr = PetscDualSpaceGetDimension(QC, &cpdim);CHKERRQ(ierr);
2995     } else {
2996       ierr = PetscDSGetDiscretization(prob, field, (PetscObject *) &fvC);CHKERRQ(ierr);
2997       ierr = PetscFVGetNumComponents(fvC, &NcC);CHKERRQ(ierr);
2998       ierr = PetscFVGetNumComponents(fvRef[field], &NcF);CHKERRQ(ierr);
2999       ierr = PetscFVGetDualSpace(fvRef[field], &QF);CHKERRQ(ierr);
3000       ierr = PetscDualSpaceGetDimension(QF, &fpdim);CHKERRQ(ierr);
3001       ierr = PetscFVGetDualSpace(fvC, &QC);CHKERRQ(ierr);
3002       ierr = PetscDualSpaceGetDimension(QC, &cpdim);CHKERRQ(ierr);
3003     }
3004     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);
3005     for (c = 0; c < cpdim; ++c) {
3006       PetscQuadrature  cfunc;
3007       const PetscReal *cqpoints, *cqweights;
3008       PetscInt         NqcC, NpC;
3009       PetscBool        found = PETSC_FALSE;
3010 
3011       ierr = PetscDualSpaceGetFunctional(QC, c, &cfunc);CHKERRQ(ierr);
3012       ierr = PetscQuadratureGetData(cfunc, NULL, &NqcC, &NpC, &cqpoints, &cqweights);CHKERRQ(ierr);
3013       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);
3014       if (NpC != 1 && feRef[field]) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Do not know how to do injection for moments");
3015       for (f = 0; f < fpdim; ++f) {
3016         PetscQuadrature  ffunc;
3017         const PetscReal *fqpoints, *fqweights;
3018         PetscReal        sum = 0.0;
3019         PetscInt         NqcF, NpF;
3020 
3021         ierr = PetscDualSpaceGetFunctional(QF, f, &ffunc);CHKERRQ(ierr);
3022         ierr = PetscQuadratureGetData(ffunc, NULL, &NqcF, &NpF, &fqpoints, &fqweights);CHKERRQ(ierr);
3023         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);
3024         if (NpC != NpF) continue;
3025         for (d = 0; d < dim; ++d) sum += PetscAbsReal(cqpoints[d] - fqpoints[d]);
3026         if (sum > 1.0e-9) continue;
3027         for (d = 0; d < NcC; ++d) sum += PetscAbsReal(cqweights[d]*fqweights[d]);
3028         if (sum < 1.0e-9) continue;
3029         cmap[offsetC+c] = offsetF+f;
3030         found = PETSC_TRUE;
3031         break;
3032       }
3033       if (!found) {
3034         /* TODO We really want the average here, but some asshole put VecScatter in the interface */
3035         if (fvRef[field] || (feRef[field] && order == 0)) {
3036           cmap[offsetC+c] = offsetF+0;
3037         } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Could not locate matching functional for injection");
3038       }
3039     }
3040     offsetC += cpdim;
3041     offsetF += fpdim;
3042   }
3043   for (f = 0; f < Nf; ++f) {ierr = PetscFEDestroy(&feRef[f]);CHKERRQ(ierr);ierr = PetscFVDestroy(&fvRef[f]);CHKERRQ(ierr);}
3044   ierr = PetscFree3(feRef,fvRef,needAvg);CHKERRQ(ierr);
3045 
3046   ierr = DMGetGlobalVector(dmf, &fv);CHKERRQ(ierr);
3047   ierr = DMGetGlobalVector(dmc, &cv);CHKERRQ(ierr);
3048   ierr = VecGetOwnershipRange(cv, &startC, &endC);CHKERRQ(ierr);
3049   ierr = PetscSectionGetConstrainedStorageSize(cglobalSection, &m);CHKERRQ(ierr);
3050   ierr = PetscMalloc2(cTotDim,&cellCIndices,fTotDim,&cellFIndices);CHKERRQ(ierr);
3051   ierr = PetscMalloc1(m,&cindices);CHKERRQ(ierr);
3052   ierr = PetscMalloc1(m,&findices);CHKERRQ(ierr);
3053   for (d = 0; d < m; ++d) cindices[d] = findices[d] = -1;
3054   for (c = cStart; c < cEnd; ++c) {
3055     ierr = DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, c, cellCIndices, cellFIndices);CHKERRQ(ierr);
3056     for (d = 0; d < cTotDim; ++d) {
3057       if ((cellCIndices[d] < startC) || (cellCIndices[d] >= endC)) continue;
3058       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]]);
3059       cindices[cellCIndices[d]-startC] = cellCIndices[d];
3060       findices[cellCIndices[d]-startC] = cellFIndices[cmap[d]];
3061     }
3062   }
3063   ierr = PetscFree(cmap);CHKERRQ(ierr);
3064   ierr = PetscFree2(cellCIndices,cellFIndices);CHKERRQ(ierr);
3065 
3066   ierr = ISCreateGeneral(PETSC_COMM_SELF, m, cindices, PETSC_OWN_POINTER, &cis);CHKERRQ(ierr);
3067   ierr = ISCreateGeneral(PETSC_COMM_SELF, m, findices, PETSC_OWN_POINTER, &fis);CHKERRQ(ierr);
3068   ierr = VecScatterCreate(cv, cis, fv, fis, sc);CHKERRQ(ierr);
3069   ierr = ISDestroy(&cis);CHKERRQ(ierr);
3070   ierr = ISDestroy(&fis);CHKERRQ(ierr);
3071   ierr = DMRestoreGlobalVector(dmf, &fv);CHKERRQ(ierr);
3072   ierr = DMRestoreGlobalVector(dmc, &cv);CHKERRQ(ierr);
3073   ierr = PetscLogEventEnd(DMPLEX_InjectorFEM,dmc,dmf,0,0);CHKERRQ(ierr);
3074   PetscFunctionReturn(0);
3075 }
3076 
3077 /*@C
3078   DMPlexGetCellFields - Retrieve the field values values for a chunk of cells
3079 
3080   Input Parameters:
3081 + dm     - The DM
3082 . cellIS - The cells to include
3083 . locX   - A local vector with the solution fields
3084 . locX_t - A local vector with solution field time derivatives, or NULL
3085 - locA   - A local vector with auxiliary fields, or NULL
3086 
3087   Output Parameters:
3088 + u   - The field coefficients
3089 . u_t - The fields derivative coefficients
3090 - a   - The auxiliary field coefficients
3091 
3092   Level: developer
3093 
3094 .seealso: DMPlexGetFaceFields()
3095 @*/
3096 PetscErrorCode DMPlexGetCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
3097 {
3098   DM              plex, plexA = NULL;
3099   PetscSection    section, sectionAux;
3100   PetscDS         prob;
3101   const PetscInt *cells;
3102   PetscInt        cStart, cEnd, numCells, totDim, totDimAux, c;
3103   PetscErrorCode  ierr;
3104 
3105   PetscFunctionBegin;
3106   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3107   PetscValidHeaderSpecific(locX, VEC_CLASSID, 4);
3108   if (locX_t) {PetscValidHeaderSpecific(locX_t, VEC_CLASSID, 5);}
3109   if (locA)   {PetscValidHeaderSpecific(locA, VEC_CLASSID, 6);}
3110   PetscValidPointer(u, 7);
3111   PetscValidPointer(u_t, 8);
3112   PetscValidPointer(a, 9);
3113   ierr = DMPlexConvertPlex(dm, &plex, PETSC_FALSE);CHKERRQ(ierr);
3114   ierr = ISGetPointRange(cellIS, &cStart, &cEnd, &cells);CHKERRQ(ierr);
3115   ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);
3116   ierr = DMGetCellDS(dm, cStart, &prob);CHKERRQ(ierr);
3117   ierr = PetscDSGetTotalDimension(prob, &totDim);CHKERRQ(ierr);
3118   if (locA) {
3119     DM      dmAux;
3120     PetscDS probAux;
3121 
3122     ierr = VecGetDM(locA, &dmAux);CHKERRQ(ierr);
3123     ierr = DMPlexConvertPlex(dmAux, &plexA, PETSC_FALSE);CHKERRQ(ierr);
3124     ierr = DMGetLocalSection(dmAux, &sectionAux);CHKERRQ(ierr);
3125     ierr = DMGetDS(dmAux, &probAux);CHKERRQ(ierr);
3126     ierr = PetscDSGetTotalDimension(probAux, &totDimAux);CHKERRQ(ierr);
3127   }
3128   numCells = cEnd - cStart;
3129   ierr = DMGetWorkArray(dm, numCells*totDim, MPIU_SCALAR, u);CHKERRQ(ierr);
3130   if (locX_t) {ierr = DMGetWorkArray(dm, numCells*totDim, MPIU_SCALAR, u_t);CHKERRQ(ierr);} else {*u_t = NULL;}
3131   if (locA)   {ierr = DMGetWorkArray(dm, numCells*totDimAux, MPIU_SCALAR, a);CHKERRQ(ierr);} else {*a = NULL;}
3132   for (c = cStart; c < cEnd; ++c) {
3133     const PetscInt cell = cells ? cells[c] : c;
3134     const PetscInt cind = c - cStart;
3135     PetscScalar   *x = NULL, *x_t = NULL, *ul = *u, *ul_t = *u_t, *al = *a;
3136     PetscInt       i;
3137 
3138     ierr = DMPlexVecGetClosure(plex, section, locX, cell, NULL, &x);CHKERRQ(ierr);
3139     for (i = 0; i < totDim; ++i) ul[cind*totDim+i] = x[i];
3140     ierr = DMPlexVecRestoreClosure(plex, section, locX, cell, NULL, &x);CHKERRQ(ierr);
3141     if (locX_t) {
3142       ierr = DMPlexVecGetClosure(plex, section, locX_t, cell, NULL, &x_t);CHKERRQ(ierr);
3143       for (i = 0; i < totDim; ++i) ul_t[cind*totDim+i] = x_t[i];
3144       ierr = DMPlexVecRestoreClosure(plex, section, locX_t, cell, NULL, &x_t);CHKERRQ(ierr);
3145     }
3146     if (locA) {
3147       PetscInt subcell;
3148       ierr = DMPlexGetAuxiliaryPoint(plex, plexA, cell, &subcell);CHKERRQ(ierr);
3149       ierr = DMPlexVecGetClosure(plexA, sectionAux, locA, subcell, NULL, &x);CHKERRQ(ierr);
3150       for (i = 0; i < totDimAux; ++i) al[cind*totDimAux+i] = x[i];
3151       ierr = DMPlexVecRestoreClosure(plexA, sectionAux, locA, subcell, NULL, &x);CHKERRQ(ierr);
3152     }
3153   }
3154   ierr = DMDestroy(&plex);CHKERRQ(ierr);
3155   if (locA) {ierr = DMDestroy(&plexA);CHKERRQ(ierr);}
3156   ierr = ISRestorePointRange(cellIS, &cStart, &cEnd, &cells);CHKERRQ(ierr);
3157   PetscFunctionReturn(0);
3158 }
3159 
3160 /*@C
3161   DMPlexRestoreCellFields - Restore the field values values for a chunk of cells
3162 
3163   Input Parameters:
3164 + dm     - The DM
3165 . cellIS - The cells to include
3166 . locX   - A local vector with the solution fields
3167 . locX_t - A local vector with solution field time derivatives, or NULL
3168 - locA   - A local vector with auxiliary fields, or NULL
3169 
3170   Output Parameters:
3171 + u   - The field coefficients
3172 . u_t - The fields derivative coefficients
3173 - a   - The auxiliary field coefficients
3174 
3175   Level: developer
3176 
3177 .seealso: DMPlexGetFaceFields()
3178 @*/
3179 PetscErrorCode DMPlexRestoreCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
3180 {
3181   PetscErrorCode ierr;
3182 
3183   PetscFunctionBegin;
3184   ierr = DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u);CHKERRQ(ierr);
3185   if (locX_t) {ierr = DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u_t);CHKERRQ(ierr);}
3186   if (locA)   {ierr = DMRestoreWorkArray(dm, 0, MPIU_SCALAR, a);CHKERRQ(ierr);}
3187   PetscFunctionReturn(0);
3188 }
3189 
3190 /*@C
3191   DMPlexGetFaceFields - Retrieve the field values values for a chunk of faces
3192 
3193   Input Parameters:
3194 + dm     - The DM
3195 . fStart - The first face to include
3196 . fEnd   - The first face to exclude
3197 . locX   - A local vector with the solution fields
3198 . locX_t - A local vector with solution field time derivatives, or NULL
3199 . faceGeometry - A local vector with face geometry
3200 . cellGeometry - A local vector with cell geometry
3201 - locaGrad - A local vector with field gradients, or NULL
3202 
3203   Output Parameters:
3204 + Nface - The number of faces with field values
3205 . uL - The field values at the left side of the face
3206 - uR - The field values at the right side of the face
3207 
3208   Level: developer
3209 
3210 .seealso: DMPlexGetCellFields()
3211 @*/
3212 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)
3213 {
3214   DM                 dmFace, dmCell, dmGrad = NULL;
3215   PetscSection       section;
3216   PetscDS            prob;
3217   DMLabel            ghostLabel;
3218   const PetscScalar *facegeom, *cellgeom, *x, *lgrad;
3219   PetscBool         *isFE;
3220   PetscInt           dim, Nf, f, Nc, numFaces = fEnd - fStart, iface, face;
3221   PetscErrorCode     ierr;
3222 
3223   PetscFunctionBegin;
3224   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3225   PetscValidHeaderSpecific(locX, VEC_CLASSID, 4);
3226   if (locX_t) {PetscValidHeaderSpecific(locX_t, VEC_CLASSID, 5);}
3227   PetscValidHeaderSpecific(faceGeometry, VEC_CLASSID, 6);
3228   PetscValidHeaderSpecific(cellGeometry, VEC_CLASSID, 7);
3229   if (locGrad) {PetscValidHeaderSpecific(locGrad, VEC_CLASSID, 8);}
3230   PetscValidPointer(uL, 9);
3231   PetscValidPointer(uR, 10);
3232   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
3233   ierr = DMGetDS(dm, &prob);CHKERRQ(ierr);
3234   ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);
3235   ierr = PetscDSGetNumFields(prob, &Nf);CHKERRQ(ierr);
3236   ierr = PetscDSGetTotalComponents(prob, &Nc);CHKERRQ(ierr);
3237   ierr = PetscMalloc1(Nf, &isFE);CHKERRQ(ierr);
3238   for (f = 0; f < Nf; ++f) {
3239     PetscObject  obj;
3240     PetscClassId id;
3241 
3242     ierr = PetscDSGetDiscretization(prob, f, &obj);CHKERRQ(ierr);
3243     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
3244     if (id == PETSCFE_CLASSID)      {isFE[f] = PETSC_TRUE;}
3245     else if (id == PETSCFV_CLASSID) {isFE[f] = PETSC_FALSE;}
3246     else                            {isFE[f] = PETSC_FALSE;}
3247   }
3248   ierr = DMGetLabel(dm, "ghost", &ghostLabel);CHKERRQ(ierr);
3249   ierr = VecGetArrayRead(locX, &x);CHKERRQ(ierr);
3250   ierr = VecGetDM(faceGeometry, &dmFace);CHKERRQ(ierr);
3251   ierr = VecGetArrayRead(faceGeometry, &facegeom);CHKERRQ(ierr);
3252   ierr = VecGetDM(cellGeometry, &dmCell);CHKERRQ(ierr);
3253   ierr = VecGetArrayRead(cellGeometry, &cellgeom);CHKERRQ(ierr);
3254   if (locGrad) {
3255     ierr = VecGetDM(locGrad, &dmGrad);CHKERRQ(ierr);
3256     ierr = VecGetArrayRead(locGrad, &lgrad);CHKERRQ(ierr);
3257   }
3258   ierr = DMGetWorkArray(dm, numFaces*Nc, MPIU_SCALAR, uL);CHKERRQ(ierr);
3259   ierr = DMGetWorkArray(dm, numFaces*Nc, MPIU_SCALAR, uR);CHKERRQ(ierr);
3260   /* Right now just eat the extra work for FE (could make a cell loop) */
3261   for (face = fStart, iface = 0; face < fEnd; ++face) {
3262     const PetscInt        *cells;
3263     PetscFVFaceGeom       *fg;
3264     PetscFVCellGeom       *cgL, *cgR;
3265     PetscScalar           *xL, *xR, *gL, *gR;
3266     PetscScalar           *uLl = *uL, *uRl = *uR;
3267     PetscInt               ghost, nsupp, nchild;
3268 
3269     ierr = DMLabelGetValue(ghostLabel, face, &ghost);CHKERRQ(ierr);
3270     ierr = DMPlexGetSupportSize(dm, face, &nsupp);CHKERRQ(ierr);
3271     ierr = DMPlexGetTreeChildren(dm, face, &nchild, NULL);CHKERRQ(ierr);
3272     if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
3273     ierr = DMPlexPointLocalRead(dmFace, face, facegeom, &fg);CHKERRQ(ierr);
3274     ierr = DMPlexGetSupport(dm, face, &cells);CHKERRQ(ierr);
3275     ierr = DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL);CHKERRQ(ierr);
3276     ierr = DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR);CHKERRQ(ierr);
3277     for (f = 0; f < Nf; ++f) {
3278       PetscInt off;
3279 
3280       ierr = PetscDSGetComponentOffset(prob, f, &off);CHKERRQ(ierr);
3281       if (isFE[f]) {
3282         const PetscInt *cone;
3283         PetscInt        comp, coneSizeL, coneSizeR, faceLocL, faceLocR, ldof, rdof, d;
3284 
3285         xL = xR = NULL;
3286         ierr = PetscSectionGetFieldComponents(section, f, &comp);CHKERRQ(ierr);
3287         ierr = DMPlexVecGetClosure(dm, section, locX, cells[0], &ldof, (PetscScalar **) &xL);CHKERRQ(ierr);
3288         ierr = DMPlexVecGetClosure(dm, section, locX, cells[1], &rdof, (PetscScalar **) &xR);CHKERRQ(ierr);
3289         ierr = DMPlexGetCone(dm, cells[0], &cone);CHKERRQ(ierr);
3290         ierr = DMPlexGetConeSize(dm, cells[0], &coneSizeL);CHKERRQ(ierr);
3291         for (faceLocL = 0; faceLocL < coneSizeL; ++faceLocL) if (cone[faceLocL] == face) break;
3292         ierr = DMPlexGetCone(dm, cells[1], &cone);CHKERRQ(ierr);
3293         ierr = DMPlexGetConeSize(dm, cells[1], &coneSizeR);CHKERRQ(ierr);
3294         for (faceLocR = 0; faceLocR < coneSizeR; ++faceLocR) if (cone[faceLocR] == face) break;
3295         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]);
3296         /* Check that FEM field has values in the right cell (sometimes its an FV ghost cell) */
3297         /* TODO: this is a hack that might not be right for nonconforming */
3298         if (faceLocL < coneSizeL) {
3299           ierr = PetscFEEvaluateFaceFields_Internal(prob, f, faceLocL, xL, &uLl[iface*Nc+off]);CHKERRQ(ierr);
3300           if (rdof == ldof && faceLocR < coneSizeR) {ierr = PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface*Nc+off]);CHKERRQ(ierr);}
3301           else              {for(d = 0; d < comp; ++d) uRl[iface*Nc+off+d] = uLl[iface*Nc+off+d];}
3302         }
3303         else {
3304           ierr = PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface*Nc+off]);CHKERRQ(ierr);
3305           ierr = PetscSectionGetFieldComponents(section, f, &comp);CHKERRQ(ierr);
3306           for(d = 0; d < comp; ++d) uLl[iface*Nc+off+d] = uRl[iface*Nc+off+d];
3307         }
3308         ierr = DMPlexVecRestoreClosure(dm, section, locX, cells[0], &ldof, (PetscScalar **) &xL);CHKERRQ(ierr);
3309         ierr = DMPlexVecRestoreClosure(dm, section, locX, cells[1], &rdof, (PetscScalar **) &xR);CHKERRQ(ierr);
3310       } else {
3311         PetscFV  fv;
3312         PetscInt numComp, c;
3313 
3314         ierr = PetscDSGetDiscretization(prob, f, (PetscObject *) &fv);CHKERRQ(ierr);
3315         ierr = PetscFVGetNumComponents(fv, &numComp);CHKERRQ(ierr);
3316         ierr = DMPlexPointLocalFieldRead(dm, cells[0], f, x, &xL);CHKERRQ(ierr);
3317         ierr = DMPlexPointLocalFieldRead(dm, cells[1], f, x, &xR);CHKERRQ(ierr);
3318         if (dmGrad) {
3319           PetscReal dxL[3], dxR[3];
3320 
3321           ierr = DMPlexPointLocalRead(dmGrad, cells[0], lgrad, &gL);CHKERRQ(ierr);
3322           ierr = DMPlexPointLocalRead(dmGrad, cells[1], lgrad, &gR);CHKERRQ(ierr);
3323           DMPlex_WaxpyD_Internal(dim, -1, cgL->centroid, fg->centroid, dxL);
3324           DMPlex_WaxpyD_Internal(dim, -1, cgR->centroid, fg->centroid, dxR);
3325           for (c = 0; c < numComp; ++c) {
3326             uLl[iface*Nc+off+c] = xL[c] + DMPlex_DotD_Internal(dim, &gL[c*dim], dxL);
3327             uRl[iface*Nc+off+c] = xR[c] + DMPlex_DotD_Internal(dim, &gR[c*dim], dxR);
3328           }
3329         } else {
3330           for (c = 0; c < numComp; ++c) {
3331             uLl[iface*Nc+off+c] = xL[c];
3332             uRl[iface*Nc+off+c] = xR[c];
3333           }
3334         }
3335       }
3336     }
3337     ++iface;
3338   }
3339   *Nface = iface;
3340   ierr = VecRestoreArrayRead(locX, &x);CHKERRQ(ierr);
3341   ierr = VecRestoreArrayRead(faceGeometry, &facegeom);CHKERRQ(ierr);
3342   ierr = VecRestoreArrayRead(cellGeometry, &cellgeom);CHKERRQ(ierr);
3343   if (locGrad) {
3344     ierr = VecRestoreArrayRead(locGrad, &lgrad);CHKERRQ(ierr);
3345   }
3346   ierr = PetscFree(isFE);CHKERRQ(ierr);
3347   PetscFunctionReturn(0);
3348 }
3349 
3350 /*@C
3351   DMPlexRestoreFaceFields - Restore the field values values for a chunk of faces
3352 
3353   Input Parameters:
3354 + dm     - The DM
3355 . fStart - The first face to include
3356 . fEnd   - The first face to exclude
3357 . locX   - A local vector with the solution fields
3358 . locX_t - A local vector with solution field time derivatives, or NULL
3359 . faceGeometry - A local vector with face geometry
3360 . cellGeometry - A local vector with cell geometry
3361 - locaGrad - A local vector with field gradients, or NULL
3362 
3363   Output Parameters:
3364 + Nface - The number of faces with field values
3365 . uL - The field values at the left side of the face
3366 - uR - The field values at the right side of the face
3367 
3368   Level: developer
3369 
3370 .seealso: DMPlexGetFaceFields()
3371 @*/
3372 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)
3373 {
3374   PetscErrorCode ierr;
3375 
3376   PetscFunctionBegin;
3377   ierr = DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uL);CHKERRQ(ierr);
3378   ierr = DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uR);CHKERRQ(ierr);
3379   PetscFunctionReturn(0);
3380 }
3381 
3382 /*@C
3383   DMPlexGetFaceGeometry - Retrieve the geometric values for a chunk of faces
3384 
3385   Input Parameters:
3386 + dm     - The DM
3387 . fStart - The first face to include
3388 . fEnd   - The first face to exclude
3389 . faceGeometry - A local vector with face geometry
3390 - cellGeometry - A local vector with cell geometry
3391 
3392   Output Parameters:
3393 + Nface - The number of faces with field values
3394 . fgeom - The extract the face centroid and normal
3395 - vol   - The cell volume
3396 
3397   Level: developer
3398 
3399 .seealso: DMPlexGetCellFields()
3400 @*/
3401 PetscErrorCode DMPlexGetFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom **fgeom, PetscReal **vol)
3402 {
3403   DM                 dmFace, dmCell;
3404   DMLabel            ghostLabel;
3405   const PetscScalar *facegeom, *cellgeom;
3406   PetscInt           dim, numFaces = fEnd - fStart, iface, face;
3407   PetscErrorCode     ierr;
3408 
3409   PetscFunctionBegin;
3410   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3411   PetscValidHeaderSpecific(faceGeometry, VEC_CLASSID, 4);
3412   PetscValidHeaderSpecific(cellGeometry, VEC_CLASSID, 5);
3413   PetscValidPointer(fgeom, 6);
3414   PetscValidPointer(vol, 7);
3415   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
3416   ierr = DMGetLabel(dm, "ghost", &ghostLabel);CHKERRQ(ierr);
3417   ierr = VecGetDM(faceGeometry, &dmFace);CHKERRQ(ierr);
3418   ierr = VecGetArrayRead(faceGeometry, &facegeom);CHKERRQ(ierr);
3419   ierr = VecGetDM(cellGeometry, &dmCell);CHKERRQ(ierr);
3420   ierr = VecGetArrayRead(cellGeometry, &cellgeom);CHKERRQ(ierr);
3421   ierr = PetscMalloc1(numFaces, fgeom);CHKERRQ(ierr);
3422   ierr = DMGetWorkArray(dm, numFaces*2, MPIU_SCALAR, vol);CHKERRQ(ierr);
3423   for (face = fStart, iface = 0; face < fEnd; ++face) {
3424     const PetscInt        *cells;
3425     PetscFVFaceGeom       *fg;
3426     PetscFVCellGeom       *cgL, *cgR;
3427     PetscFVFaceGeom       *fgeoml = *fgeom;
3428     PetscReal             *voll   = *vol;
3429     PetscInt               ghost, d, nchild, nsupp;
3430 
3431     ierr = DMLabelGetValue(ghostLabel, face, &ghost);CHKERRQ(ierr);
3432     ierr = DMPlexGetSupportSize(dm, face, &nsupp);CHKERRQ(ierr);
3433     ierr = DMPlexGetTreeChildren(dm, face, &nchild, NULL);CHKERRQ(ierr);
3434     if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
3435     ierr = DMPlexPointLocalRead(dmFace, face, facegeom, &fg);CHKERRQ(ierr);
3436     ierr = DMPlexGetSupport(dm, face, &cells);CHKERRQ(ierr);
3437     ierr = DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL);CHKERRQ(ierr);
3438     ierr = DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR);CHKERRQ(ierr);
3439     for (d = 0; d < dim; ++d) {
3440       fgeoml[iface].centroid[d] = fg->centroid[d];
3441       fgeoml[iface].normal[d]   = fg->normal[d];
3442     }
3443     voll[iface*2+0] = cgL->volume;
3444     voll[iface*2+1] = cgR->volume;
3445     ++iface;
3446   }
3447   *Nface = iface;
3448   ierr = VecRestoreArrayRead(faceGeometry, &facegeom);CHKERRQ(ierr);
3449   ierr = VecRestoreArrayRead(cellGeometry, &cellgeom);CHKERRQ(ierr);
3450   PetscFunctionReturn(0);
3451 }
3452 
3453 /*@C
3454   DMPlexRestoreFaceGeometry - Restore the field values values for a chunk of faces
3455 
3456   Input Parameters:
3457 + dm     - The DM
3458 . fStart - The first face to include
3459 . fEnd   - The first face to exclude
3460 . faceGeometry - A local vector with face geometry
3461 - cellGeometry - A local vector with cell geometry
3462 
3463   Output Parameters:
3464 + Nface - The number of faces with field values
3465 . fgeom - The extract the face centroid and normal
3466 - vol   - The cell volume
3467 
3468   Level: developer
3469 
3470 .seealso: DMPlexGetFaceFields()
3471 @*/
3472 PetscErrorCode DMPlexRestoreFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom **fgeom, PetscReal **vol)
3473 {
3474   PetscErrorCode ierr;
3475 
3476   PetscFunctionBegin;
3477   ierr = PetscFree(*fgeom);CHKERRQ(ierr);
3478   ierr = DMRestoreWorkArray(dm, 0, MPIU_REAL, vol);CHKERRQ(ierr);
3479   PetscFunctionReturn(0);
3480 }
3481 
3482 PetscErrorCode DMSNESGetFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
3483 {
3484   char            composeStr[33] = {0};
3485   PetscObjectId   id;
3486   PetscContainer  container;
3487   PetscErrorCode  ierr;
3488 
3489   PetscFunctionBegin;
3490   ierr = PetscObjectGetId((PetscObject)quad,&id);CHKERRQ(ierr);
3491   ierr = PetscSNPrintf(composeStr, 32, "DMSNESGetFEGeom_%x\n", id);CHKERRQ(ierr);
3492   ierr = PetscObjectQuery((PetscObject) pointIS, composeStr, (PetscObject *) &container);CHKERRQ(ierr);
3493   if (container) {
3494     ierr = PetscContainerGetPointer(container, (void **) geom);CHKERRQ(ierr);
3495   } else {
3496     ierr = DMFieldCreateFEGeom(coordField, pointIS, quad, faceData, geom);CHKERRQ(ierr);
3497     ierr = PetscContainerCreate(PETSC_COMM_SELF,&container);CHKERRQ(ierr);
3498     ierr = PetscContainerSetPointer(container, (void *) *geom);CHKERRQ(ierr);
3499     ierr = PetscContainerSetUserDestroy(container, PetscContainerUserDestroy_PetscFEGeom);CHKERRQ(ierr);
3500     ierr = PetscObjectCompose((PetscObject) pointIS, composeStr, (PetscObject) container);CHKERRQ(ierr);
3501     ierr = PetscContainerDestroy(&container);CHKERRQ(ierr);
3502   }
3503   PetscFunctionReturn(0);
3504 }
3505 
3506 PetscErrorCode DMSNESRestoreFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
3507 {
3508   PetscFunctionBegin;
3509   *geom = NULL;
3510   PetscFunctionReturn(0);
3511 }
3512 
3513 PetscErrorCode DMPlexComputeResidual_Patch_Internal(DM dm, PetscSection section, IS cellIS, PetscReal t, Vec locX, Vec locX_t, Vec locF, void *user)
3514 {
3515   DM_Plex         *mesh       = (DM_Plex *) dm->data;
3516   const char      *name       = "Residual";
3517   DM               dmAux      = NULL;
3518   DMLabel          ghostLabel = NULL;
3519   PetscDS          prob       = NULL;
3520   PetscDS          probAux    = NULL;
3521   PetscBool        useFEM     = PETSC_FALSE;
3522   PetscBool        isImplicit = (locX_t || t == PETSC_MIN_REAL) ? PETSC_TRUE : PETSC_FALSE;
3523   DMField          coordField = NULL;
3524   Vec              locA;
3525   PetscScalar     *u = NULL, *u_t, *a, *uL = NULL, *uR = NULL;
3526   IS               chunkIS;
3527   const PetscInt  *cells;
3528   PetscInt         cStart, cEnd, numCells;
3529   PetscInt         Nf, f, totDim, totDimAux, numChunks, cellChunkSize, chunk, fStart, fEnd;
3530   PetscInt         maxDegree = PETSC_MAX_INT;
3531   PetscQuadrature  affineQuad = NULL, *quads = NULL;
3532   PetscFEGeom     *affineGeom = NULL, **geoms = NULL;
3533   PetscErrorCode   ierr;
3534 
3535   PetscFunctionBegin;
3536   ierr = PetscLogEventBegin(DMPLEX_ResidualFEM,dm,0,0,0);CHKERRQ(ierr);
3537   /* FEM+FVM */
3538   /* 1: Get sizes from dm and dmAux */
3539   ierr = DMGetLabel(dm, "ghost", &ghostLabel);CHKERRQ(ierr);
3540   ierr = DMGetDS(dm, &prob);CHKERRQ(ierr);
3541   ierr = PetscDSGetNumFields(prob, &Nf);CHKERRQ(ierr);
3542   ierr = PetscDSGetTotalDimension(prob, &totDim);CHKERRQ(ierr);
3543   ierr = PetscObjectQuery((PetscObject) dm, "A", (PetscObject *) &locA);CHKERRQ(ierr);
3544   if (locA) {
3545     ierr = VecGetDM(locA, &dmAux);CHKERRQ(ierr);
3546     ierr = DMGetDS(dmAux, &probAux);CHKERRQ(ierr);
3547     ierr = PetscDSGetTotalDimension(probAux, &totDimAux);CHKERRQ(ierr);
3548   }
3549   /* 2: Get geometric data */
3550   for (f = 0; f < Nf; ++f) {
3551     PetscObject  obj;
3552     PetscClassId id;
3553     PetscBool    fimp;
3554 
3555     ierr = PetscDSGetImplicit(prob, f, &fimp);CHKERRQ(ierr);
3556     if (isImplicit != fimp) continue;
3557     ierr = PetscDSGetDiscretization(prob, f, &obj);CHKERRQ(ierr);
3558     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
3559     if (id == PETSCFE_CLASSID) {useFEM = PETSC_TRUE;}
3560     if (id == PETSCFV_CLASSID) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONG,"Use of FVM with PCPATCH not yet implemented");
3561   }
3562   if (useFEM) {
3563     ierr = DMGetCoordinateField(dm, &coordField);CHKERRQ(ierr);
3564     ierr = DMFieldGetDegree(coordField,cellIS,NULL,&maxDegree);CHKERRQ(ierr);
3565     if (maxDegree <= 1) {
3566       ierr = DMFieldCreateDefaultQuadrature(coordField,cellIS,&affineQuad);CHKERRQ(ierr);
3567       if (affineQuad) {
3568         ierr = DMSNESGetFEGeom(coordField,cellIS,affineQuad,PETSC_FALSE,&affineGeom);CHKERRQ(ierr);
3569       }
3570     } else {
3571       ierr = PetscCalloc2(Nf,&quads,Nf,&geoms);CHKERRQ(ierr);
3572       for (f = 0; f < Nf; ++f) {
3573         PetscObject  obj;
3574         PetscClassId id;
3575         PetscBool    fimp;
3576 
3577         ierr = PetscDSGetImplicit(prob, f, &fimp);CHKERRQ(ierr);
3578         if (isImplicit != fimp) continue;
3579         ierr = PetscDSGetDiscretization(prob, f, &obj);CHKERRQ(ierr);
3580         ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
3581         if (id == PETSCFE_CLASSID) {
3582           PetscFE fe = (PetscFE) obj;
3583 
3584           ierr = PetscFEGetQuadrature(fe, &quads[f]);CHKERRQ(ierr);
3585           ierr = PetscObjectReference((PetscObject)quads[f]);CHKERRQ(ierr);
3586           ierr = DMSNESGetFEGeom(coordField,cellIS,quads[f],PETSC_FALSE,&geoms[f]);CHKERRQ(ierr);
3587         }
3588       }
3589     }
3590   }
3591   /* Loop over chunks */
3592   ierr = ISGetPointRange(cellIS, &cStart, &cEnd, &cells);CHKERRQ(ierr);
3593   ierr = DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd);CHKERRQ(ierr);
3594   if (useFEM) {ierr = ISCreate(PETSC_COMM_SELF, &chunkIS);CHKERRQ(ierr);}
3595   numCells      = cEnd - cStart;
3596   numChunks     = 1;
3597   cellChunkSize = numCells/numChunks;
3598   numChunks     = PetscMin(1,numCells);
3599   for (chunk = 0; chunk < numChunks; ++chunk) {
3600     PetscScalar     *elemVec, *fluxL = NULL, *fluxR = NULL;
3601     PetscReal       *vol = NULL;
3602     PetscFVFaceGeom *fgeom = NULL;
3603     PetscInt         cS = cStart+chunk*cellChunkSize, cE = PetscMin(cS+cellChunkSize, cEnd), numCells = cE - cS, c;
3604     PetscInt         numFaces = 0;
3605 
3606     /* Extract field coefficients */
3607     if (useFEM) {
3608       ierr = ISGetPointSubrange(chunkIS, cS, cE, cells);CHKERRQ(ierr);
3609       ierr = DMPlexGetCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a);CHKERRQ(ierr);
3610       ierr = DMGetWorkArray(dm, numCells*totDim, MPIU_SCALAR, &elemVec);CHKERRQ(ierr);
3611       ierr = PetscArrayzero(elemVec, numCells*totDim);CHKERRQ(ierr);
3612     }
3613     /* TODO We will interlace both our field coefficients (u, u_t, uL, uR, etc.) and our output (elemVec, fL, fR). I think this works */
3614     /* Loop over fields */
3615     for (f = 0; f < Nf; ++f) {
3616       PetscObject  obj;
3617       PetscClassId id;
3618       PetscBool    fimp;
3619       PetscInt     numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;
3620 
3621       ierr = PetscDSGetImplicit(prob, f, &fimp);CHKERRQ(ierr);
3622       if (isImplicit != fimp) continue;
3623       ierr = PetscDSGetDiscretization(prob, f, &obj);CHKERRQ(ierr);
3624       ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
3625       if (id == PETSCFE_CLASSID) {
3626         PetscFE         fe = (PetscFE) obj;
3627         PetscFEGeom    *geom = affineGeom ? affineGeom : geoms[f];
3628         PetscFEGeom    *chunkGeom = NULL;
3629         PetscQuadrature quad = affineQuad ? affineQuad : quads[f];
3630         PetscInt        Nq, Nb;
3631 
3632         ierr = PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches);CHKERRQ(ierr);
3633         ierr = PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL);CHKERRQ(ierr);
3634         ierr = PetscFEGetDimension(fe, &Nb);CHKERRQ(ierr);
3635         blockSize = Nb;
3636         batchSize = numBlocks * blockSize;
3637         ierr      = PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches);CHKERRQ(ierr);
3638         numChunks = numCells / (numBatches*batchSize);
3639         Ne        = numChunks*numBatches*batchSize;
3640         Nr        = numCells % (numBatches*batchSize);
3641         offset    = numCells - Nr;
3642         /* Integrate FE residual to get elemVec (need fields at quadrature points) */
3643         /*   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) */
3644         ierr = PetscFEGeomGetChunk(geom,0,offset,&chunkGeom);CHKERRQ(ierr);
3645         ierr = PetscFEIntegrateResidual(prob, f, Ne, chunkGeom, u, u_t, probAux, a, t, elemVec);CHKERRQ(ierr);
3646         ierr = PetscFEGeomGetChunk(geom,offset,numCells,&chunkGeom);CHKERRQ(ierr);
3647         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);
3648         ierr = PetscFEGeomRestoreChunk(geom,offset,numCells,&chunkGeom);CHKERRQ(ierr);
3649       } else if (id == PETSCFV_CLASSID) {
3650         PetscFV fv = (PetscFV) obj;
3651 
3652         Ne = numFaces;
3653         /* Riemann solve over faces (need fields at face centroids) */
3654         /*   We need to evaluate FE fields at those coordinates */
3655         ierr = PetscFVIntegrateRHSFunction(fv, prob, f, Ne, fgeom, vol, uL, uR, fluxL, fluxR);CHKERRQ(ierr);
3656       } else SETERRQ1(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", f);
3657     }
3658     /* Loop over domain */
3659     if (useFEM) {
3660       /* Add elemVec to locX */
3661       for (c = cS; c < cE; ++c) {
3662         const PetscInt cell = cells ? cells[c] : c;
3663         const PetscInt cind = c - cStart;
3664 
3665         if (mesh->printFEM > 1) {ierr = DMPrintCellVector(cell, name, totDim, &elemVec[cind*totDim]);CHKERRQ(ierr);}
3666         if (ghostLabel) {
3667           PetscInt ghostVal;
3668 
3669           ierr = DMLabelGetValue(ghostLabel,cell,&ghostVal);CHKERRQ(ierr);
3670           if (ghostVal > 0) continue;
3671         }
3672         ierr = DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind*totDim], ADD_ALL_VALUES);CHKERRQ(ierr);
3673       }
3674     }
3675     /* Handle time derivative */
3676     if (locX_t) {
3677       PetscScalar *x_t, *fa;
3678 
3679       ierr = VecGetArray(locF, &fa);CHKERRQ(ierr);
3680       ierr = VecGetArray(locX_t, &x_t);CHKERRQ(ierr);
3681       for (f = 0; f < Nf; ++f) {
3682         PetscFV      fv;
3683         PetscObject  obj;
3684         PetscClassId id;
3685         PetscInt     pdim, d;
3686 
3687         ierr = PetscDSGetDiscretization(prob, f, &obj);CHKERRQ(ierr);
3688         ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
3689         if (id != PETSCFV_CLASSID) continue;
3690         fv   = (PetscFV) obj;
3691         ierr = PetscFVGetNumComponents(fv, &pdim);CHKERRQ(ierr);
3692         for (c = cS; c < cE; ++c) {
3693           const PetscInt cell = cells ? cells[c] : c;
3694           PetscScalar   *u_t, *r;
3695 
3696           if (ghostLabel) {
3697             PetscInt ghostVal;
3698 
3699             ierr = DMLabelGetValue(ghostLabel, cell, &ghostVal);CHKERRQ(ierr);
3700             if (ghostVal > 0) continue;
3701           }
3702           ierr = DMPlexPointLocalFieldRead(dm, cell, f, x_t, &u_t);CHKERRQ(ierr);
3703           ierr = DMPlexPointLocalFieldRef(dm, cell, f, fa, &r);CHKERRQ(ierr);
3704           for (d = 0; d < pdim; ++d) r[d] += u_t[d];
3705         }
3706       }
3707       ierr = VecRestoreArray(locX_t, &x_t);CHKERRQ(ierr);
3708       ierr = VecRestoreArray(locF, &fa);CHKERRQ(ierr);
3709     }
3710     if (useFEM) {
3711       ierr = DMPlexRestoreCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a);CHKERRQ(ierr);
3712       ierr = DMRestoreWorkArray(dm, numCells*totDim, MPIU_SCALAR, &elemVec);CHKERRQ(ierr);
3713     }
3714   }
3715   if (useFEM) {ierr = ISDestroy(&chunkIS);CHKERRQ(ierr);}
3716   ierr = ISRestorePointRange(cellIS, &cStart, &cEnd, &cells);CHKERRQ(ierr);
3717   /* TODO Could include boundary residual here (see DMPlexComputeResidual_Internal) */
3718   if (useFEM) {
3719     if (maxDegree <= 1) {
3720       ierr = DMSNESRestoreFEGeom(coordField,cellIS,affineQuad,PETSC_FALSE,&affineGeom);CHKERRQ(ierr);
3721       ierr = PetscQuadratureDestroy(&affineQuad);CHKERRQ(ierr);
3722     } else {
3723       for (f = 0; f < Nf; ++f) {
3724         ierr = DMSNESRestoreFEGeom(coordField,cellIS,quads[f],PETSC_FALSE,&geoms[f]);CHKERRQ(ierr);
3725         ierr = PetscQuadratureDestroy(&quads[f]);CHKERRQ(ierr);
3726       }
3727       ierr = PetscFree2(quads,geoms);CHKERRQ(ierr);
3728     }
3729   }
3730   ierr = PetscLogEventEnd(DMPLEX_ResidualFEM,dm,0,0,0);CHKERRQ(ierr);
3731   PetscFunctionReturn(0);
3732 }
3733 
3734 /*
3735   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
3736 
3737   X   - The local solution vector
3738   X_t - The local solution time derviative vector, or NULL
3739 */
3740 PetscErrorCode DMPlexComputeJacobian_Patch_Internal(DM dm, PetscSection section, PetscSection globalSection, IS cellIS,
3741                                                     PetscReal t, PetscReal X_tShift, Vec X, Vec X_t, Mat Jac, Mat JacP, void *ctx)
3742 {
3743   DM_Plex         *mesh  = (DM_Plex *) dm->data;
3744   const char      *name = "Jacobian", *nameP = "JacobianPre";
3745   DM               dmAux = NULL;
3746   PetscDS          prob,   probAux = NULL;
3747   PetscSection     sectionAux = NULL;
3748   Vec              A;
3749   DMField          coordField;
3750   PetscFEGeom     *cgeomFEM;
3751   PetscQuadrature  qGeom = NULL;
3752   Mat              J = Jac, JP = JacP;
3753   PetscScalar     *work, *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL, *elemMatP = NULL, *elemMatD = NULL;
3754   PetscBool        hasJac, hasPrec, hasDyn, assembleJac, isMatIS, isMatISP, *isFE, hasFV = PETSC_FALSE;
3755   const PetscInt  *cells;
3756   PetscInt         Nf, fieldI, fieldJ, maxDegree, numCells, cStart, cEnd, numChunks, chunkSize, chunk, totDim, totDimAux = 0, sz, wsz, off = 0, offCell = 0;
3757   PetscErrorCode   ierr;
3758 
3759   PetscFunctionBegin;
3760   CHKMEMQ;
3761   ierr = ISGetLocalSize(cellIS, &numCells);CHKERRQ(ierr);
3762   ierr = ISGetPointRange(cellIS, &cStart, &cEnd, &cells);CHKERRQ(ierr);
3763   ierr = PetscLogEventBegin(DMPLEX_JacobianFEM,dm,0,0,0);CHKERRQ(ierr);
3764   ierr = DMGetDS(dm, &prob);CHKERRQ(ierr);
3765   ierr = PetscObjectQuery((PetscObject) dm, "dmAux", (PetscObject *) &dmAux);CHKERRQ(ierr);
3766   ierr = PetscObjectQuery((PetscObject) dm, "A", (PetscObject *) &A);CHKERRQ(ierr);
3767   if (dmAux) {
3768     ierr = DMGetLocalSection(dmAux, &sectionAux);CHKERRQ(ierr);
3769     ierr = DMGetDS(dmAux, &probAux);CHKERRQ(ierr);
3770   }
3771   /* Get flags */
3772   ierr = PetscDSGetNumFields(prob, &Nf);CHKERRQ(ierr);
3773   ierr = DMGetWorkArray(dm, Nf, MPIU_BOOL, &isFE);CHKERRQ(ierr);
3774   for (fieldI = 0; fieldI < Nf; ++fieldI) {
3775     PetscObject  disc;
3776     PetscClassId id;
3777     ierr = PetscDSGetDiscretization(prob, fieldI, &disc);CHKERRQ(ierr);
3778     ierr = PetscObjectGetClassId(disc, &id);CHKERRQ(ierr);
3779     if (id == PETSCFE_CLASSID)      {isFE[fieldI] = PETSC_TRUE;}
3780     else if (id == PETSCFV_CLASSID) {hasFV = PETSC_TRUE; isFE[fieldI] = PETSC_FALSE;}
3781   }
3782   ierr = PetscDSHasJacobian(prob, &hasJac);CHKERRQ(ierr);
3783   ierr = PetscDSHasJacobianPreconditioner(prob, &hasPrec);CHKERRQ(ierr);
3784   ierr = PetscDSHasDynamicJacobian(prob, &hasDyn);CHKERRQ(ierr);
3785   assembleJac = hasJac && hasPrec && (Jac != JacP) ? PETSC_TRUE : PETSC_FALSE;
3786   hasDyn      = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
3787   ierr = PetscObjectTypeCompare((PetscObject) Jac,  MATIS, &isMatIS);CHKERRQ(ierr);
3788   ierr = PetscObjectTypeCompare((PetscObject) JacP, MATIS, &isMatISP);CHKERRQ(ierr);
3789   /* Setup input data and temp arrays (should be DMGetWorkArray) */
3790   if (isMatISP || isMatISP) {ierr = DMPlexGetSubdomainSection(dm, &globalSection);CHKERRQ(ierr);}
3791   if (isMatIS)  {ierr = MatISGetLocalMat(Jac,  &J);CHKERRQ(ierr);}
3792   if (isMatISP) {ierr = MatISGetLocalMat(JacP, &JP);CHKERRQ(ierr);}
3793   if (hasFV)    {ierr = MatSetOption(JP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE);CHKERRQ(ierr);} /* No allocated space for FV stuff, so ignore the zero entries */
3794   ierr = PetscObjectQuery((PetscObject) dm, "dmAux", (PetscObject *) &dmAux);CHKERRQ(ierr);
3795   ierr = PetscObjectQuery((PetscObject) dm, "A", (PetscObject *) &A);CHKERRQ(ierr);
3796   ierr = PetscDSGetTotalDimension(prob, &totDim);CHKERRQ(ierr);
3797   if (probAux) {ierr = PetscDSGetTotalDimension(probAux, &totDimAux);CHKERRQ(ierr);}
3798   CHKMEMQ;
3799   /* Compute batch sizes */
3800   if (isFE[0]) {
3801     PetscFE         fe;
3802     PetscQuadrature q;
3803     PetscInt        numQuadPoints, numBatches, batchSize, numBlocks, blockSize, Nb;
3804 
3805     ierr = PetscDSGetDiscretization(prob, 0, (PetscObject *) &fe);CHKERRQ(ierr);
3806     ierr = PetscFEGetQuadrature(fe, &q);CHKERRQ(ierr);
3807     ierr = PetscQuadratureGetData(q, NULL, NULL, &numQuadPoints, NULL, NULL);CHKERRQ(ierr);
3808     ierr = PetscFEGetDimension(fe, &Nb);CHKERRQ(ierr);
3809     ierr = PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches);CHKERRQ(ierr);
3810     blockSize = Nb*numQuadPoints;
3811     batchSize = numBlocks  * blockSize;
3812     chunkSize = numBatches * batchSize;
3813     numChunks = numCells / chunkSize + numCells % chunkSize;
3814     ierr = PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches);CHKERRQ(ierr);
3815   } else {
3816     chunkSize = numCells;
3817     numChunks = 1;
3818   }
3819   /* Get work space */
3820   wsz  = (((X?1:0) + (X_t?1:0) + (dmAux?1:0))*totDim + ((hasJac?1:0) + (hasPrec?1:0) + (hasDyn?1:0))*totDim*totDim)*chunkSize;
3821   ierr = DMGetWorkArray(dm, wsz, MPIU_SCALAR, &work);CHKERRQ(ierr);
3822   ierr = PetscArrayzero(work, wsz);CHKERRQ(ierr);
3823   off      = 0;
3824   u        = X       ? (sz = chunkSize*totDim,        off += sz, work+off-sz) : NULL;
3825   u_t      = X_t     ? (sz = chunkSize*totDim,        off += sz, work+off-sz) : NULL;
3826   a        = dmAux   ? (sz = chunkSize*totDimAux,     off += sz, work+off-sz) : NULL;
3827   elemMat  = hasJac  ? (sz = chunkSize*totDim*totDim, off += sz, work+off-sz) : NULL;
3828   elemMatP = hasPrec ? (sz = chunkSize*totDim*totDim, off += sz, work+off-sz) : NULL;
3829   elemMatD = hasDyn  ? (sz = chunkSize*totDim*totDim, off += sz, work+off-sz) : NULL;
3830   if (off != wsz) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Error is workspace size %D should be %D", off, wsz);
3831   /* Setup geometry */
3832   ierr = DMGetCoordinateField(dm, &coordField);CHKERRQ(ierr);
3833   ierr = DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree);CHKERRQ(ierr);
3834   if (maxDegree <= 1) {ierr = DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom);CHKERRQ(ierr);}
3835   if (!qGeom) {
3836     PetscFE fe;
3837 
3838     ierr = PetscDSGetDiscretization(prob, 0, (PetscObject *) &fe);CHKERRQ(ierr);
3839     ierr = PetscFEGetQuadrature(fe, &qGeom);CHKERRQ(ierr);
3840     ierr = PetscObjectReference((PetscObject) qGeom);CHKERRQ(ierr);
3841   }
3842   ierr = DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM);CHKERRQ(ierr);
3843   /* Compute volume integrals */
3844   if (assembleJac) {ierr = MatZeroEntries(J);CHKERRQ(ierr);}
3845   ierr = MatZeroEntries(JP);CHKERRQ(ierr);
3846   for (chunk = 0; chunk < numChunks; ++chunk, offCell += chunkSize) {
3847     const PetscInt   Ncell = PetscMin(chunkSize, numCells - offCell);
3848     PetscInt         c;
3849 
3850     /* Extract values */
3851     for (c = 0; c < Ncell; ++c) {
3852       const PetscInt cell = cells ? cells[c+offCell] : c+offCell;
3853       PetscScalar   *x = NULL,  *x_t = NULL;
3854       PetscInt       i;
3855 
3856       if (X) {
3857         ierr = DMPlexVecGetClosure(dm, section, X, cell, NULL, &x);CHKERRQ(ierr);
3858         for (i = 0; i < totDim; ++i) u[c*totDim+i] = x[i];
3859         ierr = DMPlexVecRestoreClosure(dm, section, X, cell, NULL, &x);CHKERRQ(ierr);
3860       }
3861       if (X_t) {
3862         ierr = DMPlexVecGetClosure(dm, section, X_t, cell, NULL, &x_t);CHKERRQ(ierr);
3863         for (i = 0; i < totDim; ++i) u_t[c*totDim+i] = x_t[i];
3864         ierr = DMPlexVecRestoreClosure(dm, section, X_t, cell, NULL, &x_t);CHKERRQ(ierr);
3865       }
3866       if (dmAux) {
3867         ierr = DMPlexVecGetClosure(dmAux, sectionAux, A, cell, NULL, &x);CHKERRQ(ierr);
3868         for (i = 0; i < totDimAux; ++i) a[c*totDimAux+i] = x[i];
3869         ierr = DMPlexVecRestoreClosure(dmAux, sectionAux, A, cell, NULL, &x);CHKERRQ(ierr);
3870       }
3871     }
3872     CHKMEMQ;
3873     for (fieldI = 0; fieldI < Nf; ++fieldI) {
3874       PetscFE fe;
3875       ierr = PetscDSGetDiscretization(prob, fieldI, (PetscObject *) &fe);CHKERRQ(ierr);
3876       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
3877         if (hasJac)  {ierr = PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN,     fieldI, fieldJ, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMat);CHKERRQ(ierr);}
3878         if (hasPrec) {ierr = PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, fieldI, fieldJ, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatP);CHKERRQ(ierr);}
3879         if (hasDyn)  {ierr = PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, fieldI, fieldJ, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatD);CHKERRQ(ierr);}
3880       }
3881       /* For finite volume, add the identity */
3882       if (!isFE[fieldI]) {
3883         PetscFV  fv;
3884         PetscInt eOffset = 0, Nc, fc, foff;
3885 
3886         ierr = PetscDSGetFieldOffset(prob, fieldI, &foff);CHKERRQ(ierr);
3887         ierr = PetscDSGetDiscretization(prob, fieldI, (PetscObject *) &fv);CHKERRQ(ierr);
3888         ierr = PetscFVGetNumComponents(fv, &Nc);CHKERRQ(ierr);
3889         for (c = 0; c < chunkSize; ++c, eOffset += totDim*totDim) {
3890           for (fc = 0; fc < Nc; ++fc) {
3891             const PetscInt i = foff + fc;
3892             if (hasJac)  {elemMat [eOffset+i*totDim+i] = 1.0;}
3893             if (hasPrec) {elemMatP[eOffset+i*totDim+i] = 1.0;}
3894           }
3895         }
3896       }
3897     }
3898     CHKMEMQ;
3899     /*   Add contribution from X_t */
3900     if (hasDyn) {for (c = 0; c < chunkSize*totDim*totDim; ++c) elemMat[c] += X_tShift*elemMatD[c];}
3901     /* Insert values into matrix */
3902     for (c = 0; c < Ncell; ++c) {
3903       const PetscInt cell = cells ? cells[c+offCell] : c+offCell;
3904       if (mesh->printFEM > 1) {
3905         if (hasJac)  {ierr = DMPrintCellMatrix(cell, name,  totDim, totDim, &elemMat[(c-cStart)*totDim*totDim]);CHKERRQ(ierr);}
3906         if (hasPrec) {ierr = DMPrintCellMatrix(cell, nameP, totDim, totDim, &elemMatP[(c-cStart)*totDim*totDim]);CHKERRQ(ierr);}
3907       }
3908       if (assembleJac) {ierr = DMPlexMatSetClosure(dm, section, globalSection, Jac, cell, &elemMat[(c-cStart)*totDim*totDim], ADD_VALUES);CHKERRQ(ierr);}
3909       ierr = DMPlexMatSetClosure(dm, section, globalSection, JP, cell, &elemMat[(c-cStart)*totDim*totDim], ADD_VALUES);CHKERRQ(ierr);
3910     }
3911     CHKMEMQ;
3912   }
3913   /* Cleanup */
3914   ierr = DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM);CHKERRQ(ierr);
3915   ierr = PetscQuadratureDestroy(&qGeom);CHKERRQ(ierr);
3916   if (hasFV) {ierr = MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE);CHKERRQ(ierr);}
3917   ierr = DMRestoreWorkArray(dm, Nf, MPIU_BOOL, &isFE);CHKERRQ(ierr);
3918   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);
3919   /* Compute boundary integrals */
3920   /* ierr = DMPlexComputeBdJacobian_Internal(dm, X, X_t, t, X_tShift, Jac, JacP, ctx);CHKERRQ(ierr); */
3921   /* Assemble matrix */
3922   if (assembleJac) {ierr = MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);ierr = MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);}
3923   ierr = MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);ierr = MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
3924   ierr = PetscLogEventEnd(DMPLEX_JacobianFEM,dm,0,0,0);CHKERRQ(ierr);
3925   CHKMEMQ;
3926   PetscFunctionReturn(0);
3927 }
3928