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