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