xref: /petsc/src/dm/impls/plex/plexfem.c (revision 0d3b6ff2d777fc53ae5f1aa453274beef4146c2b)
1 #include <petsc/private/dmpleximpl.h>   /*I      "petscdmplex.h"   I*/
2 #include <petscsf.h>
3 
4 #include <petsc/private/hashsetij.h>
5 #include <petsc/private/petscfeimpl.h>
6 #include <petsc/private/petscfvimpl.h>
7 
8 static PetscErrorCode DMPlexConvertPlex(DM dm, DM *plex, PetscBool copy)
9 {
10   PetscBool      isPlex;
11   PetscErrorCode ierr;
12 
13   PetscFunctionBegin;
14   ierr = PetscObjectTypeCompare((PetscObject) dm, DMPLEX, &isPlex);CHKERRQ(ierr);
15   if (isPlex) {
16     *plex = dm;
17     ierr = PetscObjectReference((PetscObject) dm);CHKERRQ(ierr);
18   } else {
19     ierr = PetscObjectQuery((PetscObject) dm, "dm_plex", (PetscObject *) plex);CHKERRQ(ierr);
20     if (!*plex) {
21       ierr = DMConvert(dm,DMPLEX,plex);CHKERRQ(ierr);
22       ierr = PetscObjectCompose((PetscObject) dm, "dm_plex", (PetscObject) *plex);CHKERRQ(ierr);
23       if (copy) {
24         const char *comps[3] = {"A", "dmAux"};
25         PetscObject obj;
26         PetscInt    i;
27 
28         {
29           /* Run the subdomain hook (this will copy the DMSNES/DMTS) */
30           DMSubDomainHookLink link;
31           for (link = dm->subdomainhook; link; link = link->next) {
32             if (link->ddhook) {ierr = (*link->ddhook)(dm, *plex, link->ctx);CHKERRQ(ierr);}
33           }
34         }
35         for (i = 0; i < 3; i++) {
36           ierr = PetscObjectQuery((PetscObject) dm, comps[i], &obj);CHKERRQ(ierr);
37           ierr = PetscObjectCompose((PetscObject) *plex, comps[i], obj);CHKERRQ(ierr);
38         }
39       }
40     } else {
41       ierr = PetscObjectReference((PetscObject) *plex);CHKERRQ(ierr);
42     }
43   }
44   PetscFunctionReturn(0);
45 }
46 
47 static PetscErrorCode PetscContainerUserDestroy_PetscFEGeom (void *ctx)
48 {
49   PetscFEGeom *geom = (PetscFEGeom *) ctx;
50   PetscErrorCode ierr;
51 
52   PetscFunctionBegin;
53   ierr = PetscFEGeomDestroy(&geom);CHKERRQ(ierr);
54   PetscFunctionReturn(0);
55 }
56 
57 static PetscErrorCode DMPlexGetFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
58 {
59   char            composeStr[33] = {0};
60   PetscObjectId   id;
61   PetscContainer  container;
62   PetscErrorCode  ierr;
63 
64   PetscFunctionBegin;
65   ierr = PetscObjectGetId((PetscObject)quad,&id);CHKERRQ(ierr);
66   ierr = PetscSNPrintf(composeStr, 32, "DMPlexGetFEGeom_%x\n", id);CHKERRQ(ierr);
67   ierr = PetscObjectQuery((PetscObject) pointIS, composeStr, (PetscObject *) &container);CHKERRQ(ierr);
68   if (container) {
69     ierr = PetscContainerGetPointer(container, (void **) geom);CHKERRQ(ierr);
70   } else {
71     ierr = DMFieldCreateFEGeom(coordField, pointIS, quad, faceData, geom);CHKERRQ(ierr);
72     ierr = PetscContainerCreate(PETSC_COMM_SELF,&container);CHKERRQ(ierr);
73     ierr = PetscContainerSetPointer(container, (void *) *geom);CHKERRQ(ierr);
74     ierr = PetscContainerSetUserDestroy(container, PetscContainerUserDestroy_PetscFEGeom);CHKERRQ(ierr);
75     ierr = PetscObjectCompose((PetscObject) pointIS, composeStr, (PetscObject) container);CHKERRQ(ierr);
76     ierr = PetscContainerDestroy(&container);CHKERRQ(ierr);
77   }
78   PetscFunctionReturn(0);
79 }
80 
81 static PetscErrorCode DMPlexRestoreFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
82 {
83   PetscFunctionBegin;
84   *geom = NULL;
85   PetscFunctionReturn(0);
86 }
87 
88 /*@
89   DMPlexGetScale - Get the scale for the specified fundamental unit
90 
91   Not collective
92 
93   Input Arguments:
94 + dm   - the DM
95 - unit - The SI unit
96 
97   Output Argument:
98 . scale - The value used to scale all quantities with this unit
99 
100   Level: advanced
101 
102 .seealso: DMPlexSetScale(), PetscUnit
103 @*/
104 PetscErrorCode DMPlexGetScale(DM dm, PetscUnit unit, PetscReal *scale)
105 {
106   DM_Plex *mesh = (DM_Plex*) dm->data;
107 
108   PetscFunctionBegin;
109   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
110   PetscValidPointer(scale, 3);
111   *scale = mesh->scale[unit];
112   PetscFunctionReturn(0);
113 }
114 
115 /*@
116   DMPlexSetScale - Set the scale for the specified fundamental unit
117 
118   Not collective
119 
120   Input Arguments:
121 + dm   - the DM
122 . unit - The SI unit
123 - scale - The value used to scale all quantities with this unit
124 
125   Level: advanced
126 
127 .seealso: DMPlexGetScale(), PetscUnit
128 @*/
129 PetscErrorCode DMPlexSetScale(DM dm, PetscUnit unit, PetscReal scale)
130 {
131   DM_Plex *mesh = (DM_Plex*) dm->data;
132 
133   PetscFunctionBegin;
134   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
135   mesh->scale[unit] = scale;
136   PetscFunctionReturn(0);
137 }
138 
139 static PetscErrorCode DMPlexProjectRigidBody_Private(PetscInt dim, PetscReal t, const PetscReal X[], PetscInt Nf, PetscScalar *mode, void *ctx)
140 {
141   const PetscInt eps[3][3][3] = {{{0, 0, 0}, {0, 0, 1}, {0, -1, 0}}, {{0, 0, -1}, {0, 0, 0}, {1, 0, 0}}, {{0, 1, 0}, {-1, 0, 0}, {0, 0, 0}}};
142   PetscInt *ctxInt  = (PetscInt *) ctx;
143   PetscInt  dim2    = ctxInt[0];
144   PetscInt  d       = ctxInt[1];
145   PetscInt  i, j, k = dim > 2 ? d - dim : d;
146 
147   PetscFunctionBegin;
148   if (dim != dim2) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Input dimension %D does not match context dimension %D", dim, dim2);
149   for (i = 0; i < dim; i++) mode[i] = 0.;
150   if (d < dim) {
151     mode[d] = 1.; /* Translation along axis d */
152   } else {
153     for (i = 0; i < dim; i++) {
154       for (j = 0; j < dim; j++) {
155         mode[j] += eps[i][j][k]*X[i]; /* Rotation about axis d */
156       }
157     }
158   }
159   PetscFunctionReturn(0);
160 }
161 
162 /*@
163   DMPlexCreateRigidBody - For the default global section, create rigid body modes by function space interpolation
164 
165   Collective on dm
166 
167   Input Arguments:
168 . dm - the DM
169 
170   Output Argument:
171 . sp - the null space
172 
173   Note: This is necessary to provide a suitable coarse space for algebraic multigrid
174 
175   Level: advanced
176 
177 .seealso: MatNullSpaceCreate(), PCGAMG
178 @*/
179 PetscErrorCode DMPlexCreateRigidBody(DM dm, MatNullSpace *sp)
180 {
181   MPI_Comm       comm;
182   Vec            mode[6];
183   PetscSection   section, globalSection;
184   PetscInt       dim, dimEmbed, n, m, mmin, d, i, j;
185   PetscErrorCode ierr;
186 
187   PetscFunctionBegin;
188   ierr = PetscObjectGetComm((PetscObject)dm,&comm);CHKERRQ(ierr);
189   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
190   ierr = DMGetCoordinateDim(dm, &dimEmbed);CHKERRQ(ierr);
191   if (dim == 1) {
192     ierr = MatNullSpaceCreate(comm, PETSC_TRUE, 0, NULL, sp);CHKERRQ(ierr);
193     PetscFunctionReturn(0);
194   }
195   ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);
196   ierr = DMGetGlobalSection(dm, &globalSection);CHKERRQ(ierr);
197   ierr = PetscSectionGetConstrainedStorageSize(globalSection, &n);CHKERRQ(ierr);
198   m    = (dim*(dim+1))/2;
199   ierr = VecCreate(comm, &mode[0]);CHKERRQ(ierr);
200   ierr = VecSetSizes(mode[0], n, PETSC_DETERMINE);CHKERRQ(ierr);
201   ierr = VecSetUp(mode[0]);CHKERRQ(ierr);
202   ierr = VecGetSize(mode[0], &n);CHKERRQ(ierr);
203   mmin = PetscMin(m, n);
204   for (i = 1; i < m; ++i) {ierr = VecDuplicate(mode[0], &mode[i]);CHKERRQ(ierr);}
205   for (d = 0; d < m; d++) {
206     PetscInt         ctx[2];
207     PetscErrorCode (*func)(PetscInt, PetscReal, const PetscReal *, PetscInt, PetscScalar *, void *) = DMPlexProjectRigidBody_Private;
208     void            *voidctx = (void *) (&ctx[0]);
209 
210     ctx[0] = dimEmbed;
211     ctx[1] = d;
212     ierr = DMProjectFunction(dm, 0.0, &func, &voidctx, INSERT_VALUES, mode[d]);CHKERRQ(ierr);
213   }
214   /* Orthonormalize system */
215   for (i = 0; i < mmin; ++i) {
216     PetscScalar dots[6];
217 
218     ierr = VecNormalize(mode[i], NULL);CHKERRQ(ierr);
219     ierr = VecMDot(mode[i], mmin-i-1, mode+i+1, dots+i+1);CHKERRQ(ierr);
220     for (j = i+1; j < mmin; ++j) {
221       dots[j] *= -1.0;
222       ierr = VecAXPY(mode[j], dots[j], mode[i]);CHKERRQ(ierr);
223     }
224   }
225   ierr = MatNullSpaceCreate(comm, PETSC_FALSE, mmin, mode, sp);CHKERRQ(ierr);
226   for (i = 0; i < m; ++i) {ierr = VecDestroy(&mode[i]);CHKERRQ(ierr);}
227   PetscFunctionReturn(0);
228 }
229 
230 /*@
231   DMPlexCreateRigidBodies - For the default global section, create rigid body modes by function space interpolation
232 
233   Collective on dm
234 
235   Input Arguments:
236 + dm    - the DM
237 . nb    - The number of bodies
238 . label - The DMLabel marking each domain
239 . nids  - The number of ids per body
240 - ids   - An array of the label ids in sequence for each domain
241 
242   Output Argument:
243 . sp - the null space
244 
245   Note: This is necessary to provide a suitable coarse space for algebraic multigrid
246 
247   Level: advanced
248 
249 .seealso: MatNullSpaceCreate()
250 @*/
251 PetscErrorCode DMPlexCreateRigidBodies(DM dm, PetscInt nb, DMLabel label, const PetscInt nids[], const PetscInt ids[], MatNullSpace *sp)
252 {
253   MPI_Comm       comm;
254   PetscSection   section, globalSection;
255   Vec           *mode;
256   PetscScalar   *dots;
257   PetscInt       dim, dimEmbed, n, m, b, d, i, j, off;
258   PetscErrorCode ierr;
259 
260   PetscFunctionBegin;
261   ierr = PetscObjectGetComm((PetscObject)dm,&comm);CHKERRQ(ierr);
262   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
263   ierr = DMGetCoordinateDim(dm, &dimEmbed);CHKERRQ(ierr);
264   ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);
265   ierr = DMGetGlobalSection(dm, &globalSection);CHKERRQ(ierr);
266   ierr = PetscSectionGetConstrainedStorageSize(globalSection, &n);CHKERRQ(ierr);
267   m    = nb * (dim*(dim+1))/2;
268   ierr = PetscMalloc2(m, &mode, m, &dots);CHKERRQ(ierr);
269   ierr = VecCreate(comm, &mode[0]);CHKERRQ(ierr);
270   ierr = VecSetSizes(mode[0], n, PETSC_DETERMINE);CHKERRQ(ierr);
271   ierr = VecSetUp(mode[0]);CHKERRQ(ierr);
272   for (i = 1; i < m; ++i) {ierr = VecDuplicate(mode[0], &mode[i]);CHKERRQ(ierr);}
273   for (b = 0, off = 0; b < nb; ++b) {
274     for (d = 0; d < m/nb; ++d) {
275       PetscInt         ctx[2];
276       PetscErrorCode (*func)(PetscInt, PetscReal, const PetscReal *, PetscInt, PetscScalar *, void *) = DMPlexProjectRigidBody_Private;
277       void            *voidctx = (void *) (&ctx[0]);
278 
279       ctx[0] = dimEmbed;
280       ctx[1] = d;
281       ierr = DMProjectFunctionLabel(dm, 0.0, label, nids[b], &ids[off], 0, NULL, &func, &voidctx, INSERT_VALUES, mode[d]);CHKERRQ(ierr);
282       off   += nids[b];
283     }
284   }
285   /* Orthonormalize system */
286   for (i = 0; i < m; ++i) {
287     PetscScalar dots[6];
288 
289     ierr = VecNormalize(mode[i], NULL);CHKERRQ(ierr);
290     ierr = VecMDot(mode[i], m-i-1, mode+i+1, dots+i+1);CHKERRQ(ierr);
291     for (j = i+1; j < m; ++j) {
292       dots[j] *= -1.0;
293       ierr = VecAXPY(mode[j], dots[j], mode[i]);CHKERRQ(ierr);
294     }
295   }
296   ierr = MatNullSpaceCreate(comm, PETSC_FALSE, m, mode, sp);CHKERRQ(ierr);
297   for (i = 0; i< m; ++i) {ierr = VecDestroy(&mode[i]);CHKERRQ(ierr);}
298   ierr = PetscFree2(mode, dots);CHKERRQ(ierr);
299   PetscFunctionReturn(0);
300 }
301 
302 /*@
303   DMPlexSetMaxProjectionHeight - In DMPlexProjectXXXLocal() functions, the projected values of a basis function's dofs
304   are computed by associating the basis function with one of the mesh points in its transitively-closed support, and
305   evaluating the dual space basis of that point.  A basis function is associated with the point in its
306   transitively-closed support whose mesh height is highest (w.r.t. DAG height), but not greater than the maximum
307   projection height, which is set with this function.  By default, the maximum projection height is zero, which means
308   that only mesh cells are used to project basis functions.  A height of one, for example, evaluates a cell-interior
309   basis functions using its cells dual space basis, but all other basis functions with the dual space basis of a face.
310 
311   Input Parameters:
312 + dm - the DMPlex object
313 - height - the maximum projection height >= 0
314 
315   Level: advanced
316 
317 .seealso: DMPlexGetMaxProjectionHeight(), DMProjectFunctionLocal(), DMProjectFunctionLabelLocal()
318 @*/
319 PetscErrorCode DMPlexSetMaxProjectionHeight(DM dm, PetscInt height)
320 {
321   DM_Plex *plex = (DM_Plex *) dm->data;
322 
323   PetscFunctionBegin;
324   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
325   plex->maxProjectionHeight = height;
326   PetscFunctionReturn(0);
327 }
328 
329 /*@
330   DMPlexGetMaxProjectionHeight - Get the maximum height (w.r.t. DAG) of mesh points used to evaluate dual bases in
331   DMPlexProjectXXXLocal() functions.
332 
333   Input Parameters:
334 . dm - the DMPlex object
335 
336   Output Parameters:
337 . height - the maximum projection height
338 
339   Level: intermediate
340 
341 .seealso: DMPlexSetMaxProjectionHeight(), DMProjectFunctionLocal(), DMProjectFunctionLabelLocal()
342 @*/
343 PetscErrorCode DMPlexGetMaxProjectionHeight(DM dm, PetscInt *height)
344 {
345   DM_Plex *plex = (DM_Plex *) dm->data;
346 
347   PetscFunctionBegin;
348   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
349   *height = plex->maxProjectionHeight;
350   PetscFunctionReturn(0);
351 }
352 
353 typedef struct {
354   PetscReal    alpha; /* The first Euler angle, and in 2D the only one */
355   PetscReal    beta;  /* The second Euler angle */
356   PetscReal    gamma; /* The third Euler angle */
357   PetscInt     dim;   /* The dimension of R */
358   PetscScalar *R;     /* The rotation matrix, transforming a vector in the local basis to the global basis */
359   PetscScalar *RT;    /* The transposed rotation matrix, transforming a vector in the global basis to the local basis */
360 } RotCtx;
361 
362 /*
363   Note: Following https://en.wikipedia.org/wiki/Euler_angles, we will specify Euler angles by extrinsic rotations, meaning that
364   we rotate with respect to a fixed initial coordinate system, the local basis (x-y-z). The global basis (X-Y-Z) is reached as follows:
365   $ The XYZ system rotates about the z axis by alpha. The X axis is now at angle alpha with respect to the x axis.
366   $ The XYZ system rotates again about the x axis by beta. The Z axis is now at angle beta with respect to the z axis.
367   $ The XYZ system rotates a third time about the z axis by gamma.
368 */
369 static PetscErrorCode DMPlexBasisTransformSetUp_Rotation_Internal(DM dm, void *ctx)
370 {
371   RotCtx        *rc  = (RotCtx *) ctx;
372   PetscInt       dim = rc->dim;
373   PetscReal      c1, s1, c2, s2, c3, s3;
374   PetscErrorCode ierr;
375 
376   PetscFunctionBegin;
377   ierr = PetscMalloc2(PetscSqr(dim), &rc->R, PetscSqr(dim), &rc->RT);CHKERRQ(ierr);
378   switch (dim) {
379   case 2:
380     c1 = PetscCosReal(rc->alpha);s1 = PetscSinReal(rc->alpha);
381     rc->R[0] =  c1;rc->R[1] = s1;
382     rc->R[2] = -s1;rc->R[3] = c1;
383     ierr = PetscArraycpy(rc->RT, rc->R, PetscSqr(dim));CHKERRQ(ierr);
384     DMPlex_Transpose2D_Internal(rc->RT);break;
385     break;
386   case 3:
387     c1 = PetscCosReal(rc->alpha);s1 = PetscSinReal(rc->alpha);
388     c2 = PetscCosReal(rc->beta); s2 = PetscSinReal(rc->beta);
389     c3 = PetscCosReal(rc->gamma);s3 = PetscSinReal(rc->gamma);
390     rc->R[0] =  c1*c3 - c2*s1*s3;rc->R[1] =  c3*s1    + c1*c2*s3;rc->R[2] = s2*s3;
391     rc->R[3] = -c1*s3 - c2*c3*s1;rc->R[4] =  c1*c2*c3 - s1*s3;   rc->R[5] = c3*s2;
392     rc->R[6] =  s1*s2;           rc->R[7] = -c1*s2;              rc->R[8] = c2;
393     ierr = PetscArraycpy(rc->RT, rc->R, PetscSqr(dim));CHKERRQ(ierr);
394     DMPlex_Transpose3D_Internal(rc->RT);break;
395     break;
396   default: SETERRQ1(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_OUTOFRANGE, "Dimension %D not supported", dim);
397   }
398   PetscFunctionReturn(0);
399 }
400 
401 static PetscErrorCode DMPlexBasisTransformDestroy_Rotation_Internal(DM dm, void *ctx)
402 {
403   RotCtx        *rc = (RotCtx *) ctx;
404   PetscErrorCode ierr;
405 
406   PetscFunctionBegin;
407   ierr = PetscFree2(rc->R, rc->RT);CHKERRQ(ierr);
408   ierr = PetscFree(rc);CHKERRQ(ierr);
409   PetscFunctionReturn(0);
410 }
411 
412 static PetscErrorCode DMPlexBasisTransformGetMatrix_Rotation_Internal(DM dm, const PetscReal x[], PetscBool l2g, const PetscScalar **A, void *ctx)
413 {
414   RotCtx *rc = (RotCtx *) ctx;
415 
416   PetscFunctionBeginHot;
417   PetscValidPointer(ctx, 5);
418   if (l2g) {*A = rc->R;}
419   else     {*A = rc->RT;}
420   PetscFunctionReturn(0);
421 }
422 
423 PetscErrorCode DMPlexBasisTransformApplyReal_Internal(DM dm, const PetscReal x[], PetscBool l2g, PetscInt dim, const PetscReal *y, PetscReal *z, void *ctx)
424 {
425   PetscErrorCode ierr;
426 
427   PetscFunctionBegin;
428   #if defined(PETSC_USE_COMPLEX)
429   switch (dim) {
430     case 2:
431     {
432       PetscScalar yt[2], zt[2];
433 
434       yt[0] = y[0]; yt[1] = y[1];
435       ierr = DMPlexBasisTransformApply_Internal(dm, x, l2g, dim, yt, zt, ctx);CHKERRQ(ierr);
436       z[0] = PetscRealPart(zt[0]); z[1] = PetscRealPart(zt[1]);
437     }
438     break;
439     case 3:
440     {
441       PetscScalar yt[3], zt[3];
442 
443       yt[0] = y[0]; yt[1] = y[1]; yt[2] = y[2];
444       ierr = DMPlexBasisTransformApply_Internal(dm, x, l2g, dim, yt, zt, ctx);CHKERRQ(ierr);
445       z[0] = PetscRealPart(zt[0]); z[1] = PetscRealPart(zt[1]); z[2] = PetscRealPart(zt[2]);
446     }
447     break;
448   }
449   #else
450   ierr = DMPlexBasisTransformApply_Internal(dm, x, l2g, dim, y, z, ctx);CHKERRQ(ierr);
451   #endif
452   PetscFunctionReturn(0);
453 }
454 
455 PetscErrorCode DMPlexBasisTransformApply_Internal(DM dm, const PetscReal x[], PetscBool l2g, PetscInt dim, const PetscScalar *y, PetscScalar *z, void *ctx)
456 {
457   const PetscScalar *A;
458   PetscErrorCode     ierr;
459 
460   PetscFunctionBeginHot;
461   ierr = (*dm->transformGetMatrix)(dm, x, l2g, &A, ctx);CHKERRQ(ierr);
462   switch (dim) {
463   case 2: DMPlex_Mult2D_Internal(A, 1, y, z);break;
464   case 3: DMPlex_Mult3D_Internal(A, 1, y, z);break;
465   }
466   PetscFunctionReturn(0);
467 }
468 
469 static PetscErrorCode DMPlexBasisTransformField_Internal(DM dm, DM tdm, Vec tv, PetscInt p, PetscInt f, PetscBool l2g, PetscScalar *a)
470 {
471   PetscSection       ts;
472   const PetscScalar *ta, *tva;
473   PetscInt           dof;
474   PetscErrorCode     ierr;
475 
476   PetscFunctionBeginHot;
477   ierr = DMGetLocalSection(tdm, &ts);CHKERRQ(ierr);
478   ierr = PetscSectionGetFieldDof(ts, p, f, &dof);CHKERRQ(ierr);
479   ierr = VecGetArrayRead(tv, &ta);CHKERRQ(ierr);
480   ierr = DMPlexPointLocalFieldRead(tdm, p, f, ta, (void *) &tva);CHKERRQ(ierr);
481   if (l2g) {
482     switch (dof) {
483     case 4: DMPlex_Mult2D_Internal(tva, 1, a, a);break;
484     case 9: DMPlex_Mult3D_Internal(tva, 1, a, a);break;
485     }
486   } else {
487     switch (dof) {
488     case 4: DMPlex_MultTranspose2D_Internal(tva, 1, a, a);break;
489     case 9: DMPlex_MultTranspose3D_Internal(tva, 1, a, a);break;
490     }
491   }
492   ierr = VecRestoreArrayRead(tv, &ta);CHKERRQ(ierr);
493   PetscFunctionReturn(0);
494 }
495 
496 static PetscErrorCode DMPlexBasisTransformFieldTensor_Internal(DM dm, DM tdm, Vec tv, PetscInt pf, PetscInt f, PetscInt pg, PetscInt g, PetscBool l2g, PetscInt lda, PetscScalar *a)
497 {
498   PetscSection       s, ts;
499   const PetscScalar *ta, *tvaf, *tvag;
500   PetscInt           fdof, gdof, fpdof, gpdof;
501   PetscErrorCode     ierr;
502 
503   PetscFunctionBeginHot;
504   ierr = DMGetLocalSection(dm, &s);CHKERRQ(ierr);
505   ierr = DMGetLocalSection(tdm, &ts);CHKERRQ(ierr);
506   ierr = PetscSectionGetFieldDof(s, pf, f, &fpdof);CHKERRQ(ierr);
507   ierr = PetscSectionGetFieldDof(s, pg, g, &gpdof);CHKERRQ(ierr);
508   ierr = PetscSectionGetFieldDof(ts, pf, f, &fdof);CHKERRQ(ierr);
509   ierr = PetscSectionGetFieldDof(ts, pg, g, &gdof);CHKERRQ(ierr);
510   ierr = VecGetArrayRead(tv, &ta);CHKERRQ(ierr);
511   ierr = DMPlexPointLocalFieldRead(tdm, pf, f, ta, (void *) &tvaf);CHKERRQ(ierr);
512   ierr = DMPlexPointLocalFieldRead(tdm, pg, g, ta, (void *) &tvag);CHKERRQ(ierr);
513   if (l2g) {
514     switch (fdof) {
515     case 4: DMPlex_MatMult2D_Internal(tvaf, gpdof, lda, a, a);break;
516     case 9: DMPlex_MatMult3D_Internal(tvaf, gpdof, lda, a, a);break;
517     }
518     switch (gdof) {
519     case 4: DMPlex_MatMultTransposeLeft2D_Internal(tvag, fpdof, lda, a, a);break;
520     case 9: DMPlex_MatMultTransposeLeft3D_Internal(tvag, fpdof, lda, a, a);break;
521     }
522   } else {
523     switch (fdof) {
524     case 4: DMPlex_MatMultTranspose2D_Internal(tvaf, gpdof, lda, a, a);break;
525     case 9: DMPlex_MatMultTranspose3D_Internal(tvaf, gpdof, lda, a, a);break;
526     }
527     switch (gdof) {
528     case 4: DMPlex_MatMultLeft2D_Internal(tvag, fpdof, lda, a, a);break;
529     case 9: DMPlex_MatMultLeft3D_Internal(tvag, fpdof, lda, a, a);break;
530     }
531   }
532   ierr = VecRestoreArrayRead(tv, &ta);CHKERRQ(ierr);
533   PetscFunctionReturn(0);
534 }
535 
536 PetscErrorCode DMPlexBasisTransformPoint_Internal(DM dm, DM tdm, Vec tv, PetscInt p, PetscBool fieldActive[], PetscBool l2g, PetscScalar *a)
537 {
538   PetscSection    s;
539   PetscSection    clSection;
540   IS              clPoints;
541   const PetscInt *clp;
542   PetscInt       *points = NULL;
543   PetscInt        Nf, f, Np, cp, dof, d = 0;
544   PetscErrorCode  ierr;
545 
546   PetscFunctionBegin;
547   ierr = DMGetLocalSection(dm, &s);CHKERRQ(ierr);
548   ierr = PetscSectionGetNumFields(s, &Nf);CHKERRQ(ierr);
549   ierr = DMPlexGetCompressedClosure(dm, s, p, &Np, &points, &clSection, &clPoints, &clp);CHKERRQ(ierr);
550   for (f = 0; f < Nf; ++f) {
551     for (cp = 0; cp < Np*2; cp += 2) {
552       ierr = PetscSectionGetFieldDof(s, points[cp], f, &dof);CHKERRQ(ierr);
553       if (!dof) continue;
554       if (fieldActive[f]) {ierr = DMPlexBasisTransformField_Internal(dm, tdm, tv, points[cp], f, l2g, &a[d]);CHKERRQ(ierr);}
555       d += dof;
556     }
557   }
558   ierr = DMPlexRestoreCompressedClosure(dm, s, p, &Np, &points, &clSection, &clPoints, &clp);CHKERRQ(ierr);
559   PetscFunctionReturn(0);
560 }
561 
562 PetscErrorCode DMPlexBasisTransformPointTensor_Internal(DM dm, DM tdm, Vec tv, PetscInt p, PetscBool l2g, PetscInt lda, PetscScalar *a)
563 {
564   PetscSection    s;
565   PetscSection    clSection;
566   IS              clPoints;
567   const PetscInt *clp;
568   PetscInt       *points = NULL;
569   PetscInt        Nf, f, g, Np, cpf, cpg, fdof, gdof, r, c = 0;
570   PetscErrorCode  ierr;
571 
572   PetscFunctionBegin;
573   ierr = DMGetLocalSection(dm, &s);CHKERRQ(ierr);
574   ierr = PetscSectionGetNumFields(s, &Nf);CHKERRQ(ierr);
575   ierr = DMPlexGetCompressedClosure(dm, s, p, &Np, &points, &clSection, &clPoints, &clp);CHKERRQ(ierr);
576   for (f = 0, r = 0; f < Nf; ++f) {
577     for (cpf = 0; cpf < Np*2; cpf += 2) {
578       ierr = PetscSectionGetFieldDof(s, points[cpf], f, &fdof);CHKERRQ(ierr);
579       for (g = 0, c = 0; g < Nf; ++g) {
580         for (cpg = 0; cpg < Np*2; cpg += 2) {
581           ierr = PetscSectionGetFieldDof(s, points[cpg], g, &gdof);CHKERRQ(ierr);
582           ierr = DMPlexBasisTransformFieldTensor_Internal(dm, tdm, tv, points[cpf], f, points[cpg], g, l2g, lda, &a[r*lda+c]);CHKERRQ(ierr);
583           c += gdof;
584         }
585       }
586       if (c != lda) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid number of columns %D should be %D", c, lda);
587       r += fdof;
588     }
589   }
590   if (r != lda) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid number of rows %D should be %D", c, lda);
591   ierr = DMPlexRestoreCompressedClosure(dm, s, p, &Np, &points, &clSection, &clPoints, &clp);CHKERRQ(ierr);
592   PetscFunctionReturn(0);
593 }
594 
595 static PetscErrorCode DMPlexBasisTransform_Internal(DM dm, Vec lv, PetscBool l2g)
596 {
597   DM                 tdm;
598   Vec                tv;
599   PetscSection       ts, s;
600   const PetscScalar *ta;
601   PetscScalar       *a, *va;
602   PetscInt           pStart, pEnd, p, Nf, f;
603   PetscErrorCode     ierr;
604 
605   PetscFunctionBegin;
606   ierr = DMGetBasisTransformDM_Internal(dm, &tdm);CHKERRQ(ierr);
607   ierr = DMGetBasisTransformVec_Internal(dm, &tv);CHKERRQ(ierr);
608   ierr = DMGetLocalSection(tdm, &ts);CHKERRQ(ierr);
609   ierr = DMGetLocalSection(dm, &s);CHKERRQ(ierr);
610   ierr = PetscSectionGetChart(s, &pStart, &pEnd);CHKERRQ(ierr);
611   ierr = PetscSectionGetNumFields(s, &Nf);CHKERRQ(ierr);
612   ierr = VecGetArray(lv, &a);CHKERRQ(ierr);
613   ierr = VecGetArrayRead(tv, &ta);CHKERRQ(ierr);
614   for (p = pStart; p < pEnd; ++p) {
615     for (f = 0; f < Nf; ++f) {
616       ierr = DMPlexPointLocalFieldRef(dm, p, f, a, (void *) &va);CHKERRQ(ierr);
617       ierr = DMPlexBasisTransformField_Internal(dm, tdm, tv, p, f, l2g, va);CHKERRQ(ierr);
618     }
619   }
620   ierr = VecRestoreArray(lv, &a);CHKERRQ(ierr);
621   ierr = VecRestoreArrayRead(tv, &ta);CHKERRQ(ierr);
622   PetscFunctionReturn(0);
623 }
624 
625 /*@
626   DMPlexGlobalToLocalBasis - Transform the values in the given local vector from the global basis to the local basis
627 
628   Input Parameters:
629 + dm - The DM
630 - lv - A local vector with values in the global basis
631 
632   Output Parameters:
633 . lv - A local vector with values in the local basis
634 
635   Note: This method is only intended to be called inside DMGlobalToLocal(). It is unlikely that a user will have a local vector full of coefficients for the global basis unless they are reimplementing GlobalToLocal.
636 
637   Level: developer
638 
639 .seealso: DMPlexLocalToGlobalBasis(), DMGetLocalSection()
640 @*/
641 PetscErrorCode DMPlexGlobalToLocalBasis(DM dm, Vec lv)
642 {
643   PetscErrorCode ierr;
644 
645   PetscFunctionBegin;
646   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
647   PetscValidHeaderSpecific(lv, VEC_CLASSID, 2);
648   ierr = DMPlexBasisTransform_Internal(dm, lv, PETSC_FALSE);CHKERRQ(ierr);
649   PetscFunctionReturn(0);
650 }
651 
652 /*@
653   DMPlexLocalToGlobalBasis - Transform the values in the given local vector from the local basis to the global basis
654 
655   Input Parameters:
656 + dm - The DM
657 - lv - A local vector with values in the local basis
658 
659   Output Parameters:
660 . lv - A local vector with values in the global basis
661 
662   Note: This method is only intended to be called inside DMGlobalToLocal(). It is unlikely that a user would want a local vector full of coefficients for the global basis unless they are reimplementing GlobalToLocal.
663 
664   Level: developer
665 
666 .seealso: DMPlexGlobalToLocalBasis(), DMGetLocalSection()
667 @*/
668 PetscErrorCode DMPlexLocalToGlobalBasis(DM dm, Vec lv)
669 {
670   PetscErrorCode ierr;
671 
672   PetscFunctionBegin;
673   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
674   PetscValidHeaderSpecific(lv, VEC_CLASSID, 2);
675   ierr = DMPlexBasisTransform_Internal(dm, lv, PETSC_TRUE);CHKERRQ(ierr);
676   PetscFunctionReturn(0);
677 }
678 
679 /*@
680   DMPlexCreateBasisRotation - Create an internal transformation from the global basis, used to specify boundary conditions
681     and global solutions, to a local basis, appropriate for discretization integrals and assembly.
682 
683   Input Parameters:
684 + dm    - The DM
685 . alpha - The first Euler angle, and in 2D the only one
686 . beta  - The second Euler angle
687 . gamma - The third Euler angle
688 
689   Note: Following https://en.wikipedia.org/wiki/Euler_angles, we will specify Euler angles by extrinsic rotations, meaning that
690   we rotate with respect to a fixed initial coordinate system, the local basis (x-y-z). The global basis (X-Y-Z) is reached as follows:
691   $ The XYZ system rotates about the z axis by alpha. The X axis is now at angle alpha with respect to the x axis.
692   $ The XYZ system rotates again about the x axis by beta. The Z axis is now at angle beta with respect to the z axis.
693   $ The XYZ system rotates a third time about the z axis by gamma.
694 
695   Level: developer
696 
697 .seealso: DMPlexGlobalToLocalBasis(), DMPlexLocalToGlobalBasis()
698 @*/
699 PetscErrorCode DMPlexCreateBasisRotation(DM dm, PetscReal alpha, PetscReal beta, PetscReal gamma)
700 {
701   RotCtx        *rc;
702   PetscInt       cdim;
703   PetscErrorCode ierr;
704 
705   ierr = DMGetCoordinateDim(dm, &cdim);CHKERRQ(ierr);
706   ierr = PetscMalloc1(1, &rc);CHKERRQ(ierr);
707   dm->transformCtx       = rc;
708   dm->transformSetUp     = DMPlexBasisTransformSetUp_Rotation_Internal;
709   dm->transformDestroy   = DMPlexBasisTransformDestroy_Rotation_Internal;
710   dm->transformGetMatrix = DMPlexBasisTransformGetMatrix_Rotation_Internal;
711   rc->dim   = cdim;
712   rc->alpha = alpha;
713   rc->beta  = beta;
714   rc->gamma = gamma;
715   ierr = (*dm->transformSetUp)(dm, dm->transformCtx);CHKERRQ(ierr);
716   ierr = DMConstructBasisTransform_Internal(dm);CHKERRQ(ierr);
717   PetscFunctionReturn(0);
718 }
719 
720 /*@C
721   DMPlexInsertBoundaryValuesEssential - Insert boundary values into a local vector
722 
723   Input Parameters:
724 + dm     - The DM, with a PetscDS that matches the problem being constrained
725 . time   - The time
726 . field  - The field to constrain
727 . Nc     - The number of constrained field components, or 0 for all components
728 . comps  - An array of constrained component numbers, or NULL for all components
729 . label  - The DMLabel defining constrained points
730 . numids - The number of DMLabel ids for constrained points
731 . ids    - An array of ids for constrained points
732 . func   - A pointwise function giving boundary values
733 - ctx    - An optional user context for bcFunc
734 
735   Output Parameter:
736 . locX   - A local vector to receives the boundary values
737 
738   Level: developer
739 
740 .seealso: DMPlexInsertBoundaryValuesEssentialField(), DMAddBoundary()
741 @*/
742 PetscErrorCode DMPlexInsertBoundaryValuesEssential(DM dm, PetscReal time, PetscInt field, PetscInt Nc, const PetscInt comps[], DMLabel label, PetscInt numids, const PetscInt ids[], PetscErrorCode (*func)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void *ctx, Vec locX)
743 {
744   PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal x[], PetscInt, PetscScalar *u, void *ctx);
745   void            **ctxs;
746   PetscInt          numFields;
747   PetscErrorCode    ierr;
748 
749   PetscFunctionBegin;
750   ierr = DMGetNumFields(dm, &numFields);CHKERRQ(ierr);
751   ierr = PetscCalloc2(numFields,&funcs,numFields,&ctxs);CHKERRQ(ierr);
752   funcs[field] = func;
753   ctxs[field]  = ctx;
754   ierr = DMProjectFunctionLabelLocal(dm, time, label, numids, ids, Nc, comps, funcs, ctxs, INSERT_BC_VALUES, locX);CHKERRQ(ierr);
755   ierr = PetscFree2(funcs,ctxs);CHKERRQ(ierr);
756   PetscFunctionReturn(0);
757 }
758 
759 /*@C
760   DMPlexInsertBoundaryValuesEssentialField - Insert boundary values into a local vector
761 
762   Input Parameters:
763 + dm     - The DM, with a PetscDS that matches the problem being constrained
764 . time   - The time
765 . locU   - A local vector with the input solution values
766 . field  - The field to constrain
767 . Nc     - The number of constrained field components, or 0 for all components
768 . comps  - An array of constrained component numbers, or NULL for all components
769 . label  - The DMLabel defining constrained points
770 . numids - The number of DMLabel ids for constrained points
771 . ids    - An array of ids for constrained points
772 . func   - A pointwise function giving boundary values
773 - ctx    - An optional user context for bcFunc
774 
775   Output Parameter:
776 . locX   - A local vector to receives the boundary values
777 
778   Level: developer
779 
780 .seealso: DMPlexInsertBoundaryValuesEssential(), DMAddBoundary()
781 @*/
782 PetscErrorCode DMPlexInsertBoundaryValuesEssentialField(DM dm, PetscReal time, Vec locU, PetscInt field, PetscInt Nc, const PetscInt comps[], DMLabel label, PetscInt numids, const PetscInt ids[],
783                                                         void (*func)(PetscInt, PetscInt, PetscInt,
784                                                                      const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[],
785                                                                      const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[],
786                                                                      PetscReal, const PetscReal[], PetscInt, const PetscScalar[],
787                                                                      PetscScalar[]),
788                                                         void *ctx, Vec locX)
789 {
790   void (**funcs)(PetscInt, PetscInt, PetscInt,
791                  const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[],
792                  const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[],
793                  PetscReal, const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]);
794   void            **ctxs;
795   PetscInt          numFields;
796   PetscErrorCode    ierr;
797 
798   PetscFunctionBegin;
799   ierr = DMGetNumFields(dm, &numFields);CHKERRQ(ierr);
800   ierr = PetscCalloc2(numFields,&funcs,numFields,&ctxs);CHKERRQ(ierr);
801   funcs[field] = func;
802   ctxs[field]  = ctx;
803   ierr = DMProjectFieldLabelLocal(dm, time, label, numids, ids, Nc, comps, locU, funcs, INSERT_BC_VALUES, locX);CHKERRQ(ierr);
804   ierr = PetscFree2(funcs,ctxs);CHKERRQ(ierr);
805   PetscFunctionReturn(0);
806 }
807 
808 /*@C
809   DMPlexInsertBoundaryValuesRiemann - Insert boundary values into a local vector
810 
811   Input Parameters:
812 + dm     - The DM, with a PetscDS that matches the problem being constrained
813 . time   - The time
814 . faceGeometry - A vector with the FVM face geometry information
815 . cellGeometry - A vector with the FVM cell geometry information
816 . Grad         - A vector with the FVM cell gradient information
817 . field  - The field to constrain
818 . Nc     - The number of constrained field components, or 0 for all components
819 . comps  - An array of constrained component numbers, or NULL for all components
820 . label  - The DMLabel defining constrained points
821 . numids - The number of DMLabel ids for constrained points
822 . ids    - An array of ids for constrained points
823 . func   - A pointwise function giving boundary values
824 - ctx    - An optional user context for bcFunc
825 
826   Output Parameter:
827 . locX   - A local vector to receives the boundary values
828 
829   Note: This implementation currently ignores the numcomps/comps argument from DMAddBoundary()
830 
831   Level: developer
832 
833 .seealso: DMPlexInsertBoundaryValuesEssential(), DMPlexInsertBoundaryValuesEssentialField(), DMAddBoundary()
834 @*/
835 PetscErrorCode DMPlexInsertBoundaryValuesRiemann(DM dm, PetscReal time, Vec faceGeometry, Vec cellGeometry, Vec Grad, PetscInt field, PetscInt Nc, const PetscInt comps[], DMLabel label, PetscInt numids, const PetscInt ids[],
836                                                  PetscErrorCode (*func)(PetscReal,const PetscReal*,const PetscReal*,const PetscScalar*,PetscScalar*,void*), void *ctx, Vec locX)
837 {
838   PetscDS            prob;
839   PetscSF            sf;
840   DM                 dmFace, dmCell, dmGrad;
841   const PetscScalar *facegeom, *cellgeom = NULL, *grad;
842   const PetscInt    *leaves;
843   PetscScalar       *x, *fx;
844   PetscInt           dim, nleaves, loc, fStart, fEnd, pdim, i;
845   PetscErrorCode     ierr, ierru = 0;
846 
847   PetscFunctionBegin;
848   ierr = DMGetPointSF(dm, &sf);CHKERRQ(ierr);
849   ierr = PetscSFGetGraph(sf, NULL, &nleaves, &leaves, NULL);CHKERRQ(ierr);
850   nleaves = PetscMax(0, nleaves);
851   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
852   ierr = DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd);CHKERRQ(ierr);
853   ierr = DMGetDS(dm, &prob);CHKERRQ(ierr);
854   ierr = VecGetDM(faceGeometry, &dmFace);CHKERRQ(ierr);
855   ierr = VecGetArrayRead(faceGeometry, &facegeom);CHKERRQ(ierr);
856   if (cellGeometry) {
857     ierr = VecGetDM(cellGeometry, &dmCell);CHKERRQ(ierr);
858     ierr = VecGetArrayRead(cellGeometry, &cellgeom);CHKERRQ(ierr);
859   }
860   if (Grad) {
861     PetscFV fv;
862 
863     ierr = PetscDSGetDiscretization(prob, field, (PetscObject *) &fv);CHKERRQ(ierr);
864     ierr = VecGetDM(Grad, &dmGrad);CHKERRQ(ierr);
865     ierr = VecGetArrayRead(Grad, &grad);CHKERRQ(ierr);
866     ierr = PetscFVGetNumComponents(fv, &pdim);CHKERRQ(ierr);
867     ierr = DMGetWorkArray(dm, pdim, MPIU_SCALAR, &fx);CHKERRQ(ierr);
868   }
869   ierr = VecGetArray(locX, &x);CHKERRQ(ierr);
870   for (i = 0; i < numids; ++i) {
871     IS              faceIS;
872     const PetscInt *faces;
873     PetscInt        numFaces, f;
874 
875     ierr = DMLabelGetStratumIS(label, ids[i], &faceIS);CHKERRQ(ierr);
876     if (!faceIS) continue; /* No points with that id on this process */
877     ierr = ISGetLocalSize(faceIS, &numFaces);CHKERRQ(ierr);
878     ierr = ISGetIndices(faceIS, &faces);CHKERRQ(ierr);
879     for (f = 0; f < numFaces; ++f) {
880       const PetscInt         face = faces[f], *cells;
881       PetscFVFaceGeom        *fg;
882 
883       if ((face < fStart) || (face >= fEnd)) continue; /* Refinement adds non-faces to labels */
884       ierr = PetscFindInt(face, nleaves, (PetscInt *) leaves, &loc);CHKERRQ(ierr);
885       if (loc >= 0) continue;
886       ierr = DMPlexPointLocalRead(dmFace, face, facegeom, &fg);CHKERRQ(ierr);
887       ierr = DMPlexGetSupport(dm, face, &cells);CHKERRQ(ierr);
888       if (Grad) {
889         PetscFVCellGeom       *cg;
890         PetscScalar           *cx, *cgrad;
891         PetscScalar           *xG;
892         PetscReal              dx[3];
893         PetscInt               d;
894 
895         ierr = DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cg);CHKERRQ(ierr);
896         ierr = DMPlexPointLocalRead(dm, cells[0], x, &cx);CHKERRQ(ierr);
897         ierr = DMPlexPointLocalRead(dmGrad, cells[0], grad, &cgrad);CHKERRQ(ierr);
898         ierr = DMPlexPointLocalFieldRef(dm, cells[1], field, x, &xG);CHKERRQ(ierr);
899         DMPlex_WaxpyD_Internal(dim, -1, cg->centroid, fg->centroid, dx);
900         for (d = 0; d < pdim; ++d) fx[d] = cx[d] + DMPlex_DotD_Internal(dim, &cgrad[d*dim], dx);
901         ierru = (*func)(time, fg->centroid, fg->normal, fx, xG, ctx);
902         if (ierru) {
903           ierr = ISRestoreIndices(faceIS, &faces);CHKERRQ(ierr);
904           ierr = ISDestroy(&faceIS);CHKERRQ(ierr);
905           goto cleanup;
906         }
907       } else {
908         PetscScalar       *xI;
909         PetscScalar       *xG;
910 
911         ierr = DMPlexPointLocalRead(dm, cells[0], x, &xI);CHKERRQ(ierr);
912         ierr = DMPlexPointLocalFieldRef(dm, cells[1], field, x, &xG);CHKERRQ(ierr);
913         ierru = (*func)(time, fg->centroid, fg->normal, xI, xG, ctx);
914         if (ierru) {
915           ierr = ISRestoreIndices(faceIS, &faces);CHKERRQ(ierr);
916           ierr = ISDestroy(&faceIS);CHKERRQ(ierr);
917           goto cleanup;
918         }
919       }
920     }
921     ierr = ISRestoreIndices(faceIS, &faces);CHKERRQ(ierr);
922     ierr = ISDestroy(&faceIS);CHKERRQ(ierr);
923   }
924   cleanup:
925   ierr = VecRestoreArray(locX, &x);CHKERRQ(ierr);
926   if (Grad) {
927     ierr = DMRestoreWorkArray(dm, pdim, MPIU_SCALAR, &fx);CHKERRQ(ierr);
928     ierr = VecRestoreArrayRead(Grad, &grad);CHKERRQ(ierr);
929   }
930   if (cellGeometry) {ierr = VecRestoreArrayRead(cellGeometry, &cellgeom);CHKERRQ(ierr);}
931   ierr = VecRestoreArrayRead(faceGeometry, &facegeom);CHKERRQ(ierr);
932   CHKERRQ(ierru);
933   PetscFunctionReturn(0);
934 }
935 
936 PetscErrorCode DMPlexInsertBoundaryValues_Plex(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
937 {
938   PetscDS        prob;
939   PetscInt       numBd, b;
940   PetscErrorCode ierr;
941 
942   PetscFunctionBegin;
943   ierr = DMGetDS(dm, &prob);CHKERRQ(ierr);
944   ierr = PetscDSGetNumBoundary(prob, &numBd);CHKERRQ(ierr);
945   for (b = 0; b < numBd; ++b) {
946     DMBoundaryConditionType type;
947     const char             *name, *labelname;
948     DMLabel                 label;
949     PetscInt                field, Nc;
950     const PetscInt         *comps;
951     PetscObject             obj;
952     PetscClassId            id;
953     void                    (*func)(void);
954     PetscInt                numids;
955     const PetscInt         *ids;
956     void                   *ctx;
957 
958     ierr = DMGetBoundary(dm, b, &type, &name, &labelname, &field, &Nc, &comps, &func, &numids, &ids, &ctx);CHKERRQ(ierr);
959     if (insertEssential != (type & DM_BC_ESSENTIAL)) continue;
960     ierr = DMGetLabel(dm, labelname, &label);CHKERRQ(ierr);
961     if (!label) SETERRQ2(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONGSTATE, "Label %s for boundary condition %s does not exist in the DM", labelname, name);
962     ierr = DMGetField(dm, field, NULL, &obj);CHKERRQ(ierr);
963     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
964     if (id == PETSCFE_CLASSID) {
965       switch (type) {
966         /* for FEM, there is no insertion to be done for non-essential boundary conditions */
967       case DM_BC_ESSENTIAL:
968         ierr = DMPlexLabelAddCells(dm,label);CHKERRQ(ierr);
969         ierr = DMPlexInsertBoundaryValuesEssential(dm, time, field, Nc, comps, label, numids, ids, (PetscErrorCode (*)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *)) func, ctx, locX);CHKERRQ(ierr);
970         ierr = DMPlexLabelClearCells(dm,label);CHKERRQ(ierr);
971         break;
972       case DM_BC_ESSENTIAL_FIELD:
973         ierr = DMPlexLabelAddCells(dm,label);CHKERRQ(ierr);
974         ierr = DMPlexInsertBoundaryValuesEssentialField(dm, time, locX, field, Nc, comps, label, numids, ids,
975                                                         (void (*)(PetscInt, PetscInt, PetscInt, const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[],
976                                                                   const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[],
977                                                                   PetscReal, const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[])) func, ctx, locX);CHKERRQ(ierr);
978         ierr = DMPlexLabelClearCells(dm,label);CHKERRQ(ierr);
979         break;
980       default: break;
981       }
982     } else if (id == PETSCFV_CLASSID) {
983       if (!faceGeomFVM) continue;
984       ierr = DMPlexInsertBoundaryValuesRiemann(dm, time, faceGeomFVM, cellGeomFVM, gradFVM, field, Nc, comps, label, numids, ids,
985                                                (PetscErrorCode (*)(PetscReal,const PetscReal*,const PetscReal*,const PetscScalar*,PetscScalar*,void*)) func, ctx, locX);CHKERRQ(ierr);
986     } else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", field);
987   }
988   PetscFunctionReturn(0);
989 }
990 
991 /*@
992   DMPlexInsertBoundaryValues - Puts coefficients which represent boundary values into the local solution vector
993 
994   Input Parameters:
995 + dm - The DM
996 . insertEssential - Should I insert essential (e.g. Dirichlet) or inessential (e.g. Neumann) boundary conditions
997 . time - The time
998 . faceGeomFVM - Face geometry data for FV discretizations
999 . cellGeomFVM - Cell geometry data for FV discretizations
1000 - gradFVM - Gradient reconstruction data for FV discretizations
1001 
1002   Output Parameters:
1003 . locX - Solution updated with boundary values
1004 
1005   Level: developer
1006 
1007 .seealso: DMProjectFunctionLabelLocal()
1008 @*/
1009 PetscErrorCode DMPlexInsertBoundaryValues(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
1010 {
1011   PetscErrorCode ierr;
1012 
1013   PetscFunctionBegin;
1014   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1015   PetscValidHeaderSpecific(locX, VEC_CLASSID, 2);
1016   if (faceGeomFVM) {PetscValidHeaderSpecific(faceGeomFVM, VEC_CLASSID, 4);}
1017   if (cellGeomFVM) {PetscValidHeaderSpecific(cellGeomFVM, VEC_CLASSID, 5);}
1018   if (gradFVM)     {PetscValidHeaderSpecific(gradFVM, VEC_CLASSID, 6);}
1019   ierr = PetscTryMethod(dm,"DMPlexInsertBoundaryValues_C",(DM,PetscBool,Vec,PetscReal,Vec,Vec,Vec),(dm,insertEssential,locX,time,faceGeomFVM,cellGeomFVM,gradFVM));CHKERRQ(ierr);
1020   PetscFunctionReturn(0);
1021 }
1022 
1023 PetscErrorCode DMComputeL2Diff_Plex(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal [], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, PetscReal *diff)
1024 {
1025   Vec              localX;
1026   PetscErrorCode   ierr;
1027 
1028   PetscFunctionBegin;
1029   ierr = DMGetLocalVector(dm, &localX);CHKERRQ(ierr);
1030   ierr = DMPlexInsertBoundaryValues(dm, PETSC_TRUE, localX, time, NULL, NULL, NULL);CHKERRQ(ierr);
1031   ierr = DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX);CHKERRQ(ierr);
1032   ierr = DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX);CHKERRQ(ierr);
1033   ierr = DMPlexComputeL2DiffLocal(dm, time, funcs, ctxs, localX, diff);CHKERRQ(ierr);
1034   ierr = DMRestoreLocalVector(dm, &localX);CHKERRQ(ierr);
1035   PetscFunctionReturn(0);
1036 }
1037 
1038 /*@C
1039   DMComputeL2DiffLocal - This function computes the L_2 difference between a function u and an FEM interpolant solution u_h.
1040 
1041   Collective on dm
1042 
1043   Input Parameters:
1044 + dm     - The DM
1045 . time   - The time
1046 . funcs  - The functions to evaluate for each field component
1047 . ctxs   - Optional array of contexts to pass to each function, or NULL.
1048 - localX - The coefficient vector u_h, a local vector
1049 
1050   Output Parameter:
1051 . diff - The diff ||u - u_h||_2
1052 
1053   Level: developer
1054 
1055 .seealso: DMProjectFunction(), DMComputeL2FieldDiff(), DMComputeL2GradientDiff()
1056 @*/
1057 PetscErrorCode DMPlexComputeL2DiffLocal(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal [], PetscInt, PetscScalar *, void *), void **ctxs, Vec localX, PetscReal *diff)
1058 {
1059   const PetscInt   debug = ((DM_Plex*)dm->data)->printL2;
1060   DM               tdm;
1061   Vec              tv;
1062   PetscSection     section;
1063   PetscQuadrature  quad;
1064   PetscFEGeom      fegeom;
1065   PetscScalar     *funcVal, *interpolant;
1066   PetscReal       *coords, *gcoords;
1067   PetscReal        localDiff = 0.0;
1068   const PetscReal *quadWeights;
1069   PetscInt         dim, coordDim, numFields, numComponents = 0, qNc, Nq, cellHeight, cStart, cEnd, cEndInterior, c, field, fieldOffset;
1070   PetscBool        transform;
1071   PetscErrorCode   ierr;
1072 
1073   PetscFunctionBegin;
1074   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
1075   ierr = DMGetCoordinateDim(dm, &coordDim);CHKERRQ(ierr);
1076   fegeom.dimEmbed = coordDim;
1077   ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);
1078   ierr = PetscSectionGetNumFields(section, &numFields);CHKERRQ(ierr);
1079   ierr = DMGetBasisTransformDM_Internal(dm, &tdm);CHKERRQ(ierr);
1080   ierr = DMGetBasisTransformVec_Internal(dm, &tv);CHKERRQ(ierr);
1081   ierr = DMHasBasisTransform(dm, &transform);CHKERRQ(ierr);
1082   for (field = 0; field < numFields; ++field) {
1083     PetscObject  obj;
1084     PetscClassId id;
1085     PetscInt     Nc;
1086 
1087     ierr = DMGetField(dm, field, NULL, &obj);CHKERRQ(ierr);
1088     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
1089     if (id == PETSCFE_CLASSID) {
1090       PetscFE fe = (PetscFE) obj;
1091 
1092       ierr = PetscFEGetQuadrature(fe, &quad);CHKERRQ(ierr);
1093       ierr = PetscFEGetNumComponents(fe, &Nc);CHKERRQ(ierr);
1094     } else if (id == PETSCFV_CLASSID) {
1095       PetscFV fv = (PetscFV) obj;
1096 
1097       ierr = PetscFVGetQuadrature(fv, &quad);CHKERRQ(ierr);
1098       ierr = PetscFVGetNumComponents(fv, &Nc);CHKERRQ(ierr);
1099     } else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", field);
1100     numComponents += Nc;
1101   }
1102   ierr = PetscQuadratureGetData(quad, NULL, &qNc, &Nq, NULL, &quadWeights);CHKERRQ(ierr);
1103   if ((qNc != 1) && (qNc != numComponents)) SETERRQ2(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_SIZ, "Quadrature components %D != %D field components", qNc, numComponents);
1104   ierr = PetscMalloc6(numComponents,&funcVal,numComponents,&interpolant,coordDim*Nq,&coords,Nq,&fegeom.detJ,coordDim*coordDim*Nq,&fegeom.J,coordDim*coordDim*Nq,&fegeom.invJ);CHKERRQ(ierr);
1105   ierr = DMPlexGetVTKCellHeight(dm, &cellHeight);CHKERRQ(ierr);
1106   ierr = DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd);CHKERRQ(ierr);
1107   ierr = DMPlexGetGhostCellStratum(dm, &cEndInterior, NULL);CHKERRQ(ierr);
1108   cEnd = cEndInterior < 0 ? cEnd : cEndInterior;
1109   ierr = DMPlexGetHybridBounds(dm, &cEndInterior, NULL, NULL, NULL);CHKERRQ(ierr);
1110   cEnd = cEndInterior < 0 ? cEnd : cEndInterior;
1111   for (c = cStart; c < cEnd; ++c) {
1112     PetscScalar *x = NULL;
1113     PetscReal    elemDiff = 0.0;
1114     PetscInt     qc = 0;
1115 
1116     ierr = DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ);CHKERRQ(ierr);
1117     ierr = DMPlexVecGetClosure(dm, NULL, localX, c, NULL, &x);CHKERRQ(ierr);
1118 
1119     for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1120       PetscObject  obj;
1121       PetscClassId id;
1122       void * const ctx = ctxs ? ctxs[field] : NULL;
1123       PetscInt     Nb, Nc, q, fc;
1124 
1125       ierr = DMGetField(dm, field, NULL, &obj);CHKERRQ(ierr);
1126       ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
1127       if (id == PETSCFE_CLASSID)      {ierr = PetscFEGetNumComponents((PetscFE) obj, &Nc);CHKERRQ(ierr);ierr = PetscFEGetDimension((PetscFE) obj, &Nb);CHKERRQ(ierr);}
1128       else if (id == PETSCFV_CLASSID) {ierr = PetscFVGetNumComponents((PetscFV) obj, &Nc);CHKERRQ(ierr);Nb = 1;}
1129       else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", field);
1130       if (debug) {
1131         char title[1024];
1132         ierr = PetscSNPrintf(title, 1023, "Solution for Field %D", field);CHKERRQ(ierr);
1133         ierr = DMPrintCellVector(c, title, Nb, &x[fieldOffset]);CHKERRQ(ierr);
1134       }
1135       for (q = 0; q < Nq; ++q) {
1136         PetscFEGeom qgeom;
1137 
1138         qgeom.dimEmbed = fegeom.dimEmbed;
1139         qgeom.J        = &fegeom.J[q*coordDim*coordDim];
1140         qgeom.invJ     = &fegeom.invJ[q*coordDim*coordDim];
1141         qgeom.detJ     = &fegeom.detJ[q];
1142         if (fegeom.detJ[q] <= 0.0) SETERRQ3(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for element %D, point %D", (double)fegeom.detJ[q], c, q);
1143         if (transform) {
1144           gcoords = &coords[coordDim*Nq];
1145           ierr = DMPlexBasisTransformApplyReal_Internal(dm, &coords[coordDim*q], PETSC_TRUE, coordDim, &coords[coordDim*q], gcoords, dm->transformCtx);CHKERRQ(ierr);
1146         } else {
1147           gcoords = &coords[coordDim*q];
1148         }
1149         ierr = (*funcs[field])(coordDim, time, gcoords, Nc, funcVal, ctx);
1150         if (ierr) {
1151           PetscErrorCode ierr2;
1152           ierr2 = DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x);CHKERRQ(ierr2);
1153           ierr2 = DMRestoreLocalVector(dm, &localX);CHKERRQ(ierr2);
1154           ierr2 = PetscFree6(funcVal,interpolant,coords,fegeom.detJ,fegeom.J,fegeom.invJ);CHKERRQ(ierr2);
1155           CHKERRQ(ierr);
1156         }
1157         if (transform) {ierr = DMPlexBasisTransformApply_Internal(dm, &coords[coordDim*q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx);CHKERRQ(ierr);}
1158         if (id == PETSCFE_CLASSID)      {ierr = PetscFEInterpolate_Static((PetscFE) obj, &x[fieldOffset], &qgeom, q, interpolant);CHKERRQ(ierr);}
1159         else if (id == PETSCFV_CLASSID) {ierr = PetscFVInterpolate_Static((PetscFV) obj, &x[fieldOffset], q, interpolant);CHKERRQ(ierr);}
1160         else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", field);
1161         for (fc = 0; fc < Nc; ++fc) {
1162           const PetscReal wt = quadWeights[q*qNc+(qNc == 1 ? 0 : qc+fc)];
1163           if (debug) {ierr = PetscPrintf(PETSC_COMM_SELF, "    elem %D field %D,%D diff %g\n", c, field, fc, (double)(PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc]))*wt*fegeom.detJ[q]));CHKERRQ(ierr);}
1164           elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc]))*wt*fegeom.detJ[q];
1165         }
1166       }
1167       fieldOffset += Nb;
1168       qc += Nc;
1169     }
1170     ierr = DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x);CHKERRQ(ierr);
1171     if (debug) {ierr = PetscPrintf(PETSC_COMM_SELF, "  elem %D diff %g\n", c, (double)elemDiff);CHKERRQ(ierr);}
1172     localDiff += elemDiff;
1173   }
1174   ierr  = PetscFree6(funcVal,interpolant,coords,fegeom.detJ,fegeom.J,fegeom.invJ);CHKERRQ(ierr);
1175   ierr  = MPIU_Allreduce(&localDiff, diff, 1, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm));CHKERRQ(ierr);
1176   *diff = PetscSqrtReal(*diff);
1177   PetscFunctionReturn(0);
1178 }
1179 
1180 PetscErrorCode DMComputeL2GradientDiff_Plex(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal [], const PetscReal [], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, const PetscReal n[], PetscReal *diff)
1181 {
1182   const PetscInt   debug = ((DM_Plex*)dm->data)->printL2;
1183   DM               tdm;
1184   PetscSection     section;
1185   PetscQuadrature  quad;
1186   Vec              localX, tv;
1187   PetscScalar     *funcVal, *interpolant;
1188   const PetscReal *quadWeights;
1189   PetscFEGeom      fegeom;
1190   PetscReal       *coords, *gcoords;
1191   PetscReal        localDiff = 0.0;
1192   PetscInt         dim, coordDim, qNc = 0, Nq = 0, numFields, numComponents = 0, cStart, cEnd, c, field, fieldOffset;
1193   PetscBool        transform;
1194   PetscErrorCode   ierr;
1195 
1196   PetscFunctionBegin;
1197   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
1198   ierr = DMGetCoordinateDim(dm, &coordDim);CHKERRQ(ierr);
1199   fegeom.dimEmbed = coordDim;
1200   ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);
1201   ierr = PetscSectionGetNumFields(section, &numFields);CHKERRQ(ierr);
1202   ierr = DMGetLocalVector(dm, &localX);CHKERRQ(ierr);
1203   ierr = DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX);CHKERRQ(ierr);
1204   ierr = DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX);CHKERRQ(ierr);
1205   ierr = DMGetBasisTransformDM_Internal(dm, &tdm);CHKERRQ(ierr);
1206   ierr = DMGetBasisTransformVec_Internal(dm, &tv);CHKERRQ(ierr);
1207   ierr = DMHasBasisTransform(dm, &transform);CHKERRQ(ierr);
1208   for (field = 0; field < numFields; ++field) {
1209     PetscFE  fe;
1210     PetscInt Nc;
1211 
1212     ierr = DMGetField(dm, field, NULL, (PetscObject *) &fe);CHKERRQ(ierr);
1213     ierr = PetscFEGetQuadrature(fe, &quad);CHKERRQ(ierr);
1214     ierr = PetscFEGetNumComponents(fe, &Nc);CHKERRQ(ierr);
1215     numComponents += Nc;
1216   }
1217   ierr = PetscQuadratureGetData(quad, NULL, &qNc, &Nq, NULL, &quadWeights);CHKERRQ(ierr);
1218   if ((qNc != 1) && (qNc != numComponents)) SETERRQ2(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_SIZ, "Quadrature components %D != %D field components", qNc, numComponents);
1219   /* ierr = DMProjectFunctionLocal(dm, fe, funcs, INSERT_BC_VALUES, localX);CHKERRQ(ierr); */
1220   ierr = PetscMalloc6(numComponents,&funcVal,coordDim*Nq,&coords,coordDim*coordDim*Nq,&fegeom.J,coordDim*coordDim*Nq,&fegeom.invJ,numComponents*coordDim,&interpolant,Nq,&fegeom.detJ);CHKERRQ(ierr);
1221   ierr = DMPlexGetInteriorCellStratum(dm, &cStart, &cEnd);CHKERRQ(ierr);
1222   for (c = cStart; c < cEnd; ++c) {
1223     PetscScalar *x = NULL;
1224     PetscReal    elemDiff = 0.0;
1225     PetscInt     qc = 0;
1226 
1227     ierr = DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ);CHKERRQ(ierr);
1228     ierr = DMPlexVecGetClosure(dm, NULL, localX, c, NULL, &x);CHKERRQ(ierr);
1229 
1230     for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1231       PetscFE          fe;
1232       void * const     ctx = ctxs ? ctxs[field] : NULL;
1233       PetscInt         Nb, Nc, q, fc;
1234 
1235       ierr = DMGetField(dm, field, NULL, (PetscObject *) &fe);CHKERRQ(ierr);
1236       ierr = PetscFEGetDimension(fe, &Nb);CHKERRQ(ierr);
1237       ierr = PetscFEGetNumComponents(fe, &Nc);CHKERRQ(ierr);
1238       if (debug) {
1239         char title[1024];
1240         ierr = PetscSNPrintf(title, 1023, "Solution for Field %D", field);CHKERRQ(ierr);
1241         ierr = DMPrintCellVector(c, title, Nb, &x[fieldOffset]);CHKERRQ(ierr);
1242       }
1243       for (q = 0; q < Nq; ++q) {
1244         PetscFEGeom qgeom;
1245 
1246         qgeom.dimEmbed = fegeom.dimEmbed;
1247         qgeom.J        = &fegeom.J[q*coordDim*coordDim];
1248         qgeom.invJ     = &fegeom.invJ[q*coordDim*coordDim];
1249         qgeom.detJ     = &fegeom.detJ[q];
1250         if (fegeom.detJ[q] <= 0.0) SETERRQ3(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for element %D, quadrature points %D", (double)fegeom.detJ[q], c, q);
1251         if (transform) {
1252           gcoords = &coords[coordDim*Nq];
1253           ierr = DMPlexBasisTransformApplyReal_Internal(dm, &coords[coordDim*q], PETSC_TRUE, coordDim, &coords[coordDim*q], gcoords, dm->transformCtx);CHKERRQ(ierr);
1254         } else {
1255           gcoords = &coords[coordDim*q];
1256         }
1257         ierr = (*funcs[field])(coordDim, time, gcoords, n, Nc, funcVal, ctx);
1258         if (ierr) {
1259           PetscErrorCode ierr2;
1260           ierr2 = DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x);CHKERRQ(ierr2);
1261           ierr2 = DMRestoreLocalVector(dm, &localX);CHKERRQ(ierr2);
1262           ierr2 = PetscFree6(funcVal,coords,fegeom.J,fegeom.invJ,interpolant,fegeom.detJ);CHKERRQ(ierr2);
1263           CHKERRQ(ierr);
1264         }
1265         if (transform) {ierr = DMPlexBasisTransformApply_Internal(dm, &coords[coordDim*q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx);CHKERRQ(ierr);}
1266         ierr = PetscFEInterpolateGradient_Static(fe, &x[fieldOffset], &qgeom, q, interpolant);CHKERRQ(ierr);
1267         /* Overwrite with the dot product if the normal is given */
1268         if (n) {
1269           for (fc = 0; fc < Nc; ++fc) {
1270             PetscScalar sum = 0.0;
1271             PetscInt    d;
1272             for (d = 0; d < dim; ++d) sum += interpolant[fc*dim+d]*n[d];
1273             interpolant[fc] = sum;
1274           }
1275         }
1276         for (fc = 0; fc < Nc; ++fc) {
1277           const PetscReal wt = quadWeights[q*qNc+(qNc == 1 ? 0 : qc+fc)];
1278           if (debug) {ierr = PetscPrintf(PETSC_COMM_SELF, "    elem %D fieldDer %D,%D diff %g\n", c, field, fc, (double)(PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc]))*wt*fegeom.detJ[q]));CHKERRQ(ierr);}
1279           elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc]))*wt*fegeom.detJ[q];
1280         }
1281       }
1282       fieldOffset += Nb;
1283       qc          += Nc;
1284     }
1285     ierr = DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x);CHKERRQ(ierr);
1286     if (debug) {ierr = PetscPrintf(PETSC_COMM_SELF, "  elem %D diff %g\n", c, (double)elemDiff);CHKERRQ(ierr);}
1287     localDiff += elemDiff;
1288   }
1289   ierr  = PetscFree6(funcVal,coords,fegeom.J,fegeom.invJ,interpolant,fegeom.detJ);CHKERRQ(ierr);
1290   ierr  = DMRestoreLocalVector(dm, &localX);CHKERRQ(ierr);
1291   ierr  = MPIU_Allreduce(&localDiff, diff, 1, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm));CHKERRQ(ierr);
1292   *diff = PetscSqrtReal(*diff);
1293   PetscFunctionReturn(0);
1294 }
1295 
1296 PetscErrorCode DMComputeL2FieldDiff_Plex(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal [], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, PetscReal *diff)
1297 {
1298   const PetscInt   debug = ((DM_Plex*)dm->data)->printL2;
1299   DM               tdm;
1300   PetscSection     section;
1301   PetscQuadrature  quad;
1302   Vec              localX, tv;
1303   PetscFEGeom      fegeom;
1304   PetscScalar     *funcVal, *interpolant;
1305   PetscReal       *coords, *gcoords;
1306   PetscReal       *localDiff;
1307   const PetscReal *quadPoints, *quadWeights;
1308   PetscInt         dim, coordDim, numFields, numComponents = 0, qNc, Nq, cStart, cEnd, c, field, fieldOffset;
1309   PetscBool        transform;
1310   PetscErrorCode   ierr;
1311 
1312   PetscFunctionBegin;
1313   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
1314   ierr = DMGetCoordinateDim(dm, &coordDim);CHKERRQ(ierr);
1315   ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);
1316   ierr = PetscSectionGetNumFields(section, &numFields);CHKERRQ(ierr);
1317   ierr = DMGetLocalVector(dm, &localX);CHKERRQ(ierr);
1318   ierr = VecSet(localX, 0.0);CHKERRQ(ierr);
1319   ierr = DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX);CHKERRQ(ierr);
1320   ierr = DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX);CHKERRQ(ierr);
1321   ierr = DMProjectFunctionLocal(dm, time, funcs, ctxs, INSERT_BC_VALUES, localX);CHKERRQ(ierr);
1322   ierr = DMGetBasisTransformDM_Internal(dm, &tdm);CHKERRQ(ierr);
1323   ierr = DMGetBasisTransformVec_Internal(dm, &tv);CHKERRQ(ierr);
1324   ierr = DMHasBasisTransform(dm, &transform);CHKERRQ(ierr);
1325   for (field = 0; field < numFields; ++field) {
1326     PetscObject  obj;
1327     PetscClassId id;
1328     PetscInt     Nc;
1329 
1330     ierr = DMGetField(dm, field, NULL, &obj);CHKERRQ(ierr);
1331     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
1332     if (id == PETSCFE_CLASSID) {
1333       PetscFE fe = (PetscFE) obj;
1334 
1335       ierr = PetscFEGetQuadrature(fe, &quad);CHKERRQ(ierr);
1336       ierr = PetscFEGetNumComponents(fe, &Nc);CHKERRQ(ierr);
1337     } else if (id == PETSCFV_CLASSID) {
1338       PetscFV fv = (PetscFV) obj;
1339 
1340       ierr = PetscFVGetQuadrature(fv, &quad);CHKERRQ(ierr);
1341       ierr = PetscFVGetNumComponents(fv, &Nc);CHKERRQ(ierr);
1342     } else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", field);
1343     numComponents += Nc;
1344   }
1345   ierr = PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights);CHKERRQ(ierr);
1346   if ((qNc != 1) && (qNc != numComponents)) SETERRQ2(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_SIZ, "Quadrature components %D != %D field components", qNc, numComponents);
1347   ierr = PetscCalloc7(numFields,&localDiff,numComponents,&funcVal,numComponents,&interpolant,coordDim*(Nq+1),&coords,Nq,&fegeom.detJ,coordDim*coordDim*Nq,&fegeom.J,coordDim*coordDim*Nq,&fegeom.invJ);CHKERRQ(ierr);
1348   ierr = DMPlexGetInteriorCellStratum(dm, &cStart, &cEnd);CHKERRQ(ierr);
1349   for (c = cStart; c < cEnd; ++c) {
1350     PetscScalar *x = NULL;
1351     PetscInt     qc = 0;
1352 
1353     ierr = DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ);CHKERRQ(ierr);
1354     ierr = DMPlexVecGetClosure(dm, NULL, localX, c, NULL, &x);CHKERRQ(ierr);
1355 
1356     for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1357       PetscObject  obj;
1358       PetscClassId id;
1359       void * const ctx = ctxs ? ctxs[field] : NULL;
1360       PetscInt     Nb, Nc, q, fc;
1361 
1362       PetscReal       elemDiff = 0.0;
1363 
1364       ierr = DMGetField(dm, field, NULL, &obj);CHKERRQ(ierr);
1365       ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
1366       if (id == PETSCFE_CLASSID)      {ierr = PetscFEGetNumComponents((PetscFE) obj, &Nc);CHKERRQ(ierr);ierr = PetscFEGetDimension((PetscFE) obj, &Nb);CHKERRQ(ierr);}
1367       else if (id == PETSCFV_CLASSID) {ierr = PetscFVGetNumComponents((PetscFV) obj, &Nc);CHKERRQ(ierr);Nb = 1;}
1368       else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", field);
1369       if (debug) {
1370         char title[1024];
1371         ierr = PetscSNPrintf(title, 1023, "Solution for Field %D", field);CHKERRQ(ierr);
1372         ierr = DMPrintCellVector(c, title, Nb, &x[fieldOffset]);CHKERRQ(ierr);
1373       }
1374       for (q = 0; q < Nq; ++q) {
1375         PetscFEGeom qgeom;
1376 
1377         qgeom.dimEmbed = fegeom.dimEmbed;
1378         qgeom.J        = &fegeom.J[q*coordDim*coordDim];
1379         qgeom.invJ     = &fegeom.invJ[q*coordDim*coordDim];
1380         qgeom.detJ     = &fegeom.detJ[q];
1381         if (fegeom.detJ[q] <= 0.0) SETERRQ3(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for element %D, quadrature point %D", (double)fegeom.detJ[q], c, q);
1382         if (transform) {
1383           gcoords = &coords[coordDim*Nq];
1384           ierr = DMPlexBasisTransformApplyReal_Internal(dm, &coords[coordDim*q], PETSC_TRUE, coordDim, &coords[coordDim*q], gcoords, dm->transformCtx);CHKERRQ(ierr);
1385         } else {
1386           gcoords = &coords[coordDim*q];
1387         }
1388         ierr = (*funcs[field])(coordDim, time, gcoords, Nc, funcVal, ctx);
1389         if (ierr) {
1390           PetscErrorCode ierr2;
1391           ierr2 = DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x);CHKERRQ(ierr2);
1392           ierr2 = DMRestoreLocalVector(dm, &localX);CHKERRQ(ierr2);
1393           ierr2 = PetscFree7(localDiff,funcVal,interpolant,coords,fegeom.detJ,fegeom.J,fegeom.invJ);CHKERRQ(ierr2);
1394           CHKERRQ(ierr);
1395         }
1396         if (transform) {ierr = DMPlexBasisTransformApply_Internal(dm, &coords[coordDim*q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx);CHKERRQ(ierr);}
1397         if (id == PETSCFE_CLASSID)      {ierr = PetscFEInterpolate_Static((PetscFE) obj, &x[fieldOffset], &qgeom, q, interpolant);CHKERRQ(ierr);}
1398         else if (id == PETSCFV_CLASSID) {ierr = PetscFVInterpolate_Static((PetscFV) obj, &x[fieldOffset], q, interpolant);CHKERRQ(ierr);}
1399         else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", field);
1400         for (fc = 0; fc < Nc; ++fc) {
1401           const PetscReal wt = quadWeights[q*qNc+(qNc == 1 ? 0 : qc+fc)];
1402           if (debug) {ierr = PetscPrintf(PETSC_COMM_SELF, "    elem %D field %D,%D point %g %g %g diff %g\n", c, field, fc, (double)(coordDim > 0 ? coords[coordDim*q] : 0.), (double)(coordDim > 1 ? coords[coordDim*q+1] : 0.),(double)(coordDim > 2 ? coords[coordDim*q+2] : 0.), (double)(PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc]))*wt*fegeom.detJ[q]));CHKERRQ(ierr);}
1403           elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc]))*wt*fegeom.detJ[q];
1404         }
1405       }
1406       fieldOffset += Nb;
1407       qc          += Nc;
1408       localDiff[field] += elemDiff;
1409       if (debug) {ierr = PetscPrintf(PETSC_COMM_SELF, "  elem %D field %D cum diff %g\n", c, field, (double)localDiff[field]);CHKERRQ(ierr);}
1410     }
1411     ierr = DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x);CHKERRQ(ierr);
1412   }
1413   ierr = DMRestoreLocalVector(dm, &localX);CHKERRQ(ierr);
1414   ierr = MPIU_Allreduce(localDiff, diff, numFields, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm));CHKERRQ(ierr);
1415   for (field = 0; field < numFields; ++field) diff[field] = PetscSqrtReal(diff[field]);
1416   ierr = PetscFree7(localDiff,funcVal,interpolant,coords,fegeom.detJ,fegeom.J,fegeom.invJ);CHKERRQ(ierr);
1417   PetscFunctionReturn(0);
1418 }
1419 
1420 /*@C
1421   DMPlexComputeL2DiffVec - This function computes the cellwise L_2 difference between a function u and an FEM interpolant solution u_h, and stores it in a Vec.
1422 
1423   Collective on dm
1424 
1425   Input Parameters:
1426 + dm    - The DM
1427 . time  - The time
1428 . funcs - The functions to evaluate for each field component: NULL means that component does not contribute to error calculation
1429 . ctxs  - Optional array of contexts to pass to each function, or NULL.
1430 - X     - The coefficient vector u_h
1431 
1432   Output Parameter:
1433 . D - A Vec which holds the difference ||u - u_h||_2 for each cell
1434 
1435   Level: developer
1436 
1437 .seealso: DMProjectFunction(), DMComputeL2Diff(), DMPlexComputeL2FieldDiff(), DMComputeL2GradientDiff()
1438 @*/
1439 PetscErrorCode DMPlexComputeL2DiffVec(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal [], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, Vec D)
1440 {
1441   PetscSection     section;
1442   PetscQuadrature  quad;
1443   Vec              localX;
1444   PetscFEGeom      fegeom;
1445   PetscScalar     *funcVal, *interpolant;
1446   PetscReal       *coords;
1447   const PetscReal *quadPoints, *quadWeights;
1448   PetscInt         dim, coordDim, numFields, numComponents = 0, qNc, Nq, cStart, cEnd, c, field, fieldOffset;
1449   PetscErrorCode   ierr;
1450 
1451   PetscFunctionBegin;
1452   ierr = VecSet(D, 0.0);CHKERRQ(ierr);
1453   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
1454   ierr = DMGetCoordinateDim(dm, &coordDim);CHKERRQ(ierr);
1455   ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);
1456   ierr = PetscSectionGetNumFields(section, &numFields);CHKERRQ(ierr);
1457   ierr = DMGetLocalVector(dm, &localX);CHKERRQ(ierr);
1458   ierr = DMProjectFunctionLocal(dm, time, funcs, ctxs, INSERT_BC_VALUES, localX);CHKERRQ(ierr);
1459   ierr = DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX);CHKERRQ(ierr);
1460   ierr = DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX);CHKERRQ(ierr);
1461   for (field = 0; field < numFields; ++field) {
1462     PetscObject  obj;
1463     PetscClassId id;
1464     PetscInt     Nc;
1465 
1466     ierr = DMGetField(dm, field, NULL, &obj);CHKERRQ(ierr);
1467     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
1468     if (id == PETSCFE_CLASSID) {
1469       PetscFE fe = (PetscFE) obj;
1470 
1471       ierr = PetscFEGetQuadrature(fe, &quad);CHKERRQ(ierr);
1472       ierr = PetscFEGetNumComponents(fe, &Nc);CHKERRQ(ierr);
1473     } else if (id == PETSCFV_CLASSID) {
1474       PetscFV fv = (PetscFV) obj;
1475 
1476       ierr = PetscFVGetQuadrature(fv, &quad);CHKERRQ(ierr);
1477       ierr = PetscFVGetNumComponents(fv, &Nc);CHKERRQ(ierr);
1478     } else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", field);
1479     numComponents += Nc;
1480   }
1481   ierr = PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights);CHKERRQ(ierr);
1482   if ((qNc != 1) && (qNc != numComponents)) SETERRQ2(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_SIZ, "Quadrature components %D != %D field components", qNc, numComponents);
1483   ierr = PetscMalloc6(numComponents,&funcVal,numComponents,&interpolant,coordDim*Nq,&coords,Nq,&fegeom.detJ,coordDim*coordDim*Nq,&fegeom.J,coordDim*coordDim*Nq,&fegeom.invJ);CHKERRQ(ierr);
1484   ierr = DMPlexGetInteriorCellStratum(dm, &cStart, &cEnd);CHKERRQ(ierr);
1485   for (c = cStart; c < cEnd; ++c) {
1486     PetscScalar *x = NULL;
1487     PetscScalar  elemDiff = 0.0;
1488     PetscInt     qc = 0;
1489 
1490     ierr = DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ);CHKERRQ(ierr);
1491     ierr = DMPlexVecGetClosure(dm, NULL, localX, c, NULL, &x);CHKERRQ(ierr);
1492 
1493     for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1494       PetscObject  obj;
1495       PetscClassId id;
1496       void * const ctx = ctxs ? ctxs[field] : NULL;
1497       PetscInt     Nb, Nc, q, fc;
1498 
1499       ierr = DMGetField(dm, field, NULL, &obj);CHKERRQ(ierr);
1500       ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
1501       if (id == PETSCFE_CLASSID)      {ierr = PetscFEGetNumComponents((PetscFE) obj, &Nc);CHKERRQ(ierr);ierr = PetscFEGetDimension((PetscFE) obj, &Nb);CHKERRQ(ierr);}
1502       else if (id == PETSCFV_CLASSID) {ierr = PetscFVGetNumComponents((PetscFV) obj, &Nc);CHKERRQ(ierr);Nb = 1;}
1503       else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", field);
1504       if (funcs[field]) {
1505         for (q = 0; q < Nq; ++q) {
1506           PetscFEGeom qgeom;
1507 
1508           qgeom.dimEmbed = fegeom.dimEmbed;
1509           qgeom.J        = &fegeom.J[q*coordDim*coordDim];
1510           qgeom.invJ     = &fegeom.invJ[q*coordDim*coordDim];
1511           qgeom.detJ     = &fegeom.detJ[q];
1512           if (fegeom.detJ[q] <= 0.0) SETERRQ3(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for element %D, quadrature points %D", (double)fegeom.detJ[q], c, q);
1513           ierr = (*funcs[field])(coordDim, time, &coords[q*coordDim], Nc, funcVal, ctx);
1514           if (ierr) {
1515             PetscErrorCode ierr2;
1516             ierr2 = DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x);CHKERRQ(ierr2);
1517             ierr2 = PetscFree6(funcVal,interpolant,coords,fegeom.detJ,fegeom.J,fegeom.invJ);CHKERRQ(ierr2);
1518             ierr2 = DMRestoreLocalVector(dm, &localX);CHKERRQ(ierr2);
1519             CHKERRQ(ierr);
1520           }
1521           if (id == PETSCFE_CLASSID)      {ierr = PetscFEInterpolate_Static((PetscFE) obj, &x[fieldOffset], &qgeom, q, interpolant);CHKERRQ(ierr);}
1522           else if (id == PETSCFV_CLASSID) {ierr = PetscFVInterpolate_Static((PetscFV) obj, &x[fieldOffset], q, interpolant);CHKERRQ(ierr);}
1523           else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", field);
1524           for (fc = 0; fc < Nc; ++fc) {
1525             const PetscReal wt = quadWeights[q*qNc+(qNc == 1 ? 0 : qc+fc)];
1526             elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc]))*wt*fegeom.detJ[q];
1527           }
1528         }
1529       }
1530       fieldOffset += Nb;
1531       qc          += Nc;
1532     }
1533     ierr = DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x);CHKERRQ(ierr);
1534     ierr = VecSetValue(D, c - cStart, elemDiff, INSERT_VALUES);CHKERRQ(ierr);
1535   }
1536   ierr = PetscFree6(funcVal,interpolant,coords,fegeom.detJ,fegeom.J,fegeom.invJ);CHKERRQ(ierr);
1537   ierr = DMRestoreLocalVector(dm, &localX);CHKERRQ(ierr);
1538   ierr = VecSqrtAbs(D);CHKERRQ(ierr);
1539   PetscFunctionReturn(0);
1540 }
1541 
1542 /*@C
1543   DMPlexComputeGradientClementInterpolant - This function computes the L2 projection of the cellwise gradient of a function u onto P1, and stores it in a Vec.
1544 
1545   Collective on dm
1546 
1547   Input Parameters:
1548 + dm - The DM
1549 - LocX  - The coefficient vector u_h
1550 
1551   Output Parameter:
1552 . locC - A Vec which holds the Clement interpolant of the gradient
1553 
1554   Notes:
1555     Add citation to (Clement, 1975) and definition of the interpolant
1556   \nabla u_h(v_i) = \sum_{T_i \in support(v_i)} |T_i| \nabla u_h(T_i) / \sum_{T_i \in support(v_i)} |T_i| where |T_i| is the cell volume
1557 
1558   Level: developer
1559 
1560 .seealso: DMProjectFunction(), DMComputeL2Diff(), DMPlexComputeL2FieldDiff(), DMComputeL2GradientDiff()
1561 @*/
1562 PetscErrorCode DMPlexComputeGradientClementInterpolant(DM dm, Vec locX, Vec locC)
1563 {
1564   DM_Plex         *mesh  = (DM_Plex *) dm->data;
1565   PetscInt         debug = mesh->printFEM;
1566   DM               dmC;
1567   PetscSection     section;
1568   PetscQuadrature  quad;
1569   PetscScalar     *interpolant, *gradsum;
1570   PetscFEGeom      fegeom;
1571   PetscReal       *coords;
1572   const PetscReal *quadPoints, *quadWeights;
1573   PetscInt         dim, coordDim, numFields, numComponents = 0, qNc, Nq, cStart, cEnd, vStart, vEnd, v, field, fieldOffset;
1574   PetscErrorCode   ierr;
1575 
1576   PetscFunctionBegin;
1577   ierr = VecGetDM(locC, &dmC);CHKERRQ(ierr);
1578   ierr = VecSet(locC, 0.0);CHKERRQ(ierr);
1579   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
1580   ierr = DMGetCoordinateDim(dm, &coordDim);CHKERRQ(ierr);
1581   fegeom.dimEmbed = coordDim;
1582   ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);
1583   ierr = PetscSectionGetNumFields(section, &numFields);CHKERRQ(ierr);
1584   for (field = 0; field < numFields; ++field) {
1585     PetscObject  obj;
1586     PetscClassId id;
1587     PetscInt     Nc;
1588 
1589     ierr = DMGetField(dm, field, NULL, &obj);CHKERRQ(ierr);
1590     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
1591     if (id == PETSCFE_CLASSID) {
1592       PetscFE fe = (PetscFE) obj;
1593 
1594       ierr = PetscFEGetQuadrature(fe, &quad);CHKERRQ(ierr);
1595       ierr = PetscFEGetNumComponents(fe, &Nc);CHKERRQ(ierr);
1596     } else if (id == PETSCFV_CLASSID) {
1597       PetscFV fv = (PetscFV) obj;
1598 
1599       ierr = PetscFVGetQuadrature(fv, &quad);CHKERRQ(ierr);
1600       ierr = PetscFVGetNumComponents(fv, &Nc);CHKERRQ(ierr);
1601     } else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", field);
1602     numComponents += Nc;
1603   }
1604   ierr = PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights);CHKERRQ(ierr);
1605   if ((qNc != 1) && (qNc != numComponents)) SETERRQ2(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_SIZ, "Quadrature components %D != %D field components", qNc, numComponents);
1606   ierr = PetscMalloc6(coordDim*numComponents*2,&gradsum,coordDim*numComponents,&interpolant,coordDim*Nq,&coords,Nq,&fegeom.detJ,coordDim*coordDim*Nq,&fegeom.J,coordDim*coordDim*Nq,&fegeom.invJ);CHKERRQ(ierr);
1607   ierr = DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd);CHKERRQ(ierr);
1608   ierr = DMPlexGetInteriorCellStratum(dm, &cStart, &cEnd);CHKERRQ(ierr);
1609   for (v = vStart; v < vEnd; ++v) {
1610     PetscScalar volsum = 0.0;
1611     PetscInt   *star = NULL;
1612     PetscInt    starSize, st, d, fc;
1613 
1614     ierr = PetscArrayzero(gradsum, coordDim*numComponents);CHKERRQ(ierr);
1615     ierr = DMPlexGetTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star);CHKERRQ(ierr);
1616     for (st = 0; st < starSize*2; st += 2) {
1617       const PetscInt cell = star[st];
1618       PetscScalar   *grad = &gradsum[coordDim*numComponents];
1619       PetscScalar   *x    = NULL;
1620       PetscReal      vol  = 0.0;
1621 
1622       if ((cell < cStart) || (cell >= cEnd)) continue;
1623       ierr = DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ);CHKERRQ(ierr);
1624       ierr = DMPlexVecGetClosure(dm, NULL, locX, cell, NULL, &x);CHKERRQ(ierr);
1625       for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1626         PetscObject  obj;
1627         PetscClassId id;
1628         PetscInt     Nb, Nc, q, qc = 0;
1629 
1630         ierr = PetscArrayzero(grad, coordDim*numComponents);CHKERRQ(ierr);
1631         ierr = DMGetField(dm, field, NULL, &obj);CHKERRQ(ierr);
1632         ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
1633         if (id == PETSCFE_CLASSID)      {ierr = PetscFEGetNumComponents((PetscFE) obj, &Nc);CHKERRQ(ierr);ierr = PetscFEGetDimension((PetscFE) obj, &Nb);CHKERRQ(ierr);}
1634         else if (id == PETSCFV_CLASSID) {ierr = PetscFVGetNumComponents((PetscFV) obj, &Nc);CHKERRQ(ierr);Nb = 1;}
1635         else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", field);
1636         for (q = 0; q < Nq; ++q) {
1637           PetscFEGeom qgeom;
1638 
1639           qgeom.dimEmbed = fegeom.dimEmbed;
1640           qgeom.J        = &fegeom.J[q*coordDim*coordDim];
1641           qgeom.invJ     = &fegeom.invJ[q*coordDim*coordDim];
1642           qgeom.detJ     = &fegeom.detJ[q];
1643           if (fegeom.detJ[q] <= 0.0) SETERRQ3(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for element %D, quadrature points %D", (double)fegeom.detJ[q], cell, q);
1644           if (ierr) {
1645             PetscErrorCode ierr2;
1646             ierr2 = DMPlexVecRestoreClosure(dm, NULL, locX, cell, NULL, &x);CHKERRQ(ierr2);
1647             ierr2 = DMPlexRestoreTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star);CHKERRQ(ierr2);
1648             ierr2 = PetscFree6(gradsum,interpolant,coords,fegeom.detJ,fegeom.J,fegeom.invJ);CHKERRQ(ierr2);
1649             CHKERRQ(ierr);
1650           }
1651           if (id == PETSCFE_CLASSID)      {ierr = PetscFEInterpolateGradient_Static((PetscFE) obj, &x[fieldOffset], &qgeom, q, interpolant);CHKERRQ(ierr);}
1652           else SETERRQ1(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", field);
1653           for (fc = 0; fc < Nc; ++fc) {
1654             const PetscReal wt = quadWeights[q*qNc+qc+fc];
1655 
1656             for (d = 0; d < coordDim; ++d) grad[fc*coordDim+d] += interpolant[fc*dim+d]*wt*fegeom.detJ[q];
1657           }
1658           vol += quadWeights[q*qNc]*fegeom.detJ[q];
1659         }
1660         fieldOffset += Nb;
1661         qc          += Nc;
1662       }
1663       ierr = DMPlexVecRestoreClosure(dm, NULL, locX, cell, NULL, &x);CHKERRQ(ierr);
1664       for (fc = 0; fc < numComponents; ++fc) {
1665         for (d = 0; d < coordDim; ++d) {
1666           gradsum[fc*coordDim+d] += grad[fc*coordDim+d];
1667         }
1668       }
1669       volsum += vol;
1670       if (debug) {
1671         ierr = PetscPrintf(PETSC_COMM_SELF, "Cell %D gradient: [", cell);CHKERRQ(ierr);
1672         for (fc = 0; fc < numComponents; ++fc) {
1673           for (d = 0; d < coordDim; ++d) {
1674             if (fc || d > 0) {ierr = PetscPrintf(PETSC_COMM_SELF, ", ");CHKERRQ(ierr);}
1675             ierr = PetscPrintf(PETSC_COMM_SELF, "%g", (double)PetscRealPart(grad[fc*coordDim+d]));CHKERRQ(ierr);
1676           }
1677         }
1678         ierr = PetscPrintf(PETSC_COMM_SELF, "]\n");CHKERRQ(ierr);
1679       }
1680     }
1681     for (fc = 0; fc < numComponents; ++fc) {
1682       for (d = 0; d < coordDim; ++d) gradsum[fc*coordDim+d] /= volsum;
1683     }
1684     ierr = DMPlexRestoreTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star);CHKERRQ(ierr);
1685     ierr = DMPlexVecSetClosure(dmC, NULL, locC, v, gradsum, INSERT_VALUES);CHKERRQ(ierr);
1686   }
1687   ierr = PetscFree6(gradsum,interpolant,coords,fegeom.detJ,fegeom.J,fegeom.invJ);CHKERRQ(ierr);
1688   PetscFunctionReturn(0);
1689 }
1690 
1691 static PetscErrorCode DMPlexComputeIntegral_Internal(DM dm, Vec X, PetscInt cStart, PetscInt cEnd, PetscScalar *cintegral, void *user)
1692 {
1693   DM                 dmAux = NULL;
1694   PetscDS            prob,    probAux = NULL;
1695   PetscSection       section, sectionAux;
1696   Vec                locX,    locA;
1697   PetscInt           dim, numCells = cEnd - cStart, c, f;
1698   PetscBool          useFVM = PETSC_FALSE;
1699   /* DS */
1700   PetscInt           Nf,    totDim,    *uOff, *uOff_x, numConstants;
1701   PetscInt           NfAux, totDimAux, *aOff;
1702   PetscScalar       *u, *a;
1703   const PetscScalar *constants;
1704   /* Geometry */
1705   PetscFEGeom       *cgeomFEM;
1706   DM                 dmGrad;
1707   PetscQuadrature    affineQuad = NULL;
1708   Vec                cellGeometryFVM = NULL, faceGeometryFVM = NULL, locGrad = NULL;
1709   PetscFVCellGeom   *cgeomFVM;
1710   const PetscScalar *lgrad;
1711   PetscInt           maxDegree;
1712   DMField            coordField;
1713   IS                 cellIS;
1714   PetscErrorCode     ierr;
1715 
1716   PetscFunctionBegin;
1717   ierr = DMGetDS(dm, &prob);CHKERRQ(ierr);
1718   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
1719   ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);
1720   ierr = PetscSectionGetNumFields(section, &Nf);CHKERRQ(ierr);
1721   /* Determine which discretizations we have */
1722   for (f = 0; f < Nf; ++f) {
1723     PetscObject  obj;
1724     PetscClassId id;
1725 
1726     ierr = PetscDSGetDiscretization(prob, f, &obj);CHKERRQ(ierr);
1727     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
1728     if (id == PETSCFV_CLASSID) useFVM = PETSC_TRUE;
1729   }
1730   /* Get local solution with boundary values */
1731   ierr = DMGetLocalVector(dm, &locX);CHKERRQ(ierr);
1732   ierr = DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL);CHKERRQ(ierr);
1733   ierr = DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX);CHKERRQ(ierr);
1734   ierr = DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX);CHKERRQ(ierr);
1735   /* Read DS information */
1736   ierr = PetscDSGetTotalDimension(prob, &totDim);CHKERRQ(ierr);
1737   ierr = PetscDSGetComponentOffsets(prob, &uOff);CHKERRQ(ierr);
1738   ierr = PetscDSGetComponentDerivativeOffsets(prob, &uOff_x);CHKERRQ(ierr);
1739   ierr = ISCreateStride(PETSC_COMM_SELF,numCells,cStart,1,&cellIS);CHKERRQ(ierr);
1740   ierr = PetscDSGetConstants(prob, &numConstants, &constants);CHKERRQ(ierr);
1741   /* Read Auxiliary DS information */
1742   ierr = PetscObjectQuery((PetscObject) dm, "dmAux", (PetscObject *) &dmAux);CHKERRQ(ierr);
1743   ierr = PetscObjectQuery((PetscObject) dm, "A", (PetscObject *) &locA);CHKERRQ(ierr);
1744   if (dmAux) {
1745     ierr = DMGetDS(dmAux, &probAux);CHKERRQ(ierr);
1746     ierr = PetscDSGetNumFields(probAux, &NfAux);CHKERRQ(ierr);
1747     ierr = DMGetLocalSection(dmAux, &sectionAux);CHKERRQ(ierr);
1748     ierr = PetscDSGetTotalDimension(probAux, &totDimAux);CHKERRQ(ierr);
1749     ierr = PetscDSGetComponentOffsets(probAux, &aOff);CHKERRQ(ierr);
1750   }
1751   /* Allocate data  arrays */
1752   ierr = PetscCalloc1(numCells*totDim, &u);CHKERRQ(ierr);
1753   if (dmAux) {ierr = PetscMalloc1(numCells*totDimAux, &a);CHKERRQ(ierr);}
1754   /* Read out geometry */
1755   ierr = DMGetCoordinateField(dm,&coordField);CHKERRQ(ierr);
1756   ierr = DMFieldGetDegree(coordField,cellIS,NULL,&maxDegree);CHKERRQ(ierr);
1757   if (maxDegree <= 1) {
1758     ierr = DMFieldCreateDefaultQuadrature(coordField,cellIS,&affineQuad);CHKERRQ(ierr);
1759     if (affineQuad) {
1760       ierr = DMFieldCreateFEGeom(coordField,cellIS,affineQuad,PETSC_FALSE,&cgeomFEM);CHKERRQ(ierr);
1761     }
1762   }
1763   if (useFVM) {
1764     PetscFV   fv = NULL;
1765     Vec       grad;
1766     PetscInt  fStart, fEnd;
1767     PetscBool compGrad;
1768 
1769     for (f = 0; f < Nf; ++f) {
1770       PetscObject  obj;
1771       PetscClassId id;
1772 
1773       ierr = PetscDSGetDiscretization(prob, f, &obj);CHKERRQ(ierr);
1774       ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
1775       if (id == PETSCFV_CLASSID) {fv = (PetscFV) obj; break;}
1776     }
1777     ierr = PetscFVGetComputeGradients(fv, &compGrad);CHKERRQ(ierr);
1778     ierr = PetscFVSetComputeGradients(fv, PETSC_TRUE);CHKERRQ(ierr);
1779     ierr = DMPlexComputeGeometryFVM(dm, &cellGeometryFVM, &faceGeometryFVM);CHKERRQ(ierr);
1780     ierr = DMPlexComputeGradientFVM(dm, fv, faceGeometryFVM, cellGeometryFVM, &dmGrad);CHKERRQ(ierr);
1781     ierr = PetscFVSetComputeGradients(fv, compGrad);CHKERRQ(ierr);
1782     ierr = VecGetArrayRead(cellGeometryFVM, (const PetscScalar **) &cgeomFVM);CHKERRQ(ierr);
1783     /* Reconstruct and limit cell gradients */
1784     ierr = DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd);CHKERRQ(ierr);
1785     ierr = DMGetGlobalVector(dmGrad, &grad);CHKERRQ(ierr);
1786     ierr = DMPlexReconstructGradients_Internal(dm, fv, fStart, fEnd, faceGeometryFVM, cellGeometryFVM, locX, grad);CHKERRQ(ierr);
1787     /* Communicate gradient values */
1788     ierr = DMGetLocalVector(dmGrad, &locGrad);CHKERRQ(ierr);
1789     ierr = DMGlobalToLocalBegin(dmGrad, grad, INSERT_VALUES, locGrad);CHKERRQ(ierr);
1790     ierr = DMGlobalToLocalEnd(dmGrad, grad, INSERT_VALUES, locGrad);CHKERRQ(ierr);
1791     ierr = DMRestoreGlobalVector(dmGrad, &grad);CHKERRQ(ierr);
1792     /* Handle non-essential (e.g. outflow) boundary values */
1793     ierr = DMPlexInsertBoundaryValues(dm, PETSC_FALSE, locX, 0.0, faceGeometryFVM, cellGeometryFVM, locGrad);CHKERRQ(ierr);
1794     ierr = VecGetArrayRead(locGrad, &lgrad);CHKERRQ(ierr);
1795   }
1796   /* Read out data from inputs */
1797   for (c = cStart; c < cEnd; ++c) {
1798     PetscScalar *x = NULL;
1799     PetscInt     i;
1800 
1801     ierr = DMPlexVecGetClosure(dm, section, locX, c, NULL, &x);CHKERRQ(ierr);
1802     for (i = 0; i < totDim; ++i) u[c*totDim+i] = x[i];
1803     ierr = DMPlexVecRestoreClosure(dm, section, locX, c, NULL, &x);CHKERRQ(ierr);
1804     if (dmAux) {
1805       ierr = DMPlexVecGetClosure(dmAux, sectionAux, locA, c, NULL, &x);CHKERRQ(ierr);
1806       for (i = 0; i < totDimAux; ++i) a[c*totDimAux+i] = x[i];
1807       ierr = DMPlexVecRestoreClosure(dmAux, sectionAux, locA, c, NULL, &x);CHKERRQ(ierr);
1808     }
1809   }
1810   /* Do integration for each field */
1811   for (f = 0; f < Nf; ++f) {
1812     PetscObject  obj;
1813     PetscClassId id;
1814     PetscInt     numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;
1815 
1816     ierr = PetscDSGetDiscretization(prob, f, &obj);CHKERRQ(ierr);
1817     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
1818     if (id == PETSCFE_CLASSID) {
1819       PetscFE         fe = (PetscFE) obj;
1820       PetscQuadrature q;
1821       PetscFEGeom     *chunkGeom = NULL;
1822       PetscInt        Nq, Nb;
1823 
1824       ierr = PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches);CHKERRQ(ierr);
1825       ierr = PetscFEGetQuadrature(fe, &q);CHKERRQ(ierr);
1826       ierr = PetscQuadratureGetData(q, NULL, NULL, &Nq, NULL, NULL);CHKERRQ(ierr);
1827       ierr = PetscFEGetDimension(fe, &Nb);CHKERRQ(ierr);
1828       blockSize = Nb*Nq;
1829       batchSize = numBlocks * blockSize;
1830       ierr = PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches);CHKERRQ(ierr);
1831       numChunks = numCells / (numBatches*batchSize);
1832       Ne        = numChunks*numBatches*batchSize;
1833       Nr        = numCells % (numBatches*batchSize);
1834       offset    = numCells - Nr;
1835       if (!affineQuad) {
1836         ierr = DMFieldCreateFEGeom(coordField,cellIS,q,PETSC_FALSE,&cgeomFEM);CHKERRQ(ierr);
1837       }
1838       ierr = PetscFEGeomGetChunk(cgeomFEM,0,offset,&chunkGeom);CHKERRQ(ierr);
1839       ierr = PetscFEIntegrate(prob, f, Ne, chunkGeom, u, probAux, a, cintegral);CHKERRQ(ierr);
1840       ierr = PetscFEGeomGetChunk(cgeomFEM,offset,numCells,&chunkGeom);CHKERRQ(ierr);
1841       ierr = PetscFEIntegrate(prob, f, Nr, chunkGeom, &u[offset*totDim], probAux, &a[offset*totDimAux], &cintegral[offset*Nf]);CHKERRQ(ierr);
1842       ierr = PetscFEGeomRestoreChunk(cgeomFEM,offset,numCells,&chunkGeom);CHKERRQ(ierr);
1843       if (!affineQuad) {
1844         ierr = PetscFEGeomDestroy(&cgeomFEM);CHKERRQ(ierr);
1845       }
1846     } else if (id == PETSCFV_CLASSID) {
1847       PetscInt       foff;
1848       PetscPointFunc obj_func;
1849       PetscScalar    lint;
1850 
1851       ierr = PetscDSGetObjective(prob, f, &obj_func);CHKERRQ(ierr);
1852       ierr = PetscDSGetFieldOffset(prob, f, &foff);CHKERRQ(ierr);
1853       if (obj_func) {
1854         for (c = 0; c < numCells; ++c) {
1855           PetscScalar *u_x;
1856 
1857           ierr = DMPlexPointLocalRead(dmGrad, c, lgrad, &u_x);CHKERRQ(ierr);
1858           obj_func(dim, Nf, NfAux, uOff, uOff_x, &u[totDim*c+foff], NULL, u_x, aOff, NULL, &a[totDimAux*c], NULL, NULL, 0.0, cgeomFVM[c].centroid, numConstants, constants, &lint);
1859           cintegral[c*Nf+f] += PetscRealPart(lint)*cgeomFVM[c].volume;
1860         }
1861       }
1862     } else SETERRQ1(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", f);
1863   }
1864   /* Cleanup data arrays */
1865   if (useFVM) {
1866     ierr = VecRestoreArrayRead(locGrad, &lgrad);CHKERRQ(ierr);
1867     ierr = VecRestoreArrayRead(cellGeometryFVM, (const PetscScalar **) &cgeomFVM);CHKERRQ(ierr);
1868     ierr = DMRestoreLocalVector(dmGrad, &locGrad);CHKERRQ(ierr);
1869     ierr = VecDestroy(&faceGeometryFVM);CHKERRQ(ierr);
1870     ierr = VecDestroy(&cellGeometryFVM);CHKERRQ(ierr);
1871     ierr = DMDestroy(&dmGrad);CHKERRQ(ierr);
1872   }
1873   if (dmAux) {ierr = PetscFree(a);CHKERRQ(ierr);}
1874   ierr = PetscFree(u);CHKERRQ(ierr);
1875   /* Cleanup */
1876   if (affineQuad) {
1877     ierr = PetscFEGeomDestroy(&cgeomFEM);CHKERRQ(ierr);
1878   }
1879   ierr = PetscQuadratureDestroy(&affineQuad);CHKERRQ(ierr);
1880   ierr = ISDestroy(&cellIS);CHKERRQ(ierr);
1881   ierr = DMRestoreLocalVector(dm, &locX);CHKERRQ(ierr);
1882   PetscFunctionReturn(0);
1883 }
1884 
1885 /*@
1886   DMPlexComputeIntegralFEM - Form the integral over the domain from the global input X using pointwise functions specified by the user
1887 
1888   Input Parameters:
1889 + dm - The mesh
1890 . X  - Global input vector
1891 - user - The user context
1892 
1893   Output Parameter:
1894 . integral - Integral for each field
1895 
1896   Level: developer
1897 
1898 .seealso: DMPlexComputeResidualFEM()
1899 @*/
1900 PetscErrorCode DMPlexComputeIntegralFEM(DM dm, Vec X, PetscScalar *integral, void *user)
1901 {
1902   DM_Plex       *mesh = (DM_Plex *) dm->data;
1903   PetscScalar   *cintegral, *lintegral;
1904   PetscInt       Nf, f, cellHeight, cStart, cEnd, cEndInterior[4], cell;
1905   PetscErrorCode ierr;
1906 
1907   PetscFunctionBegin;
1908   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1909   PetscValidHeaderSpecific(X, VEC_CLASSID, 2);
1910   PetscValidPointer(integral, 3);
1911   ierr = PetscLogEventBegin(DMPLEX_IntegralFEM,dm,0,0,0);CHKERRQ(ierr);
1912   ierr = DMGetNumFields(dm, &Nf);CHKERRQ(ierr);
1913   ierr = DMPlexGetVTKCellHeight(dm, &cellHeight);CHKERRQ(ierr);
1914   ierr = DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd);CHKERRQ(ierr);
1915   ierr = DMPlexGetHybridBounds(dm, &cEndInterior[0], &cEndInterior[1], &cEndInterior[2], &cEndInterior[3]);CHKERRQ(ierr);
1916   cEnd = cEndInterior[cellHeight] < 0 ? cEnd : cEndInterior[cellHeight];
1917   /* TODO Introduce a loop over large chunks (right now this is a single chunk) */
1918   ierr = PetscCalloc2(Nf, &lintegral, (cEnd-cStart)*Nf, &cintegral);CHKERRQ(ierr);
1919   ierr = DMPlexComputeIntegral_Internal(dm, X, cStart, cEnd, cintegral, user);CHKERRQ(ierr);
1920   /* Sum up values */
1921   for (cell = cStart; cell < cEnd; ++cell) {
1922     const PetscInt c = cell - cStart;
1923 
1924     if (mesh->printFEM > 1) {ierr = DMPrintCellVector(cell, "Cell Integral", Nf, &cintegral[c*Nf]);CHKERRQ(ierr);}
1925     for (f = 0; f < Nf; ++f) lintegral[f] += cintegral[c*Nf+f];
1926   }
1927   ierr = MPIU_Allreduce(lintegral, integral, Nf, MPIU_SCALAR, MPIU_SUM, PetscObjectComm((PetscObject) dm));CHKERRQ(ierr);
1928   if (mesh->printFEM) {
1929     ierr = PetscPrintf(PetscObjectComm((PetscObject) dm), "Integral:");CHKERRQ(ierr);
1930     for (f = 0; f < Nf; ++f) {ierr = PetscPrintf(PetscObjectComm((PetscObject) dm), " %g", (double) PetscRealPart(integral[f]));CHKERRQ(ierr);}
1931     ierr = PetscPrintf(PetscObjectComm((PetscObject) dm), "\n");CHKERRQ(ierr);
1932   }
1933   ierr = PetscFree2(lintegral, cintegral);CHKERRQ(ierr);
1934   ierr = PetscLogEventEnd(DMPLEX_IntegralFEM,dm,0,0,0);CHKERRQ(ierr);
1935   PetscFunctionReturn(0);
1936 }
1937 
1938 /*@
1939   DMPlexComputeCellwiseIntegralFEM - Form the vector of cellwise integrals F from the global input X using pointwise functions specified by the user
1940 
1941   Input Parameters:
1942 + dm - The mesh
1943 . X  - Global input vector
1944 - user - The user context
1945 
1946   Output Parameter:
1947 . integral - Cellwise integrals for each field
1948 
1949   Level: developer
1950 
1951 .seealso: DMPlexComputeResidualFEM()
1952 @*/
1953 PetscErrorCode DMPlexComputeCellwiseIntegralFEM(DM dm, Vec X, Vec F, void *user)
1954 {
1955   DM_Plex       *mesh = (DM_Plex *) dm->data;
1956   DM             dmF;
1957   PetscSection   sectionF;
1958   PetscScalar   *cintegral, *af;
1959   PetscInt       Nf, f, cellHeight, cStart, cEnd, cEndInterior[4], cell;
1960   PetscErrorCode ierr;
1961 
1962   PetscFunctionBegin;
1963   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1964   PetscValidHeaderSpecific(X, VEC_CLASSID, 2);
1965   PetscValidHeaderSpecific(F, VEC_CLASSID, 3);
1966   ierr = PetscLogEventBegin(DMPLEX_IntegralFEM,dm,0,0,0);CHKERRQ(ierr);
1967   ierr = DMGetNumFields(dm, &Nf);CHKERRQ(ierr);
1968   ierr = DMPlexGetVTKCellHeight(dm, &cellHeight);CHKERRQ(ierr);
1969   ierr = DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd);CHKERRQ(ierr);
1970   ierr = DMPlexGetHybridBounds(dm, &cEndInterior[0], &cEndInterior[1], &cEndInterior[2], &cEndInterior[3]);CHKERRQ(ierr);
1971   cEnd = cEndInterior[cellHeight] < 0 ? cEnd : cEndInterior[cellHeight];
1972   /* TODO Introduce a loop over large chunks (right now this is a single chunk) */
1973   ierr = PetscCalloc1((cEnd-cStart)*Nf, &cintegral);CHKERRQ(ierr);
1974   ierr = DMPlexComputeIntegral_Internal(dm, X, cStart, cEnd, cintegral, user);CHKERRQ(ierr);
1975   /* Put values in F*/
1976   ierr = VecGetDM(F, &dmF);CHKERRQ(ierr);
1977   ierr = DMGetLocalSection(dmF, &sectionF);CHKERRQ(ierr);
1978   ierr = VecGetArray(F, &af);CHKERRQ(ierr);
1979   for (cell = cStart; cell < cEnd; ++cell) {
1980     const PetscInt c = cell - cStart;
1981     PetscInt       dof, off;
1982 
1983     if (mesh->printFEM > 1) {ierr = DMPrintCellVector(cell, "Cell Integral", Nf, &cintegral[c*Nf]);CHKERRQ(ierr);}
1984     ierr = PetscSectionGetDof(sectionF, cell, &dof);CHKERRQ(ierr);
1985     ierr = PetscSectionGetOffset(sectionF, cell, &off);CHKERRQ(ierr);
1986     if (dof != Nf) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "The number of cell dofs %D != %D", dof, Nf);
1987     for (f = 0; f < Nf; ++f) af[off+f] = cintegral[c*Nf+f];
1988   }
1989   ierr = VecRestoreArray(F, &af);CHKERRQ(ierr);
1990   ierr = PetscFree(cintegral);CHKERRQ(ierr);
1991   ierr = PetscLogEventEnd(DMPLEX_IntegralFEM,dm,0,0,0);CHKERRQ(ierr);
1992   PetscFunctionReturn(0);
1993 }
1994 
1995 static PetscErrorCode DMPlexComputeBdIntegral_Internal(DM dm, Vec locX, IS pointIS,
1996                                                        void (*func)(PetscInt, PetscInt, PetscInt,
1997                                                                     const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[],
1998                                                                     const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[],
1999                                                                     PetscReal, const PetscReal[], const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]),
2000                                                        PetscScalar *fintegral, void *user)
2001 {
2002   DM                 plex = NULL, plexA = NULL;
2003   PetscDS            prob, probAux = NULL;
2004   PetscSection       section, sectionAux = NULL;
2005   Vec                locA = NULL;
2006   DMField            coordField;
2007   PetscInt           Nf,        totDim,        *uOff, *uOff_x;
2008   PetscInt           NfAux = 0, totDimAux = 0, *aOff = NULL;
2009   PetscScalar       *u, *a = NULL;
2010   const PetscScalar *constants;
2011   PetscInt           numConstants, f;
2012   PetscErrorCode     ierr;
2013 
2014   PetscFunctionBegin;
2015   ierr = DMGetCoordinateField(dm, &coordField);CHKERRQ(ierr);
2016   ierr = DMConvert(dm, DMPLEX, &plex);CHKERRQ(ierr);
2017   ierr = DMGetDS(dm, &prob);CHKERRQ(ierr);
2018   ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);
2019   ierr = PetscSectionGetNumFields(section, &Nf);CHKERRQ(ierr);
2020   /* Determine which discretizations we have */
2021   for (f = 0; f < Nf; ++f) {
2022     PetscObject  obj;
2023     PetscClassId id;
2024 
2025     ierr = PetscDSGetDiscretization(prob, f, &obj);CHKERRQ(ierr);
2026     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
2027     if (id == PETSCFV_CLASSID) SETERRQ1(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Not supported for FVM (field %D)", f);
2028   }
2029   /* Read DS information */
2030   ierr = PetscDSGetTotalDimension(prob, &totDim);CHKERRQ(ierr);
2031   ierr = PetscDSGetComponentOffsets(prob, &uOff);CHKERRQ(ierr);
2032   ierr = PetscDSGetComponentDerivativeOffsets(prob, &uOff_x);CHKERRQ(ierr);
2033   ierr = PetscDSGetConstants(prob, &numConstants, &constants);CHKERRQ(ierr);
2034   /* Read Auxiliary DS information */
2035   ierr = PetscObjectQuery((PetscObject) dm, "A", (PetscObject *) &locA);CHKERRQ(ierr);
2036   if (locA) {
2037     DM dmAux;
2038 
2039     ierr = VecGetDM(locA, &dmAux);CHKERRQ(ierr);
2040     ierr = DMConvert(dmAux, DMPLEX, &plexA);CHKERRQ(ierr);
2041     ierr = DMGetDS(dmAux, &probAux);CHKERRQ(ierr);
2042     ierr = PetscDSGetNumFields(probAux, &NfAux);CHKERRQ(ierr);
2043     ierr = DMGetLocalSection(dmAux, &sectionAux);CHKERRQ(ierr);
2044     ierr = PetscDSGetTotalDimension(probAux, &totDimAux);CHKERRQ(ierr);
2045     ierr = PetscDSGetComponentOffsets(probAux, &aOff);CHKERRQ(ierr);
2046   }
2047   /* Integrate over points */
2048   {
2049     PetscFEGeom    *fgeom, *chunkGeom = NULL;
2050     PetscInt        maxDegree;
2051     PetscQuadrature qGeom = NULL;
2052     const PetscInt *points;
2053     PetscInt        numFaces, face, Nq, field;
2054     PetscInt        numChunks, chunkSize, chunk, Nr, offset;
2055 
2056     ierr = ISGetLocalSize(pointIS, &numFaces);CHKERRQ(ierr);
2057     ierr = ISGetIndices(pointIS, &points);CHKERRQ(ierr);
2058     ierr = PetscCalloc2(numFaces*totDim, &u, locA ? numFaces*totDimAux : 0, &a);CHKERRQ(ierr);
2059     ierr = DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree);CHKERRQ(ierr);
2060     for (field = 0; field < Nf; ++field) {
2061       PetscFE fe;
2062 
2063       ierr = PetscDSGetDiscretization(prob, field, (PetscObject *) &fe);CHKERRQ(ierr);
2064       if (maxDegree <= 1) {ierr = DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom);CHKERRQ(ierr);}
2065       if (!qGeom) {
2066         ierr = PetscFEGetFaceQuadrature(fe, &qGeom);CHKERRQ(ierr);
2067         ierr = PetscObjectReference((PetscObject) qGeom);CHKERRQ(ierr);
2068       }
2069       ierr = PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL);CHKERRQ(ierr);
2070       ierr = DMPlexGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom);CHKERRQ(ierr);
2071       for (face = 0; face < numFaces; ++face) {
2072         const PetscInt point = points[face], *support, *cone;
2073         PetscScalar    *x    = NULL;
2074         PetscInt       i, coneSize, faceLoc;
2075 
2076         ierr = DMPlexGetSupport(dm, point, &support);CHKERRQ(ierr);
2077         ierr = DMPlexGetConeSize(dm, support[0], &coneSize);CHKERRQ(ierr);
2078         ierr = DMPlexGetCone(dm, support[0], &cone);CHKERRQ(ierr);
2079         for (faceLoc = 0; faceLoc < coneSize; ++faceLoc) if (cone[faceLoc] == point) break;
2080         if (faceLoc == coneSize) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find face %D in cone of support[0] %D", face, support[0]);
2081         fgeom->face[face][0] = faceLoc;
2082         ierr = DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x);CHKERRQ(ierr);
2083         for (i = 0; i < totDim; ++i) u[face*totDim+i] = x[i];
2084         ierr = DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x);CHKERRQ(ierr);
2085         if (locA) {
2086           PetscInt subp;
2087           ierr = DMPlexGetSubpoint(plexA, support[0], &subp);CHKERRQ(ierr);
2088           ierr = DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x);CHKERRQ(ierr);
2089           for (i = 0; i < totDimAux; ++i) a[f*totDimAux+i] = x[i];
2090           ierr = DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x);CHKERRQ(ierr);
2091         }
2092       }
2093       /* Get blocking */
2094       {
2095         PetscQuadrature q;
2096         PetscInt        numBatches, batchSize, numBlocks, blockSize;
2097         PetscInt        Nq, Nb;
2098 
2099         ierr = PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches);CHKERRQ(ierr);
2100         ierr = PetscFEGetQuadrature(fe, &q);CHKERRQ(ierr);
2101         ierr = PetscQuadratureGetData(q, NULL, NULL, &Nq, NULL, NULL);CHKERRQ(ierr);
2102         ierr = PetscFEGetDimension(fe, &Nb);CHKERRQ(ierr);
2103         blockSize = Nb*Nq;
2104         batchSize = numBlocks * blockSize;
2105         chunkSize = numBatches*batchSize;
2106         ierr = PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches);CHKERRQ(ierr);
2107         numChunks = numFaces / chunkSize;
2108         Nr        = numFaces % chunkSize;
2109         offset    = numFaces - Nr;
2110       }
2111       /* Do integration for each field */
2112       for (chunk = 0; chunk < numChunks; ++chunk) {
2113         ierr = PetscFEGeomGetChunk(fgeom, chunk*chunkSize, (chunk+1)*chunkSize, &chunkGeom);CHKERRQ(ierr);
2114         ierr = PetscFEIntegrateBd(prob, field, func, chunkSize, chunkGeom, u, probAux, a, fintegral);CHKERRQ(ierr);
2115         ierr = PetscFEGeomRestoreChunk(fgeom, 0, offset, &chunkGeom);CHKERRQ(ierr);
2116       }
2117       ierr = PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom);CHKERRQ(ierr);
2118       ierr = PetscFEIntegrateBd(prob, field, func, Nr, chunkGeom, &u[offset*totDim], probAux, a ? &a[offset*totDimAux] : NULL, &fintegral[offset*Nf]);CHKERRQ(ierr);
2119       ierr = PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom);CHKERRQ(ierr);
2120       /* Cleanup data arrays */
2121       ierr = DMPlexRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom);CHKERRQ(ierr);
2122       ierr = PetscQuadratureDestroy(&qGeom);CHKERRQ(ierr);
2123       ierr = PetscFree2(u, a);CHKERRQ(ierr);
2124       ierr = ISRestoreIndices(pointIS, &points);CHKERRQ(ierr);
2125     }
2126   }
2127   if (plex)  {ierr = DMDestroy(&plex);CHKERRQ(ierr);}
2128   if (plexA) {ierr = DMDestroy(&plexA);CHKERRQ(ierr);}
2129   PetscFunctionReturn(0);
2130 }
2131 
2132 /*@
2133   DMPlexComputeBdIntegral - Form the integral over the specified boundary from the global input X using pointwise functions specified by the user
2134 
2135   Input Parameters:
2136 + dm      - The mesh
2137 . X       - Global input vector
2138 . label   - The boundary DMLabel
2139 . numVals - The number of label values to use, or PETSC_DETERMINE for all values
2140 . vals    - The label values to use, or PETSC_NULL for all values
2141 . func    = The function to integrate along the boundary
2142 - user    - The user context
2143 
2144   Output Parameter:
2145 . integral - Integral for each field
2146 
2147   Level: developer
2148 
2149 .seealso: DMPlexComputeIntegralFEM(), DMPlexComputeBdResidualFEM()
2150 @*/
2151 PetscErrorCode DMPlexComputeBdIntegral(DM dm, Vec X, DMLabel label, PetscInt numVals, const PetscInt vals[],
2152                                        void (*func)(PetscInt, PetscInt, PetscInt,
2153                                                     const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[],
2154                                                     const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[],
2155                                                     PetscReal, const PetscReal[], const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]),
2156                                        PetscScalar *integral, void *user)
2157 {
2158   Vec            locX;
2159   PetscSection   section;
2160   DMLabel        depthLabel;
2161   IS             facetIS;
2162   PetscInt       dim, Nf, f, v;
2163   PetscErrorCode ierr;
2164 
2165   PetscFunctionBegin;
2166   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2167   PetscValidHeaderSpecific(X, VEC_CLASSID, 2);
2168   PetscValidPointer(label, 3);
2169   if (vals) PetscValidPointer(vals, 5);
2170   PetscValidPointer(integral, 6);
2171   ierr = PetscLogEventBegin(DMPLEX_IntegralFEM,dm,0,0,0);CHKERRQ(ierr);
2172   ierr = DMPlexGetDepthLabel(dm, &depthLabel);CHKERRQ(ierr);
2173   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
2174   ierr = DMLabelGetStratumIS(depthLabel, dim-1, &facetIS);CHKERRQ(ierr);
2175   ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);
2176   ierr = PetscSectionGetNumFields(section, &Nf);CHKERRQ(ierr);
2177   /* Get local solution with boundary values */
2178   ierr = DMGetLocalVector(dm, &locX);CHKERRQ(ierr);
2179   ierr = DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL);CHKERRQ(ierr);
2180   ierr = DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX);CHKERRQ(ierr);
2181   ierr = DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX);CHKERRQ(ierr);
2182   /* Loop over label values */
2183   ierr = PetscArrayzero(integral, Nf);CHKERRQ(ierr);
2184   for (v = 0; v < numVals; ++v) {
2185     IS           pointIS;
2186     PetscInt     numFaces, face;
2187     PetscScalar *fintegral;
2188 
2189     ierr = DMLabelGetStratumIS(label, vals[v], &pointIS);CHKERRQ(ierr);
2190     if (!pointIS) continue; /* No points with that id on this process */
2191     {
2192       IS isectIS;
2193 
2194       /* TODO: Special cases of ISIntersect where it is quick to check a priori if one is a superset of the other */
2195       ierr = ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS);CHKERRQ(ierr);
2196       ierr = ISDestroy(&pointIS);CHKERRQ(ierr);
2197       pointIS = isectIS;
2198     }
2199     ierr = ISGetLocalSize(pointIS, &numFaces);CHKERRQ(ierr);
2200     ierr = PetscCalloc1(numFaces*Nf, &fintegral);CHKERRQ(ierr);
2201     ierr = DMPlexComputeBdIntegral_Internal(dm, locX, pointIS, func, fintegral, user);CHKERRQ(ierr);
2202     /* Sum point contributions into integral */
2203     for (f = 0; f < Nf; ++f) for (face = 0; face < numFaces; ++face) integral[f] += fintegral[face*Nf+f];
2204     ierr = PetscFree(fintegral);CHKERRQ(ierr);
2205     ierr = ISDestroy(&pointIS);CHKERRQ(ierr);
2206   }
2207   ierr = DMRestoreLocalVector(dm, &locX);CHKERRQ(ierr);
2208   ierr = ISDestroy(&facetIS);CHKERRQ(ierr);
2209   ierr = PetscLogEventEnd(DMPLEX_IntegralFEM,dm,0,0,0);CHKERRQ(ierr);
2210   PetscFunctionReturn(0);
2211 }
2212 
2213 /*@
2214   DMPlexComputeInterpolatorNested - Form the local portion of the interpolation matrix I from the coarse DM to the uniformly refined DM.
2215 
2216   Input Parameters:
2217 + dmf  - The fine mesh
2218 . dmc  - The coarse mesh
2219 - user - The user context
2220 
2221   Output Parameter:
2222 . In  - The interpolation matrix
2223 
2224   Level: developer
2225 
2226 .seealso: DMPlexComputeInterpolatorGeneral(), DMPlexComputeJacobianFEM()
2227 @*/
2228 PetscErrorCode DMPlexComputeInterpolatorNested(DM dmc, DM dmf, Mat In, void *user)
2229 {
2230   DM_Plex          *mesh  = (DM_Plex *) dmc->data;
2231   const char       *name  = "Interpolator";
2232   PetscDS           cds, rds;
2233   PetscFE          *feRef;
2234   PetscFV          *fvRef;
2235   PetscSection      fsection, fglobalSection;
2236   PetscSection      csection, cglobalSection;
2237   PetscScalar      *elemMat;
2238   PetscInt          dim, Nf, f, fieldI, fieldJ, offsetI, offsetJ, cStart, cEnd, c;
2239   PetscInt          cTotDim, rTotDim = 0;
2240   PetscErrorCode    ierr;
2241 
2242   PetscFunctionBegin;
2243   ierr = PetscLogEventBegin(DMPLEX_InterpolatorFEM,dmc,dmf,0,0);CHKERRQ(ierr);
2244   ierr = DMGetDimension(dmf, &dim);CHKERRQ(ierr);
2245   ierr = DMGetLocalSection(dmf, &fsection);CHKERRQ(ierr);
2246   ierr = DMGetGlobalSection(dmf, &fglobalSection);CHKERRQ(ierr);
2247   ierr = DMGetLocalSection(dmc, &csection);CHKERRQ(ierr);
2248   ierr = DMGetGlobalSection(dmc, &cglobalSection);CHKERRQ(ierr);
2249   ierr = PetscSectionGetNumFields(fsection, &Nf);CHKERRQ(ierr);
2250   ierr = DMPlexGetInteriorCellStratum(dmc, &cStart, &cEnd);CHKERRQ(ierr);
2251   ierr = DMGetDS(dmc, &cds);CHKERRQ(ierr);
2252   ierr = DMGetDS(dmf, &rds);CHKERRQ(ierr);
2253   ierr = PetscCalloc2(Nf, &feRef, Nf, &fvRef);CHKERRQ(ierr);
2254   for (f = 0; f < Nf; ++f) {
2255     PetscObject  obj;
2256     PetscClassId id;
2257     PetscInt     rNb = 0, Nc = 0;
2258 
2259     ierr = PetscDSGetDiscretization(rds, f, &obj);CHKERRQ(ierr);
2260     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
2261     if (id == PETSCFE_CLASSID) {
2262       PetscFE fe = (PetscFE) obj;
2263 
2264       ierr = PetscFERefine(fe, &feRef[f]);CHKERRQ(ierr);
2265       ierr = PetscFEGetDimension(feRef[f], &rNb);CHKERRQ(ierr);
2266       ierr = PetscFEGetNumComponents(fe, &Nc);CHKERRQ(ierr);
2267     } else if (id == PETSCFV_CLASSID) {
2268       PetscFV        fv = (PetscFV) obj;
2269       PetscDualSpace Q;
2270 
2271       ierr = PetscFVRefine(fv, &fvRef[f]);CHKERRQ(ierr);
2272       ierr = PetscFVGetDualSpace(fvRef[f], &Q);CHKERRQ(ierr);
2273       ierr = PetscDualSpaceGetDimension(Q, &rNb);CHKERRQ(ierr);
2274       ierr = PetscFVGetNumComponents(fv, &Nc);CHKERRQ(ierr);
2275     }
2276     rTotDim += rNb;
2277   }
2278   ierr = PetscDSGetTotalDimension(cds, &cTotDim);CHKERRQ(ierr);
2279   ierr = PetscMalloc1(rTotDim*cTotDim,&elemMat);CHKERRQ(ierr);
2280   ierr = PetscArrayzero(elemMat, rTotDim*cTotDim);CHKERRQ(ierr);
2281   for (fieldI = 0, offsetI = 0; fieldI < Nf; ++fieldI) {
2282     PetscDualSpace   Qref;
2283     PetscQuadrature  f;
2284     const PetscReal *qpoints, *qweights;
2285     PetscReal       *points;
2286     PetscInt         npoints = 0, Nc, Np, fpdim, i, k, p, d;
2287 
2288     /* Compose points from all dual basis functionals */
2289     if (feRef[fieldI]) {
2290       ierr = PetscFEGetDualSpace(feRef[fieldI], &Qref);CHKERRQ(ierr);
2291       ierr = PetscFEGetNumComponents(feRef[fieldI], &Nc);CHKERRQ(ierr);
2292     } else {
2293       ierr = PetscFVGetDualSpace(fvRef[fieldI], &Qref);CHKERRQ(ierr);
2294       ierr = PetscFVGetNumComponents(fvRef[fieldI], &Nc);CHKERRQ(ierr);
2295     }
2296     ierr = PetscDualSpaceGetDimension(Qref, &fpdim);CHKERRQ(ierr);
2297     for (i = 0; i < fpdim; ++i) {
2298       ierr = PetscDualSpaceGetFunctional(Qref, i, &f);CHKERRQ(ierr);
2299       ierr = PetscQuadratureGetData(f, NULL, NULL, &Np, NULL, NULL);CHKERRQ(ierr);
2300       npoints += Np;
2301     }
2302     ierr = PetscMalloc1(npoints*dim,&points);CHKERRQ(ierr);
2303     for (i = 0, k = 0; i < fpdim; ++i) {
2304       ierr = PetscDualSpaceGetFunctional(Qref, i, &f);CHKERRQ(ierr);
2305       ierr = PetscQuadratureGetData(f, NULL, NULL, &Np, &qpoints, NULL);CHKERRQ(ierr);
2306       for (p = 0; p < Np; ++p, ++k) for (d = 0; d < dim; ++d) points[k*dim+d] = qpoints[p*dim+d];
2307     }
2308 
2309     for (fieldJ = 0, offsetJ = 0; fieldJ < Nf; ++fieldJ) {
2310       PetscObject  obj;
2311       PetscClassId id;
2312       PetscReal   *B;
2313       PetscInt     NcJ = 0, cpdim = 0, j, qNc;
2314 
2315       ierr = PetscDSGetDiscretization(cds, fieldJ, &obj);CHKERRQ(ierr);
2316       ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
2317       if (id == PETSCFE_CLASSID) {
2318         PetscFE fe = (PetscFE) obj;
2319 
2320         /* Evaluate basis at points */
2321         ierr = PetscFEGetNumComponents(fe, &NcJ);CHKERRQ(ierr);
2322         ierr = PetscFEGetDimension(fe, &cpdim);CHKERRQ(ierr);
2323         /* For now, fields only interpolate themselves */
2324         if (fieldI == fieldJ) {
2325           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);
2326           ierr = PetscFEGetTabulation(fe, npoints, points, &B, NULL, NULL);CHKERRQ(ierr);
2327           for (i = 0, k = 0; i < fpdim; ++i) {
2328             ierr = PetscDualSpaceGetFunctional(Qref, i, &f);CHKERRQ(ierr);
2329             ierr = PetscQuadratureGetData(f, NULL, &qNc, &Np, NULL, &qweights);CHKERRQ(ierr);
2330             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);
2331             for (p = 0; p < Np; ++p, ++k) {
2332               for (j = 0; j < cpdim; ++j) {
2333                 /*
2334                    cTotDim:            Total columns in element interpolation matrix, sum of number of dual basis functionals in each field
2335                    offsetI, offsetJ:   Offsets into the larger element interpolation matrix for different fields
2336                    fpdim, i, cpdim, j: Dofs for fine and coarse grids, correspond to dual space basis functionals
2337                    qNC, Nc, Ncj, c:    Number of components in this field
2338                    Np, p:              Number of quad points in the fine grid functional i
2339                    k:                  i*Np + p, overall point number for the interpolation
2340                 */
2341                 for (c = 0; c < Nc; ++c) elemMat[(offsetI + i)*cTotDim + offsetJ + j] += B[k*cpdim*NcJ+j*Nc+c]*qweights[p*qNc+c];
2342               }
2343             }
2344           }
2345           ierr = PetscFERestoreTabulation(fe, npoints, points, &B, NULL, NULL);CHKERRQ(ierr);
2346         }
2347       } else if (id == PETSCFV_CLASSID) {
2348         PetscFV        fv = (PetscFV) obj;
2349 
2350         /* Evaluate constant function at points */
2351         ierr = PetscFVGetNumComponents(fv, &NcJ);CHKERRQ(ierr);
2352         cpdim = 1;
2353         /* For now, fields only interpolate themselves */
2354         if (fieldI == fieldJ) {
2355           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);
2356           for (i = 0, k = 0; i < fpdim; ++i) {
2357             ierr = PetscDualSpaceGetFunctional(Qref, i, &f);CHKERRQ(ierr);
2358             ierr = PetscQuadratureGetData(f, NULL, &qNc, &Np, NULL, &qweights);CHKERRQ(ierr);
2359             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);
2360             for (p = 0; p < Np; ++p, ++k) {
2361               for (j = 0; j < cpdim; ++j) {
2362                 for (c = 0; c < Nc; ++c) elemMat[(offsetI + i)*cTotDim + offsetJ + j] += 1.0*qweights[p*qNc+c];
2363               }
2364             }
2365           }
2366         }
2367       }
2368       offsetJ += cpdim;
2369     }
2370     offsetI += fpdim;
2371     ierr = PetscFree(points);CHKERRQ(ierr);
2372   }
2373   if (mesh->printFEM > 1) {ierr = DMPrintCellMatrix(0, name, rTotDim, cTotDim, elemMat);CHKERRQ(ierr);}
2374   /* Preallocate matrix */
2375   {
2376     Mat          preallocator;
2377     PetscScalar *vals;
2378     PetscInt    *cellCIndices, *cellFIndices;
2379     PetscInt     locRows, locCols, cell;
2380 
2381     ierr = MatGetLocalSize(In, &locRows, &locCols);CHKERRQ(ierr);
2382     ierr = MatCreate(PetscObjectComm((PetscObject) In), &preallocator);CHKERRQ(ierr);
2383     ierr = MatSetType(preallocator, MATPREALLOCATOR);CHKERRQ(ierr);
2384     ierr = MatSetSizes(preallocator, locRows, locCols, PETSC_DETERMINE, PETSC_DETERMINE);CHKERRQ(ierr);
2385     ierr = MatSetUp(preallocator);CHKERRQ(ierr);
2386     ierr = PetscCalloc3(rTotDim*cTotDim, &vals,cTotDim,&cellCIndices,rTotDim,&cellFIndices);CHKERRQ(ierr);
2387     for (cell = cStart; cell < cEnd; ++cell) {
2388       ierr = DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, cell, cellCIndices, cellFIndices);CHKERRQ(ierr);
2389       ierr = MatSetValues(preallocator, rTotDim, cellFIndices, cTotDim, cellCIndices, vals, INSERT_VALUES);CHKERRQ(ierr);
2390     }
2391     ierr = PetscFree3(vals,cellCIndices,cellFIndices);CHKERRQ(ierr);
2392     ierr = MatAssemblyBegin(preallocator, MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
2393     ierr = MatAssemblyEnd(preallocator, MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
2394     ierr = MatPreallocatorPreallocate(preallocator, PETSC_TRUE, In);CHKERRQ(ierr);
2395     ierr = MatDestroy(&preallocator);CHKERRQ(ierr);
2396   }
2397   /* Fill matrix */
2398   ierr = MatZeroEntries(In);CHKERRQ(ierr);
2399   for (c = cStart; c < cEnd; ++c) {
2400     ierr = DMPlexMatSetClosureRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, In, c, elemMat, INSERT_VALUES);CHKERRQ(ierr);
2401   }
2402   for (f = 0; f < Nf; ++f) {ierr = PetscFEDestroy(&feRef[f]);CHKERRQ(ierr);}
2403   ierr = PetscFree2(feRef,fvRef);CHKERRQ(ierr);
2404   ierr = PetscFree(elemMat);CHKERRQ(ierr);
2405   ierr = MatAssemblyBegin(In, MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
2406   ierr = MatAssemblyEnd(In, MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
2407   if (mesh->printFEM) {
2408     ierr = PetscPrintf(PetscObjectComm((PetscObject)In), "%s:\n", name);CHKERRQ(ierr);
2409     ierr = MatChop(In, 1.0e-10);CHKERRQ(ierr);
2410     ierr = MatView(In, NULL);CHKERRQ(ierr);
2411   }
2412   ierr = PetscLogEventEnd(DMPLEX_InterpolatorFEM,dmc,dmf,0,0);CHKERRQ(ierr);
2413   PetscFunctionReturn(0);
2414 }
2415 
2416 PetscErrorCode DMPlexComputeMassMatrixNested(DM dmc, DM dmf, Mat mass, void *user)
2417 {
2418   SETERRQ(PetscObjectComm((PetscObject) dmc), PETSC_ERR_SUP, "Laziness");
2419 }
2420 
2421 /*@
2422   DMPlexComputeInterpolatorGeneral - Form the local portion of the interpolation matrix I from the coarse DM to a non-nested fine DM.
2423 
2424   Input Parameters:
2425 + dmf  - The fine mesh
2426 . dmc  - The coarse mesh
2427 - user - The user context
2428 
2429   Output Parameter:
2430 . In  - The interpolation matrix
2431 
2432   Level: developer
2433 
2434 .seealso: DMPlexComputeInterpolatorNested(), DMPlexComputeJacobianFEM()
2435 @*/
2436 PetscErrorCode DMPlexComputeInterpolatorGeneral(DM dmc, DM dmf, Mat In, void *user)
2437 {
2438   DM_Plex       *mesh = (DM_Plex *) dmf->data;
2439   const char    *name = "Interpolator";
2440   PetscDS        prob;
2441   PetscSection   fsection, csection, globalFSection, globalCSection;
2442   PetscHSetIJ    ht;
2443   PetscLayout    rLayout;
2444   PetscInt      *dnz, *onz;
2445   PetscInt       locRows, rStart, rEnd;
2446   PetscReal     *x, *v0, *J, *invJ, detJ;
2447   PetscReal     *v0c, *Jc, *invJc, detJc;
2448   PetscScalar   *elemMat;
2449   PetscInt       dim, Nf, field, totDim, cStart, cEnd, cell, ccell;
2450   PetscErrorCode ierr;
2451 
2452   PetscFunctionBegin;
2453   ierr = PetscLogEventBegin(DMPLEX_InterpolatorFEM,dmc,dmf,0,0);CHKERRQ(ierr);
2454   ierr = DMGetCoordinateDim(dmc, &dim);CHKERRQ(ierr);
2455   ierr = DMGetDS(dmc, &prob);CHKERRQ(ierr);
2456   ierr = PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL);CHKERRQ(ierr);
2457   ierr = PetscDSGetNumFields(prob, &Nf);CHKERRQ(ierr);
2458   ierr = PetscMalloc3(dim,&v0,dim*dim,&J,dim*dim,&invJ);CHKERRQ(ierr);
2459   ierr = PetscMalloc3(dim,&v0c,dim*dim,&Jc,dim*dim,&invJc);CHKERRQ(ierr);
2460   ierr = DMGetLocalSection(dmf, &fsection);CHKERRQ(ierr);
2461   ierr = DMGetGlobalSection(dmf, &globalFSection);CHKERRQ(ierr);
2462   ierr = DMGetLocalSection(dmc, &csection);CHKERRQ(ierr);
2463   ierr = DMGetGlobalSection(dmc, &globalCSection);CHKERRQ(ierr);
2464   ierr = DMPlexGetHeightStratum(dmf, 0, &cStart, &cEnd);CHKERRQ(ierr);
2465   ierr = PetscDSGetTotalDimension(prob, &totDim);CHKERRQ(ierr);
2466   ierr = PetscMalloc1(totDim, &elemMat);CHKERRQ(ierr);
2467 
2468   ierr = MatGetLocalSize(In, &locRows, NULL);CHKERRQ(ierr);
2469   ierr = PetscLayoutCreate(PetscObjectComm((PetscObject) In), &rLayout);CHKERRQ(ierr);
2470   ierr = PetscLayoutSetLocalSize(rLayout, locRows);CHKERRQ(ierr);
2471   ierr = PetscLayoutSetBlockSize(rLayout, 1);CHKERRQ(ierr);
2472   ierr = PetscLayoutSetUp(rLayout);CHKERRQ(ierr);
2473   ierr = PetscLayoutGetRange(rLayout, &rStart, &rEnd);CHKERRQ(ierr);
2474   ierr = PetscLayoutDestroy(&rLayout);CHKERRQ(ierr);
2475   ierr = PetscCalloc2(locRows,&dnz,locRows,&onz);CHKERRQ(ierr);
2476   ierr = PetscHSetIJCreate(&ht);CHKERRQ(ierr);
2477   for (field = 0; field < Nf; ++field) {
2478     PetscObject      obj;
2479     PetscClassId     id;
2480     PetscDualSpace   Q = NULL;
2481     PetscQuadrature  f;
2482     const PetscReal *qpoints;
2483     PetscInt         Nc, Np, fpdim, i, d;
2484 
2485     ierr = PetscDSGetDiscretization(prob, field, &obj);CHKERRQ(ierr);
2486     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
2487     if (id == PETSCFE_CLASSID) {
2488       PetscFE fe = (PetscFE) obj;
2489 
2490       ierr = PetscFEGetDualSpace(fe, &Q);CHKERRQ(ierr);
2491       ierr = PetscFEGetNumComponents(fe, &Nc);CHKERRQ(ierr);
2492     } else if (id == PETSCFV_CLASSID) {
2493       PetscFV fv = (PetscFV) obj;
2494 
2495       ierr = PetscFVGetDualSpace(fv, &Q);CHKERRQ(ierr);
2496       Nc   = 1;
2497     }
2498     ierr = PetscDualSpaceGetDimension(Q, &fpdim);CHKERRQ(ierr);
2499     /* For each fine grid cell */
2500     for (cell = cStart; cell < cEnd; ++cell) {
2501       PetscInt *findices,   *cindices;
2502       PetscInt  numFIndices, numCIndices;
2503 
2504       ierr = DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, &numFIndices, &findices, NULL);CHKERRQ(ierr);
2505       ierr = DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ);CHKERRQ(ierr);
2506       if (numFIndices != fpdim) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fine indices %D != %D dual basis vecs", numFIndices, fpdim);
2507       for (i = 0; i < fpdim; ++i) {
2508         Vec             pointVec;
2509         PetscScalar    *pV;
2510         PetscSF         coarseCellSF = NULL;
2511         const PetscSFNode *coarseCells;
2512         PetscInt        numCoarseCells, q, c;
2513 
2514         /* Get points from the dual basis functional quadrature */
2515         ierr = PetscDualSpaceGetFunctional(Q, i, &f);CHKERRQ(ierr);
2516         ierr = PetscQuadratureGetData(f, NULL, NULL, &Np, &qpoints, NULL);CHKERRQ(ierr);
2517         ierr = VecCreateSeq(PETSC_COMM_SELF, Np*dim, &pointVec);CHKERRQ(ierr);
2518         ierr = VecSetBlockSize(pointVec, dim);CHKERRQ(ierr);
2519         ierr = VecGetArray(pointVec, &pV);CHKERRQ(ierr);
2520         for (q = 0; q < Np; ++q) {
2521           const PetscReal xi0[3] = {-1., -1., -1.};
2522 
2523           /* Transform point to real space */
2524           CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q*dim], x);
2525           for (d = 0; d < dim; ++d) pV[q*dim+d] = x[d];
2526         }
2527         ierr = VecRestoreArray(pointVec, &pV);CHKERRQ(ierr);
2528         /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
2529         /* OPT: Pack all quad points from fine cell */
2530         ierr = DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF);CHKERRQ(ierr);
2531         ierr = PetscSFViewFromOptions(coarseCellSF, NULL, "-interp_sf_view");CHKERRQ(ierr);
2532         /* Update preallocation info */
2533         ierr = PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells);CHKERRQ(ierr);
2534         if (numCoarseCells != Np) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_PLIB,"Not all closure points located");
2535         {
2536           PetscHashIJKey key;
2537           PetscBool      missing;
2538 
2539           key.i = findices[i];
2540           if (key.i >= 0) {
2541             /* Get indices for coarse elements */
2542             for (ccell = 0; ccell < numCoarseCells; ++ccell) {
2543               ierr = DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, &numCIndices, &cindices, NULL);CHKERRQ(ierr);
2544               for (c = 0; c < numCIndices; ++c) {
2545                 key.j = cindices[c];
2546                 if (key.j < 0) continue;
2547                 ierr = PetscHSetIJQueryAdd(ht, key, &missing);CHKERRQ(ierr);
2548                 if (missing) {
2549                   if ((key.j >= rStart) && (key.j < rEnd)) ++dnz[key.i-rStart];
2550                   else                                     ++onz[key.i-rStart];
2551                 }
2552               }
2553               ierr = DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, &numCIndices, &cindices, NULL);CHKERRQ(ierr);
2554             }
2555           }
2556         }
2557         ierr = PetscSFDestroy(&coarseCellSF);CHKERRQ(ierr);
2558         ierr = VecDestroy(&pointVec);CHKERRQ(ierr);
2559       }
2560       ierr = DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, &numFIndices, &findices, NULL);CHKERRQ(ierr);
2561     }
2562   }
2563   ierr = PetscHSetIJDestroy(&ht);CHKERRQ(ierr);
2564   ierr = MatXAIJSetPreallocation(In, 1, dnz, onz, NULL, NULL);CHKERRQ(ierr);
2565   ierr = MatSetOption(In, MAT_NEW_NONZERO_ALLOCATION_ERR,PETSC_TRUE);CHKERRQ(ierr);
2566   ierr = PetscFree2(dnz,onz);CHKERRQ(ierr);
2567   for (field = 0; field < Nf; ++field) {
2568     PetscObject      obj;
2569     PetscClassId     id;
2570     PetscDualSpace   Q = NULL;
2571     PetscQuadrature  f;
2572     const PetscReal *qpoints, *qweights;
2573     PetscInt         Nc, qNc, Np, fpdim, i, d;
2574 
2575     ierr = PetscDSGetDiscretization(prob, field, &obj);CHKERRQ(ierr);
2576     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
2577     if (id == PETSCFE_CLASSID) {
2578       PetscFE fe = (PetscFE) obj;
2579 
2580       ierr = PetscFEGetDualSpace(fe, &Q);CHKERRQ(ierr);
2581       ierr = PetscFEGetNumComponents(fe, &Nc);CHKERRQ(ierr);
2582     } else if (id == PETSCFV_CLASSID) {
2583       PetscFV fv = (PetscFV) obj;
2584 
2585       ierr = PetscFVGetDualSpace(fv, &Q);CHKERRQ(ierr);
2586       Nc   = 1;
2587     } else SETERRQ1(PetscObjectComm((PetscObject)dmc),PETSC_ERR_ARG_WRONG,"Unknown discretization type for field %D",field);
2588     ierr = PetscDualSpaceGetDimension(Q, &fpdim);CHKERRQ(ierr);
2589     /* For each fine grid cell */
2590     for (cell = cStart; cell < cEnd; ++cell) {
2591       PetscInt *findices,   *cindices;
2592       PetscInt  numFIndices, numCIndices;
2593 
2594       ierr = DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, &numFIndices, &findices, NULL);CHKERRQ(ierr);
2595       ierr = DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ);CHKERRQ(ierr);
2596       if (numFIndices != fpdim) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fine indices %D != %D dual basis vecs", numFIndices, fpdim);
2597       for (i = 0; i < fpdim; ++i) {
2598         Vec             pointVec;
2599         PetscScalar    *pV;
2600         PetscSF         coarseCellSF = NULL;
2601         const PetscSFNode *coarseCells;
2602         PetscInt        numCoarseCells, cpdim, q, c, j;
2603 
2604         /* Get points from the dual basis functional quadrature */
2605         ierr = PetscDualSpaceGetFunctional(Q, i, &f);CHKERRQ(ierr);
2606         ierr = PetscQuadratureGetData(f, NULL, &qNc, &Np, &qpoints, &qweights);CHKERRQ(ierr);
2607         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);
2608         ierr = VecCreateSeq(PETSC_COMM_SELF, Np*dim, &pointVec);CHKERRQ(ierr);
2609         ierr = VecSetBlockSize(pointVec, dim);CHKERRQ(ierr);
2610         ierr = VecGetArray(pointVec, &pV);CHKERRQ(ierr);
2611         for (q = 0; q < Np; ++q) {
2612           const PetscReal xi0[3] = {-1., -1., -1.};
2613 
2614           /* Transform point to real space */
2615           CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q*dim], x);
2616           for (d = 0; d < dim; ++d) pV[q*dim+d] = x[d];
2617         }
2618         ierr = VecRestoreArray(pointVec, &pV);CHKERRQ(ierr);
2619         /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
2620         /* OPT: Read this out from preallocation information */
2621         ierr = DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF);CHKERRQ(ierr);
2622         /* Update preallocation info */
2623         ierr = PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells);CHKERRQ(ierr);
2624         if (numCoarseCells != Np) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_PLIB,"Not all closure points located");
2625         ierr = VecGetArray(pointVec, &pV);CHKERRQ(ierr);
2626         for (ccell = 0; ccell < numCoarseCells; ++ccell) {
2627           PetscReal pVReal[3];
2628           const PetscReal xi0[3] = {-1., -1., -1.};
2629 
2630           ierr = DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, &numCIndices, &cindices, NULL);CHKERRQ(ierr);
2631           /* Transform points from real space to coarse reference space */
2632           ierr = DMPlexComputeCellGeometryFEM(dmc, coarseCells[ccell].index, NULL, v0c, Jc, invJc, &detJc);CHKERRQ(ierr);
2633           for (d = 0; d < dim; ++d) pVReal[d] = PetscRealPart(pV[ccell*dim+d]);
2634           CoordinatesRealToRef(dim, dim, xi0, v0c, invJc, pVReal, x);
2635 
2636           if (id == PETSCFE_CLASSID) {
2637             PetscFE    fe = (PetscFE) obj;
2638             PetscReal *B;
2639 
2640             /* Evaluate coarse basis on contained point */
2641             ierr = PetscFEGetDimension(fe, &cpdim);CHKERRQ(ierr);
2642             ierr = PetscFEGetTabulation(fe, 1, x, &B, NULL, NULL);CHKERRQ(ierr);
2643             ierr = PetscArrayzero(elemMat, cpdim);CHKERRQ(ierr);
2644             /* Get elemMat entries by multiplying by weight */
2645             for (j = 0; j < cpdim; ++j) {
2646               for (c = 0; c < Nc; ++c) elemMat[j] += B[j*Nc + c]*qweights[ccell*qNc + c];
2647             }
2648             ierr = PetscFERestoreTabulation(fe, 1, x, &B, NULL, NULL);CHKERRQ(ierr);
2649           } else {
2650             cpdim = 1;
2651             for (j = 0; j < cpdim; ++j) {
2652               for (c = 0; c < Nc; ++c) elemMat[j] += 1.0*qweights[ccell*qNc + c];
2653             }
2654           }
2655           /* Update interpolator */
2656           if (mesh->printFEM > 1) {ierr = DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat);CHKERRQ(ierr);}
2657           if (numCIndices != cpdim) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %D != %D", numCIndices, cpdim);
2658           ierr = MatSetValues(In, 1, &findices[i], numCIndices, cindices, elemMat, INSERT_VALUES);CHKERRQ(ierr);
2659           ierr = DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, &numCIndices, &cindices, NULL);CHKERRQ(ierr);
2660         }
2661         ierr = VecRestoreArray(pointVec, &pV);CHKERRQ(ierr);
2662         ierr = PetscSFDestroy(&coarseCellSF);CHKERRQ(ierr);
2663         ierr = VecDestroy(&pointVec);CHKERRQ(ierr);
2664       }
2665       ierr = DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, &numFIndices, &findices, NULL);CHKERRQ(ierr);
2666     }
2667   }
2668   ierr = PetscFree3(v0,J,invJ);CHKERRQ(ierr);
2669   ierr = PetscFree3(v0c,Jc,invJc);CHKERRQ(ierr);
2670   ierr = PetscFree(elemMat);CHKERRQ(ierr);
2671   ierr = MatAssemblyBegin(In, MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
2672   ierr = MatAssemblyEnd(In, MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
2673   ierr = PetscLogEventEnd(DMPLEX_InterpolatorFEM,dmc,dmf,0,0);CHKERRQ(ierr);
2674   PetscFunctionReturn(0);
2675 }
2676 
2677 /*@
2678   DMPlexComputeMassMatrixGeneral - Form the local portion of the mass matrix M from the coarse DM to a non-nested fine DM.
2679 
2680   Input Parameters:
2681 + dmf  - The fine mesh
2682 . dmc  - The coarse mesh
2683 - user - The user context
2684 
2685   Output Parameter:
2686 . mass  - The mass matrix
2687 
2688   Level: developer
2689 
2690 .seealso: DMPlexComputeMassMatrixNested(), DMPlexComputeInterpolatorNested(), DMPlexComputeInterpolatorGeneral(), DMPlexComputeJacobianFEM()
2691 @*/
2692 PetscErrorCode DMPlexComputeMassMatrixGeneral(DM dmc, DM dmf, Mat mass, void *user)
2693 {
2694   DM_Plex       *mesh = (DM_Plex *) dmf->data;
2695   const char    *name = "Mass Matrix";
2696   PetscDS        prob;
2697   PetscSection   fsection, csection, globalFSection, globalCSection;
2698   PetscHSetIJ    ht;
2699   PetscLayout    rLayout;
2700   PetscInt      *dnz, *onz;
2701   PetscInt       locRows, rStart, rEnd;
2702   PetscReal     *x, *v0, *J, *invJ, detJ;
2703   PetscReal     *v0c, *Jc, *invJc, detJc;
2704   PetscScalar   *elemMat;
2705   PetscInt       dim, Nf, field, totDim, cStart, cEnd, cell, ccell;
2706   PetscErrorCode ierr;
2707 
2708   PetscFunctionBegin;
2709   ierr = DMGetCoordinateDim(dmc, &dim);CHKERRQ(ierr);
2710   ierr = DMGetDS(dmc, &prob);CHKERRQ(ierr);
2711   ierr = PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL);CHKERRQ(ierr);
2712   ierr = PetscDSGetNumFields(prob, &Nf);CHKERRQ(ierr);
2713   ierr = PetscMalloc3(dim,&v0,dim*dim,&J,dim*dim,&invJ);CHKERRQ(ierr);
2714   ierr = PetscMalloc3(dim,&v0c,dim*dim,&Jc,dim*dim,&invJc);CHKERRQ(ierr);
2715   ierr = DMGetLocalSection(dmf, &fsection);CHKERRQ(ierr);
2716   ierr = DMGetGlobalSection(dmf, &globalFSection);CHKERRQ(ierr);
2717   ierr = DMGetLocalSection(dmc, &csection);CHKERRQ(ierr);
2718   ierr = DMGetGlobalSection(dmc, &globalCSection);CHKERRQ(ierr);
2719   ierr = DMPlexGetHeightStratum(dmf, 0, &cStart, &cEnd);CHKERRQ(ierr);
2720   ierr = PetscDSGetTotalDimension(prob, &totDim);CHKERRQ(ierr);
2721   ierr = PetscMalloc1(totDim, &elemMat);CHKERRQ(ierr);
2722 
2723   ierr = MatGetLocalSize(mass, &locRows, NULL);CHKERRQ(ierr);
2724   ierr = PetscLayoutCreate(PetscObjectComm((PetscObject) mass), &rLayout);CHKERRQ(ierr);
2725   ierr = PetscLayoutSetLocalSize(rLayout, locRows);CHKERRQ(ierr);
2726   ierr = PetscLayoutSetBlockSize(rLayout, 1);CHKERRQ(ierr);
2727   ierr = PetscLayoutSetUp(rLayout);CHKERRQ(ierr);
2728   ierr = PetscLayoutGetRange(rLayout, &rStart, &rEnd);CHKERRQ(ierr);
2729   ierr = PetscLayoutDestroy(&rLayout);CHKERRQ(ierr);
2730   ierr = PetscCalloc2(locRows,&dnz,locRows,&onz);CHKERRQ(ierr);
2731   ierr = PetscHSetIJCreate(&ht);CHKERRQ(ierr);
2732   for (field = 0; field < Nf; ++field) {
2733     PetscObject      obj;
2734     PetscClassId     id;
2735     PetscQuadrature  quad;
2736     const PetscReal *qpoints;
2737     PetscInt         Nq, Nc, i, d;
2738 
2739     ierr = PetscDSGetDiscretization(prob, field, &obj);CHKERRQ(ierr);
2740     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
2741     if (id == PETSCFE_CLASSID) {ierr = PetscFEGetQuadrature((PetscFE) obj, &quad);CHKERRQ(ierr);}
2742     else                       {ierr = PetscFVGetQuadrature((PetscFV) obj, &quad);CHKERRQ(ierr);}
2743     ierr = PetscQuadratureGetData(quad, NULL, &Nc, &Nq, &qpoints, NULL);CHKERRQ(ierr);
2744     /* For each fine grid cell */
2745     for (cell = cStart; cell < cEnd; ++cell) {
2746       Vec                pointVec;
2747       PetscScalar       *pV;
2748       PetscSF            coarseCellSF = NULL;
2749       const PetscSFNode *coarseCells;
2750       PetscInt           numCoarseCells, q, c;
2751       PetscInt          *findices,   *cindices;
2752       PetscInt           numFIndices, numCIndices;
2753 
2754       ierr = DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, &numFIndices, &findices, NULL);CHKERRQ(ierr);
2755       ierr = DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ);CHKERRQ(ierr);
2756       /* Get points from the quadrature */
2757       ierr = VecCreateSeq(PETSC_COMM_SELF, Nq*dim, &pointVec);CHKERRQ(ierr);
2758       ierr = VecSetBlockSize(pointVec, dim);CHKERRQ(ierr);
2759       ierr = VecGetArray(pointVec, &pV);CHKERRQ(ierr);
2760       for (q = 0; q < Nq; ++q) {
2761         const PetscReal xi0[3] = {-1., -1., -1.};
2762 
2763         /* Transform point to real space */
2764         CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q*dim], x);
2765         for (d = 0; d < dim; ++d) pV[q*dim+d] = x[d];
2766       }
2767       ierr = VecRestoreArray(pointVec, &pV);CHKERRQ(ierr);
2768       /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
2769       ierr = DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF);CHKERRQ(ierr);
2770       ierr = PetscSFViewFromOptions(coarseCellSF, NULL, "-interp_sf_view");CHKERRQ(ierr);
2771       /* Update preallocation info */
2772       ierr = PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells);CHKERRQ(ierr);
2773       if (numCoarseCells != Nq) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_PLIB,"Not all closure points located");
2774       {
2775         PetscHashIJKey key;
2776         PetscBool      missing;
2777 
2778         for (i = 0; i < numFIndices; ++i) {
2779           key.i = findices[i];
2780           if (key.i >= 0) {
2781             /* Get indices for coarse elements */
2782             for (ccell = 0; ccell < numCoarseCells; ++ccell) {
2783               ierr = DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, &numCIndices, &cindices, NULL);CHKERRQ(ierr);
2784               for (c = 0; c < numCIndices; ++c) {
2785                 key.j = cindices[c];
2786                 if (key.j < 0) continue;
2787                 ierr = PetscHSetIJQueryAdd(ht, key, &missing);CHKERRQ(ierr);
2788                 if (missing) {
2789                   if ((key.j >= rStart) && (key.j < rEnd)) ++dnz[key.i-rStart];
2790                   else                                     ++onz[key.i-rStart];
2791                 }
2792               }
2793               ierr = DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, &numCIndices, &cindices, NULL);CHKERRQ(ierr);
2794             }
2795           }
2796         }
2797       }
2798       ierr = PetscSFDestroy(&coarseCellSF);CHKERRQ(ierr);
2799       ierr = VecDestroy(&pointVec);CHKERRQ(ierr);
2800       ierr = DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, &numFIndices, &findices, NULL);CHKERRQ(ierr);
2801     }
2802   }
2803   ierr = PetscHSetIJDestroy(&ht);CHKERRQ(ierr);
2804   ierr = MatXAIJSetPreallocation(mass, 1, dnz, onz, NULL, NULL);CHKERRQ(ierr);
2805   ierr = MatSetOption(mass, MAT_NEW_NONZERO_ALLOCATION_ERR,PETSC_TRUE);CHKERRQ(ierr);
2806   ierr = PetscFree2(dnz,onz);CHKERRQ(ierr);
2807   for (field = 0; field < Nf; ++field) {
2808     PetscObject      obj;
2809     PetscClassId     id;
2810     PetscQuadrature  quad;
2811     PetscReal       *Bfine;
2812     const PetscReal *qpoints, *qweights;
2813     PetscInt         Nq, Nc, i, d;
2814 
2815     ierr = PetscDSGetDiscretization(prob, field, &obj);CHKERRQ(ierr);
2816     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
2817     if (id == PETSCFE_CLASSID) {ierr = PetscFEGetQuadrature((PetscFE) obj, &quad);CHKERRQ(ierr);ierr = PetscFEGetDefaultTabulation((PetscFE) obj, &Bfine, NULL, NULL);CHKERRQ(ierr);}
2818     else                       {ierr = PetscFVGetQuadrature((PetscFV) obj, &quad);CHKERRQ(ierr);}
2819     ierr = PetscQuadratureGetData(quad, NULL, &Nc, &Nq, &qpoints, &qweights);CHKERRQ(ierr);
2820     /* For each fine grid cell */
2821     for (cell = cStart; cell < cEnd; ++cell) {
2822       Vec                pointVec;
2823       PetscScalar       *pV;
2824       PetscSF            coarseCellSF = NULL;
2825       const PetscSFNode *coarseCells;
2826       PetscInt           numCoarseCells, cpdim, q, c, j;
2827       PetscInt          *findices,   *cindices;
2828       PetscInt           numFIndices, numCIndices;
2829 
2830       ierr = DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, &numFIndices, &findices, NULL);CHKERRQ(ierr);
2831       ierr = DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ);CHKERRQ(ierr);
2832       /* Get points from the quadrature */
2833       ierr = VecCreateSeq(PETSC_COMM_SELF, Nq*dim, &pointVec);CHKERRQ(ierr);
2834       ierr = VecSetBlockSize(pointVec, dim);CHKERRQ(ierr);
2835       ierr = VecGetArray(pointVec, &pV);CHKERRQ(ierr);
2836       for (q = 0; q < Nq; ++q) {
2837         const PetscReal xi0[3] = {-1., -1., -1.};
2838 
2839         /* Transform point to real space */
2840         CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q*dim], x);
2841         for (d = 0; d < dim; ++d) pV[q*dim+d] = x[d];
2842       }
2843       ierr = VecRestoreArray(pointVec, &pV);CHKERRQ(ierr);
2844       /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
2845       ierr = DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF);CHKERRQ(ierr);
2846       /* Update matrix */
2847       ierr = PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells);CHKERRQ(ierr);
2848       if (numCoarseCells != Nq) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_PLIB,"Not all closure points located");
2849       ierr = VecGetArray(pointVec, &pV);CHKERRQ(ierr);
2850       for (ccell = 0; ccell < numCoarseCells; ++ccell) {
2851         PetscReal pVReal[3];
2852         const PetscReal xi0[3] = {-1., -1., -1.};
2853 
2854 
2855         ierr = DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, &numCIndices, &cindices, NULL);CHKERRQ(ierr);
2856         /* Transform points from real space to coarse reference space */
2857         ierr = DMPlexComputeCellGeometryFEM(dmc, coarseCells[ccell].index, NULL, v0c, Jc, invJc, &detJc);CHKERRQ(ierr);
2858         for (d = 0; d < dim; ++d) pVReal[d] = PetscRealPart(pV[ccell*dim+d]);
2859         CoordinatesRealToRef(dim, dim, xi0, v0c, invJc, pVReal, x);
2860 
2861         if (id == PETSCFE_CLASSID) {
2862           PetscFE    fe = (PetscFE) obj;
2863           PetscReal *B;
2864 
2865           /* Evaluate coarse basis on contained point */
2866           ierr = PetscFEGetDimension(fe, &cpdim);CHKERRQ(ierr);
2867           ierr = PetscFEGetTabulation(fe, 1, x, &B, NULL, NULL);CHKERRQ(ierr);
2868           /* Get elemMat entries by multiplying by weight */
2869           for (i = 0; i < numFIndices; ++i) {
2870             ierr = PetscArrayzero(elemMat, cpdim);CHKERRQ(ierr);
2871             for (j = 0; j < cpdim; ++j) {
2872               for (c = 0; c < Nc; ++c) elemMat[j] += B[j*Nc + c]*Bfine[(ccell*numFIndices + i)*Nc + c]*qweights[ccell*Nc + c]*detJ;
2873             }
2874             /* Update interpolator */
2875             if (mesh->printFEM > 1) {ierr = DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat);CHKERRQ(ierr);}
2876             if (numCIndices != cpdim) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %D != %D", numCIndices, cpdim);
2877             ierr = MatSetValues(mass, 1, &findices[i], numCIndices, cindices, elemMat, ADD_VALUES);CHKERRQ(ierr);
2878           }
2879           ierr = PetscFERestoreTabulation(fe, 1, x, &B, NULL, NULL);CHKERRQ(ierr);
2880         } else {
2881           cpdim = 1;
2882           for (i = 0; i < numFIndices; ++i) {
2883             ierr = PetscArrayzero(elemMat, cpdim);CHKERRQ(ierr);
2884             for (j = 0; j < cpdim; ++j) {
2885               for (c = 0; c < Nc; ++c) elemMat[j] += 1.0*1.0*qweights[ccell*Nc + c]*detJ;
2886             }
2887             /* Update interpolator */
2888             if (mesh->printFEM > 1) {ierr = DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat);CHKERRQ(ierr);}
2889             ierr = PetscPrintf(PETSC_COMM_SELF, "Nq: %D %D Nf: %D %D Nc: %D %D\n", ccell, Nq, i, numFIndices, j, numCIndices);CHKERRQ(ierr);
2890             if (numCIndices != cpdim) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %D != %D", numCIndices, cpdim);
2891             ierr = MatSetValues(mass, 1, &findices[i], numCIndices, cindices, elemMat, ADD_VALUES);CHKERRQ(ierr);
2892           }
2893         }
2894         ierr = DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, &numCIndices, &cindices, NULL);CHKERRQ(ierr);
2895       }
2896       ierr = VecRestoreArray(pointVec, &pV);CHKERRQ(ierr);
2897       ierr = PetscSFDestroy(&coarseCellSF);CHKERRQ(ierr);
2898       ierr = VecDestroy(&pointVec);CHKERRQ(ierr);
2899       ierr = DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, &numFIndices, &findices, NULL);CHKERRQ(ierr);
2900     }
2901   }
2902   ierr = PetscFree3(v0,J,invJ);CHKERRQ(ierr);
2903   ierr = PetscFree3(v0c,Jc,invJc);CHKERRQ(ierr);
2904   ierr = PetscFree(elemMat);CHKERRQ(ierr);
2905   ierr = MatAssemblyBegin(mass, MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
2906   ierr = MatAssemblyEnd(mass, MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
2907   PetscFunctionReturn(0);
2908 }
2909 
2910 /*@
2911   DMPlexComputeInjectorFEM - Compute a mapping from coarse unknowns to fine unknowns
2912 
2913   Input Parameters:
2914 + dmc  - The coarse mesh
2915 - dmf  - The fine mesh
2916 - user - The user context
2917 
2918   Output Parameter:
2919 . sc   - The mapping
2920 
2921   Level: developer
2922 
2923 .seealso: DMPlexComputeInterpolatorNested(), DMPlexComputeJacobianFEM()
2924 @*/
2925 PetscErrorCode DMPlexComputeInjectorFEM(DM dmc, DM dmf, VecScatter *sc, void *user)
2926 {
2927   PetscDS        prob;
2928   PetscFE       *feRef;
2929   PetscFV       *fvRef;
2930   Vec            fv, cv;
2931   IS             fis, cis;
2932   PetscSection   fsection, fglobalSection, csection, cglobalSection;
2933   PetscInt      *cmap, *cellCIndices, *cellFIndices, *cindices, *findices;
2934   PetscInt       cTotDim, fTotDim = 0, Nf, f, field, cStart, cEnd, c, dim, d, startC, endC, offsetC, offsetF, m;
2935   PetscBool     *needAvg;
2936   PetscErrorCode ierr;
2937 
2938   PetscFunctionBegin;
2939   ierr = PetscLogEventBegin(DMPLEX_InjectorFEM,dmc,dmf,0,0);CHKERRQ(ierr);
2940   ierr = DMGetDimension(dmf, &dim);CHKERRQ(ierr);
2941   ierr = DMGetLocalSection(dmf, &fsection);CHKERRQ(ierr);
2942   ierr = DMGetGlobalSection(dmf, &fglobalSection);CHKERRQ(ierr);
2943   ierr = DMGetLocalSection(dmc, &csection);CHKERRQ(ierr);
2944   ierr = DMGetGlobalSection(dmc, &cglobalSection);CHKERRQ(ierr);
2945   ierr = PetscSectionGetNumFields(fsection, &Nf);CHKERRQ(ierr);
2946   ierr = DMPlexGetInteriorCellStratum(dmc, &cStart, &cEnd);CHKERRQ(ierr);
2947   ierr = DMGetDS(dmc, &prob);CHKERRQ(ierr);
2948   ierr = PetscCalloc3(Nf,&feRef,Nf,&fvRef,Nf,&needAvg);CHKERRQ(ierr);
2949   for (f = 0; f < Nf; ++f) {
2950     PetscObject  obj;
2951     PetscClassId id;
2952     PetscInt     fNb = 0, Nc = 0;
2953 
2954     ierr = PetscDSGetDiscretization(prob, f, &obj);CHKERRQ(ierr);
2955     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
2956     if (id == PETSCFE_CLASSID) {
2957       PetscFE    fe = (PetscFE) obj;
2958       PetscSpace sp;
2959       PetscInt   maxDegree;
2960 
2961       ierr = PetscFERefine(fe, &feRef[f]);CHKERRQ(ierr);
2962       ierr = PetscFEGetDimension(feRef[f], &fNb);CHKERRQ(ierr);
2963       ierr = PetscFEGetNumComponents(fe, &Nc);CHKERRQ(ierr);
2964       ierr = PetscFEGetBasisSpace(fe, &sp);CHKERRQ(ierr);
2965       ierr = PetscSpaceGetDegree(sp, NULL, &maxDegree);CHKERRQ(ierr);
2966       if (!maxDegree) needAvg[f] = PETSC_TRUE;
2967     } else if (id == PETSCFV_CLASSID) {
2968       PetscFV        fv = (PetscFV) obj;
2969       PetscDualSpace Q;
2970 
2971       ierr = PetscFVRefine(fv, &fvRef[f]);CHKERRQ(ierr);
2972       ierr = PetscFVGetDualSpace(fvRef[f], &Q);CHKERRQ(ierr);
2973       ierr = PetscDualSpaceGetDimension(Q, &fNb);CHKERRQ(ierr);
2974       ierr = PetscFVGetNumComponents(fv, &Nc);CHKERRQ(ierr);
2975       needAvg[f] = PETSC_TRUE;
2976     }
2977     fTotDim += fNb;
2978   }
2979   ierr = PetscDSGetTotalDimension(prob, &cTotDim);CHKERRQ(ierr);
2980   ierr = PetscMalloc1(cTotDim,&cmap);CHKERRQ(ierr);
2981   for (field = 0, offsetC = 0, offsetF = 0; field < Nf; ++field) {
2982     PetscFE        feC;
2983     PetscFV        fvC;
2984     PetscDualSpace QF, QC;
2985     PetscInt       order = -1, NcF, NcC, fpdim, cpdim;
2986 
2987     if (feRef[field]) {
2988       ierr = PetscDSGetDiscretization(prob, field, (PetscObject *) &feC);CHKERRQ(ierr);
2989       ierr = PetscFEGetNumComponents(feC, &NcC);CHKERRQ(ierr);
2990       ierr = PetscFEGetNumComponents(feRef[field], &NcF);CHKERRQ(ierr);
2991       ierr = PetscFEGetDualSpace(feRef[field], &QF);CHKERRQ(ierr);
2992       ierr = PetscDualSpaceGetOrder(QF, &order);CHKERRQ(ierr);
2993       ierr = PetscDualSpaceGetDimension(QF, &fpdim);CHKERRQ(ierr);
2994       ierr = PetscFEGetDualSpace(feC, &QC);CHKERRQ(ierr);
2995       ierr = PetscDualSpaceGetDimension(QC, &cpdim);CHKERRQ(ierr);
2996     } else {
2997       ierr = PetscDSGetDiscretization(prob, field, (PetscObject *) &fvC);CHKERRQ(ierr);
2998       ierr = PetscFVGetNumComponents(fvC, &NcC);CHKERRQ(ierr);
2999       ierr = PetscFVGetNumComponents(fvRef[field], &NcF);CHKERRQ(ierr);
3000       ierr = PetscFVGetDualSpace(fvRef[field], &QF);CHKERRQ(ierr);
3001       ierr = PetscDualSpaceGetDimension(QF, &fpdim);CHKERRQ(ierr);
3002       ierr = PetscFVGetDualSpace(fvC, &QC);CHKERRQ(ierr);
3003       ierr = PetscDualSpaceGetDimension(QC, &cpdim);CHKERRQ(ierr);
3004     }
3005     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);
3006     for (c = 0; c < cpdim; ++c) {
3007       PetscQuadrature  cfunc;
3008       const PetscReal *cqpoints, *cqweights;
3009       PetscInt         NqcC, NpC;
3010       PetscBool        found = PETSC_FALSE;
3011 
3012       ierr = PetscDualSpaceGetFunctional(QC, c, &cfunc);CHKERRQ(ierr);
3013       ierr = PetscQuadratureGetData(cfunc, NULL, &NqcC, &NpC, &cqpoints, &cqweights);CHKERRQ(ierr);
3014       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);
3015       if (NpC != 1 && feRef[field]) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Do not know how to do injection for moments");
3016       for (f = 0; f < fpdim; ++f) {
3017         PetscQuadrature  ffunc;
3018         const PetscReal *fqpoints, *fqweights;
3019         PetscReal        sum = 0.0;
3020         PetscInt         NqcF, NpF;
3021 
3022         ierr = PetscDualSpaceGetFunctional(QF, f, &ffunc);CHKERRQ(ierr);
3023         ierr = PetscQuadratureGetData(ffunc, NULL, &NqcF, &NpF, &fqpoints, &fqweights);CHKERRQ(ierr);
3024         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);
3025         if (NpC != NpF) continue;
3026         for (d = 0; d < dim; ++d) sum += PetscAbsReal(cqpoints[d] - fqpoints[d]);
3027         if (sum > 1.0e-9) continue;
3028         for (d = 0; d < NcC; ++d) sum += PetscAbsReal(cqweights[d]*fqweights[d]);
3029         if (sum < 1.0e-9) continue;
3030         cmap[offsetC+c] = offsetF+f;
3031         found = PETSC_TRUE;
3032         break;
3033       }
3034       if (!found) {
3035         /* TODO We really want the average here, but some asshole put VecScatter in the interface */
3036         if (fvRef[field] || (feRef[field] && order == 0)) {
3037           cmap[offsetC+c] = offsetF+0;
3038         } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Could not locate matching functional for injection");
3039       }
3040     }
3041     offsetC += cpdim;
3042     offsetF += fpdim;
3043   }
3044   for (f = 0; f < Nf; ++f) {ierr = PetscFEDestroy(&feRef[f]);CHKERRQ(ierr);ierr = PetscFVDestroy(&fvRef[f]);CHKERRQ(ierr);}
3045   ierr = PetscFree3(feRef,fvRef,needAvg);CHKERRQ(ierr);
3046 
3047   ierr = DMGetGlobalVector(dmf, &fv);CHKERRQ(ierr);
3048   ierr = DMGetGlobalVector(dmc, &cv);CHKERRQ(ierr);
3049   ierr = VecGetOwnershipRange(cv, &startC, &endC);CHKERRQ(ierr);
3050   ierr = PetscSectionGetConstrainedStorageSize(cglobalSection, &m);CHKERRQ(ierr);
3051   ierr = PetscMalloc2(cTotDim,&cellCIndices,fTotDim,&cellFIndices);CHKERRQ(ierr);
3052   ierr = PetscMalloc1(m,&cindices);CHKERRQ(ierr);
3053   ierr = PetscMalloc1(m,&findices);CHKERRQ(ierr);
3054   for (d = 0; d < m; ++d) cindices[d] = findices[d] = -1;
3055   for (c = cStart; c < cEnd; ++c) {
3056     ierr = DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, c, cellCIndices, cellFIndices);CHKERRQ(ierr);
3057     for (d = 0; d < cTotDim; ++d) {
3058       if ((cellCIndices[d] < startC) || (cellCIndices[d] >= endC)) continue;
3059       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]]);
3060       cindices[cellCIndices[d]-startC] = cellCIndices[d];
3061       findices[cellCIndices[d]-startC] = cellFIndices[cmap[d]];
3062     }
3063   }
3064   ierr = PetscFree(cmap);CHKERRQ(ierr);
3065   ierr = PetscFree2(cellCIndices,cellFIndices);CHKERRQ(ierr);
3066 
3067   ierr = ISCreateGeneral(PETSC_COMM_SELF, m, cindices, PETSC_OWN_POINTER, &cis);CHKERRQ(ierr);
3068   ierr = ISCreateGeneral(PETSC_COMM_SELF, m, findices, PETSC_OWN_POINTER, &fis);CHKERRQ(ierr);
3069   ierr = VecScatterCreate(cv, cis, fv, fis, sc);CHKERRQ(ierr);
3070   ierr = ISDestroy(&cis);CHKERRQ(ierr);
3071   ierr = ISDestroy(&fis);CHKERRQ(ierr);
3072   ierr = DMRestoreGlobalVector(dmf, &fv);CHKERRQ(ierr);
3073   ierr = DMRestoreGlobalVector(dmc, &cv);CHKERRQ(ierr);
3074   ierr = PetscLogEventEnd(DMPLEX_InjectorFEM,dmc,dmf,0,0);CHKERRQ(ierr);
3075   PetscFunctionReturn(0);
3076 }
3077 
3078 /*@C
3079   DMPlexGetCellFields - Retrieve the field values values for a chunk of cells
3080 
3081   Input Parameters:
3082 + dm     - The DM
3083 . cellIS - The cells to include
3084 . locX   - A local vector with the solution fields
3085 . locX_t - A local vector with solution field time derivatives, or NULL
3086 - locA   - A local vector with auxiliary fields, or NULL
3087 
3088   Output Parameters:
3089 + u   - The field coefficients
3090 . u_t - The fields derivative coefficients
3091 - a   - The auxiliary field coefficients
3092 
3093   Level: developer
3094 
3095 .seealso: DMPlexGetFaceFields()
3096 @*/
3097 PetscErrorCode DMPlexGetCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
3098 {
3099   DM              plex, plexA = NULL;
3100   PetscSection    section, sectionAux;
3101   PetscDS         prob;
3102   const PetscInt *cells;
3103   PetscInt        cStart, cEnd, numCells, totDim, totDimAux, c;
3104   PetscErrorCode  ierr;
3105 
3106   PetscFunctionBegin;
3107   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3108   PetscValidHeaderSpecific(locX, VEC_CLASSID, 4);
3109   if (locX_t) {PetscValidHeaderSpecific(locX_t, VEC_CLASSID, 5);}
3110   if (locA)   {PetscValidHeaderSpecific(locA, VEC_CLASSID, 6);}
3111   PetscValidPointer(u, 7);
3112   PetscValidPointer(u_t, 8);
3113   PetscValidPointer(a, 9);
3114   ierr = DMPlexConvertPlex(dm, &plex, PETSC_FALSE);CHKERRQ(ierr);
3115   ierr = ISGetPointRange(cellIS, &cStart, &cEnd, &cells);CHKERRQ(ierr);
3116   ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);
3117   ierr = DMGetCellDS(dm, cStart, &prob);CHKERRQ(ierr);
3118   ierr = PetscDSGetTotalDimension(prob, &totDim);CHKERRQ(ierr);
3119   if (locA) {
3120     DM      dmAux;
3121     PetscDS probAux;
3122 
3123     ierr = VecGetDM(locA, &dmAux);CHKERRQ(ierr);
3124     ierr = DMPlexConvertPlex(dmAux, &plexA, PETSC_FALSE);CHKERRQ(ierr);
3125     ierr = DMGetLocalSection(dmAux, &sectionAux);CHKERRQ(ierr);
3126     ierr = DMGetDS(dmAux, &probAux);CHKERRQ(ierr);
3127     ierr = PetscDSGetTotalDimension(probAux, &totDimAux);CHKERRQ(ierr);
3128   }
3129   numCells = cEnd - cStart;
3130   ierr = DMGetWorkArray(dm, numCells*totDim, MPIU_SCALAR, u);CHKERRQ(ierr);
3131   if (locX_t) {ierr = DMGetWorkArray(dm, numCells*totDim, MPIU_SCALAR, u_t);CHKERRQ(ierr);} else {*u_t = NULL;}
3132   if (locA)   {ierr = DMGetWorkArray(dm, numCells*totDimAux, MPIU_SCALAR, a);CHKERRQ(ierr);} else {*a = NULL;}
3133   for (c = cStart; c < cEnd; ++c) {
3134     const PetscInt cell = cells ? cells[c] : c;
3135     const PetscInt cind = c - cStart;
3136     PetscScalar   *x = NULL, *x_t = NULL, *ul = *u, *ul_t = *u_t, *al = *a;
3137     PetscInt       i;
3138 
3139     ierr = DMPlexVecGetClosure(plex, section, locX, cell, NULL, &x);CHKERRQ(ierr);
3140     for (i = 0; i < totDim; ++i) ul[cind*totDim+i] = x[i];
3141     ierr = DMPlexVecRestoreClosure(plex, section, locX, cell, NULL, &x);CHKERRQ(ierr);
3142     if (locX_t) {
3143       ierr = DMPlexVecGetClosure(plex, section, locX_t, cell, NULL, &x_t);CHKERRQ(ierr);
3144       for (i = 0; i < totDim; ++i) ul_t[cind*totDim+i] = x_t[i];
3145       ierr = DMPlexVecRestoreClosure(plex, section, locX_t, cell, NULL, &x_t);CHKERRQ(ierr);
3146     }
3147     if (locA) {
3148       PetscInt subcell;
3149       ierr = DMPlexGetAuxiliaryPoint(plex, plexA, cell, &subcell);CHKERRQ(ierr);
3150       ierr = DMPlexVecGetClosure(plexA, sectionAux, locA, subcell, NULL, &x);CHKERRQ(ierr);
3151       for (i = 0; i < totDimAux; ++i) al[cind*totDimAux+i] = x[i];
3152       ierr = DMPlexVecRestoreClosure(plexA, sectionAux, locA, subcell, NULL, &x);CHKERRQ(ierr);
3153     }
3154   }
3155   ierr = DMDestroy(&plex);CHKERRQ(ierr);
3156   if (locA) {ierr = DMDestroy(&plexA);CHKERRQ(ierr);}
3157   ierr = ISRestorePointRange(cellIS, &cStart, &cEnd, &cells);CHKERRQ(ierr);
3158   PetscFunctionReturn(0);
3159 }
3160 
3161 /*@C
3162   DMPlexRestoreCellFields - Restore the field values values for a chunk of cells
3163 
3164   Input Parameters:
3165 + dm     - The DM
3166 . cellIS - The cells to include
3167 . locX   - A local vector with the solution fields
3168 . locX_t - A local vector with solution field time derivatives, or NULL
3169 - locA   - A local vector with auxiliary fields, or NULL
3170 
3171   Output Parameters:
3172 + u   - The field coefficients
3173 . u_t - The fields derivative coefficients
3174 - a   - The auxiliary field coefficients
3175 
3176   Level: developer
3177 
3178 .seealso: DMPlexGetFaceFields()
3179 @*/
3180 PetscErrorCode DMPlexRestoreCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
3181 {
3182   PetscErrorCode ierr;
3183 
3184   PetscFunctionBegin;
3185   ierr = DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u);CHKERRQ(ierr);
3186   if (locX_t) {ierr = DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u_t);CHKERRQ(ierr);}
3187   if (locA)   {ierr = DMRestoreWorkArray(dm, 0, MPIU_SCALAR, a);CHKERRQ(ierr);}
3188   PetscFunctionReturn(0);
3189 }
3190 
3191 /*@C
3192   DMPlexGetFaceFields - Retrieve the field values values for a chunk of faces
3193 
3194   Input Parameters:
3195 + dm     - The DM
3196 . fStart - The first face to include
3197 . fEnd   - The first face to exclude
3198 . locX   - A local vector with the solution fields
3199 . locX_t - A local vector with solution field time derivatives, or NULL
3200 . faceGeometry - A local vector with face geometry
3201 . cellGeometry - A local vector with cell geometry
3202 - locaGrad - A local vector with field gradients, or NULL
3203 
3204   Output Parameters:
3205 + Nface - The number of faces with field values
3206 . uL - The field values at the left side of the face
3207 - uR - The field values at the right side of the face
3208 
3209   Level: developer
3210 
3211 .seealso: DMPlexGetCellFields()
3212 @*/
3213 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)
3214 {
3215   DM                 dmFace, dmCell, dmGrad = NULL;
3216   PetscSection       section;
3217   PetscDS            prob;
3218   DMLabel            ghostLabel;
3219   const PetscScalar *facegeom, *cellgeom, *x, *lgrad;
3220   PetscBool         *isFE;
3221   PetscInt           dim, Nf, f, Nc, numFaces = fEnd - fStart, iface, face;
3222   PetscErrorCode     ierr;
3223 
3224   PetscFunctionBegin;
3225   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3226   PetscValidHeaderSpecific(locX, VEC_CLASSID, 4);
3227   if (locX_t) {PetscValidHeaderSpecific(locX_t, VEC_CLASSID, 5);}
3228   PetscValidHeaderSpecific(faceGeometry, VEC_CLASSID, 6);
3229   PetscValidHeaderSpecific(cellGeometry, VEC_CLASSID, 7);
3230   if (locGrad) {PetscValidHeaderSpecific(locGrad, VEC_CLASSID, 8);}
3231   PetscValidPointer(uL, 9);
3232   PetscValidPointer(uR, 10);
3233   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
3234   ierr = DMGetDS(dm, &prob);CHKERRQ(ierr);
3235   ierr = DMGetLocalSection(dm, &section);CHKERRQ(ierr);
3236   ierr = PetscDSGetNumFields(prob, &Nf);CHKERRQ(ierr);
3237   ierr = PetscDSGetTotalComponents(prob, &Nc);CHKERRQ(ierr);
3238   ierr = PetscMalloc1(Nf, &isFE);CHKERRQ(ierr);
3239   for (f = 0; f < Nf; ++f) {
3240     PetscObject  obj;
3241     PetscClassId id;
3242 
3243     ierr = PetscDSGetDiscretization(prob, f, &obj);CHKERRQ(ierr);
3244     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
3245     if (id == PETSCFE_CLASSID)      {isFE[f] = PETSC_TRUE;}
3246     else if (id == PETSCFV_CLASSID) {isFE[f] = PETSC_FALSE;}
3247     else                            {isFE[f] = PETSC_FALSE;}
3248   }
3249   ierr = DMGetLabel(dm, "ghost", &ghostLabel);CHKERRQ(ierr);
3250   ierr = VecGetArrayRead(locX, &x);CHKERRQ(ierr);
3251   ierr = VecGetDM(faceGeometry, &dmFace);CHKERRQ(ierr);
3252   ierr = VecGetArrayRead(faceGeometry, &facegeom);CHKERRQ(ierr);
3253   ierr = VecGetDM(cellGeometry, &dmCell);CHKERRQ(ierr);
3254   ierr = VecGetArrayRead(cellGeometry, &cellgeom);CHKERRQ(ierr);
3255   if (locGrad) {
3256     ierr = VecGetDM(locGrad, &dmGrad);CHKERRQ(ierr);
3257     ierr = VecGetArrayRead(locGrad, &lgrad);CHKERRQ(ierr);
3258   }
3259   ierr = DMGetWorkArray(dm, numFaces*Nc, MPIU_SCALAR, uL);CHKERRQ(ierr);
3260   ierr = DMGetWorkArray(dm, numFaces*Nc, MPIU_SCALAR, uR);CHKERRQ(ierr);
3261   /* Right now just eat the extra work for FE (could make a cell loop) */
3262   for (face = fStart, iface = 0; face < fEnd; ++face) {
3263     const PetscInt        *cells;
3264     PetscFVFaceGeom       *fg;
3265     PetscFVCellGeom       *cgL, *cgR;
3266     PetscScalar           *xL, *xR, *gL, *gR;
3267     PetscScalar           *uLl = *uL, *uRl = *uR;
3268     PetscInt               ghost, nsupp, nchild;
3269 
3270     ierr = DMLabelGetValue(ghostLabel, face, &ghost);CHKERRQ(ierr);
3271     ierr = DMPlexGetSupportSize(dm, face, &nsupp);CHKERRQ(ierr);
3272     ierr = DMPlexGetTreeChildren(dm, face, &nchild, NULL);CHKERRQ(ierr);
3273     if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
3274     ierr = DMPlexPointLocalRead(dmFace, face, facegeom, &fg);CHKERRQ(ierr);
3275     ierr = DMPlexGetSupport(dm, face, &cells);CHKERRQ(ierr);
3276     ierr = DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL);CHKERRQ(ierr);
3277     ierr = DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR);CHKERRQ(ierr);
3278     for (f = 0; f < Nf; ++f) {
3279       PetscInt off;
3280 
3281       ierr = PetscDSGetComponentOffset(prob, f, &off);CHKERRQ(ierr);
3282       if (isFE[f]) {
3283         const PetscInt *cone;
3284         PetscInt        comp, coneSizeL, coneSizeR, faceLocL, faceLocR, ldof, rdof, d;
3285 
3286         xL = xR = NULL;
3287         ierr = PetscSectionGetFieldComponents(section, f, &comp);CHKERRQ(ierr);
3288         ierr = DMPlexVecGetClosure(dm, section, locX, cells[0], &ldof, (PetscScalar **) &xL);CHKERRQ(ierr);
3289         ierr = DMPlexVecGetClosure(dm, section, locX, cells[1], &rdof, (PetscScalar **) &xR);CHKERRQ(ierr);
3290         ierr = DMPlexGetCone(dm, cells[0], &cone);CHKERRQ(ierr);
3291         ierr = DMPlexGetConeSize(dm, cells[0], &coneSizeL);CHKERRQ(ierr);
3292         for (faceLocL = 0; faceLocL < coneSizeL; ++faceLocL) if (cone[faceLocL] == face) break;
3293         ierr = DMPlexGetCone(dm, cells[1], &cone);CHKERRQ(ierr);
3294         ierr = DMPlexGetConeSize(dm, cells[1], &coneSizeR);CHKERRQ(ierr);
3295         for (faceLocR = 0; faceLocR < coneSizeR; ++faceLocR) if (cone[faceLocR] == face) break;
3296         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]);
3297         /* Check that FEM field has values in the right cell (sometimes its an FV ghost cell) */
3298         /* TODO: this is a hack that might not be right for nonconforming */
3299         if (faceLocL < coneSizeL) {
3300           ierr = PetscFEEvaluateFaceFields_Internal(prob, f, faceLocL, xL, &uLl[iface*Nc+off]);CHKERRQ(ierr);
3301           if (rdof == ldof && faceLocR < coneSizeR) {ierr = PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface*Nc+off]);CHKERRQ(ierr);}
3302           else              {for(d = 0; d < comp; ++d) uRl[iface*Nc+off+d] = uLl[iface*Nc+off+d];}
3303         }
3304         else {
3305           ierr = PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface*Nc+off]);CHKERRQ(ierr);
3306           ierr = PetscSectionGetFieldComponents(section, f, &comp);CHKERRQ(ierr);
3307           for(d = 0; d < comp; ++d) uLl[iface*Nc+off+d] = uRl[iface*Nc+off+d];
3308         }
3309         ierr = DMPlexVecRestoreClosure(dm, section, locX, cells[0], &ldof, (PetscScalar **) &xL);CHKERRQ(ierr);
3310         ierr = DMPlexVecRestoreClosure(dm, section, locX, cells[1], &rdof, (PetscScalar **) &xR);CHKERRQ(ierr);
3311       } else {
3312         PetscFV  fv;
3313         PetscInt numComp, c;
3314 
3315         ierr = PetscDSGetDiscretization(prob, f, (PetscObject *) &fv);CHKERRQ(ierr);
3316         ierr = PetscFVGetNumComponents(fv, &numComp);CHKERRQ(ierr);
3317         ierr = DMPlexPointLocalFieldRead(dm, cells[0], f, x, &xL);CHKERRQ(ierr);
3318         ierr = DMPlexPointLocalFieldRead(dm, cells[1], f, x, &xR);CHKERRQ(ierr);
3319         if (dmGrad) {
3320           PetscReal dxL[3], dxR[3];
3321 
3322           ierr = DMPlexPointLocalRead(dmGrad, cells[0], lgrad, &gL);CHKERRQ(ierr);
3323           ierr = DMPlexPointLocalRead(dmGrad, cells[1], lgrad, &gR);CHKERRQ(ierr);
3324           DMPlex_WaxpyD_Internal(dim, -1, cgL->centroid, fg->centroid, dxL);
3325           DMPlex_WaxpyD_Internal(dim, -1, cgR->centroid, fg->centroid, dxR);
3326           for (c = 0; c < numComp; ++c) {
3327             uLl[iface*Nc+off+c] = xL[c] + DMPlex_DotD_Internal(dim, &gL[c*dim], dxL);
3328             uRl[iface*Nc+off+c] = xR[c] + DMPlex_DotD_Internal(dim, &gR[c*dim], dxR);
3329           }
3330         } else {
3331           for (c = 0; c < numComp; ++c) {
3332             uLl[iface*Nc+off+c] = xL[c];
3333             uRl[iface*Nc+off+c] = xR[c];
3334           }
3335         }
3336       }
3337     }
3338     ++iface;
3339   }
3340   *Nface = iface;
3341   ierr = VecRestoreArrayRead(locX, &x);CHKERRQ(ierr);
3342   ierr = VecRestoreArrayRead(faceGeometry, &facegeom);CHKERRQ(ierr);
3343   ierr = VecRestoreArrayRead(cellGeometry, &cellgeom);CHKERRQ(ierr);
3344   if (locGrad) {
3345     ierr = VecRestoreArrayRead(locGrad, &lgrad);CHKERRQ(ierr);
3346   }
3347   ierr = PetscFree(isFE);CHKERRQ(ierr);
3348   PetscFunctionReturn(0);
3349 }
3350 
3351 /*@C
3352   DMPlexRestoreFaceFields - Restore the field values values for a chunk of faces
3353 
3354   Input Parameters:
3355 + dm     - The DM
3356 . fStart - The first face to include
3357 . fEnd   - The first face to exclude
3358 . locX   - A local vector with the solution fields
3359 . locX_t - A local vector with solution field time derivatives, or NULL
3360 . faceGeometry - A local vector with face geometry
3361 . cellGeometry - A local vector with cell geometry
3362 - locaGrad - A local vector with field gradients, or NULL
3363 
3364   Output Parameters:
3365 + Nface - The number of faces with field values
3366 . uL - The field values at the left side of the face
3367 - uR - The field values at the right side of the face
3368 
3369   Level: developer
3370 
3371 .seealso: DMPlexGetFaceFields()
3372 @*/
3373 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)
3374 {
3375   PetscErrorCode ierr;
3376 
3377   PetscFunctionBegin;
3378   ierr = DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uL);CHKERRQ(ierr);
3379   ierr = DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uR);CHKERRQ(ierr);
3380   PetscFunctionReturn(0);
3381 }
3382 
3383 /*@C
3384   DMPlexGetFaceGeometry - Retrieve the geometric values for a chunk of faces
3385 
3386   Input Parameters:
3387 + dm     - The DM
3388 . fStart - The first face to include
3389 . fEnd   - The first face to exclude
3390 . faceGeometry - A local vector with face geometry
3391 - cellGeometry - A local vector with cell geometry
3392 
3393   Output Parameters:
3394 + Nface - The number of faces with field values
3395 . fgeom - The extract the face centroid and normal
3396 - vol   - The cell volume
3397 
3398   Level: developer
3399 
3400 .seealso: DMPlexGetCellFields()
3401 @*/
3402 PetscErrorCode DMPlexGetFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom **fgeom, PetscReal **vol)
3403 {
3404   DM                 dmFace, dmCell;
3405   DMLabel            ghostLabel;
3406   const PetscScalar *facegeom, *cellgeom;
3407   PetscInt           dim, numFaces = fEnd - fStart, iface, face;
3408   PetscErrorCode     ierr;
3409 
3410   PetscFunctionBegin;
3411   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3412   PetscValidHeaderSpecific(faceGeometry, VEC_CLASSID, 4);
3413   PetscValidHeaderSpecific(cellGeometry, VEC_CLASSID, 5);
3414   PetscValidPointer(fgeom, 6);
3415   PetscValidPointer(vol, 7);
3416   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
3417   ierr = DMGetLabel(dm, "ghost", &ghostLabel);CHKERRQ(ierr);
3418   ierr = VecGetDM(faceGeometry, &dmFace);CHKERRQ(ierr);
3419   ierr = VecGetArrayRead(faceGeometry, &facegeom);CHKERRQ(ierr);
3420   ierr = VecGetDM(cellGeometry, &dmCell);CHKERRQ(ierr);
3421   ierr = VecGetArrayRead(cellGeometry, &cellgeom);CHKERRQ(ierr);
3422   ierr = PetscMalloc1(numFaces, fgeom);CHKERRQ(ierr);
3423   ierr = DMGetWorkArray(dm, numFaces*2, MPIU_SCALAR, vol);CHKERRQ(ierr);
3424   for (face = fStart, iface = 0; face < fEnd; ++face) {
3425     const PetscInt        *cells;
3426     PetscFVFaceGeom       *fg;
3427     PetscFVCellGeom       *cgL, *cgR;
3428     PetscFVFaceGeom       *fgeoml = *fgeom;
3429     PetscReal             *voll   = *vol;
3430     PetscInt               ghost, d, nchild, nsupp;
3431 
3432     ierr = DMLabelGetValue(ghostLabel, face, &ghost);CHKERRQ(ierr);
3433     ierr = DMPlexGetSupportSize(dm, face, &nsupp);CHKERRQ(ierr);
3434     ierr = DMPlexGetTreeChildren(dm, face, &nchild, NULL);CHKERRQ(ierr);
3435     if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
3436     ierr = DMPlexPointLocalRead(dmFace, face, facegeom, &fg);CHKERRQ(ierr);
3437     ierr = DMPlexGetSupport(dm, face, &cells);CHKERRQ(ierr);
3438     ierr = DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL);CHKERRQ(ierr);
3439     ierr = DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR);CHKERRQ(ierr);
3440     for (d = 0; d < dim; ++d) {
3441       fgeoml[iface].centroid[d] = fg->centroid[d];
3442       fgeoml[iface].normal[d]   = fg->normal[d];
3443     }
3444     voll[iface*2+0] = cgL->volume;
3445     voll[iface*2+1] = cgR->volume;
3446     ++iface;
3447   }
3448   *Nface = iface;
3449   ierr = VecRestoreArrayRead(faceGeometry, &facegeom);CHKERRQ(ierr);
3450   ierr = VecRestoreArrayRead(cellGeometry, &cellgeom);CHKERRQ(ierr);
3451   PetscFunctionReturn(0);
3452 }
3453 
3454 /*@C
3455   DMPlexRestoreFaceGeometry - Restore the field values values for a chunk of faces
3456 
3457   Input Parameters:
3458 + dm     - The DM
3459 . fStart - The first face to include
3460 . fEnd   - The first face to exclude
3461 . faceGeometry - A local vector with face geometry
3462 - cellGeometry - A local vector with cell geometry
3463 
3464   Output Parameters:
3465 + Nface - The number of faces with field values
3466 . fgeom - The extract the face centroid and normal
3467 - vol   - The cell volume
3468 
3469   Level: developer
3470 
3471 .seealso: DMPlexGetFaceFields()
3472 @*/
3473 PetscErrorCode DMPlexRestoreFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom **fgeom, PetscReal **vol)
3474 {
3475   PetscErrorCode ierr;
3476 
3477   PetscFunctionBegin;
3478   ierr = PetscFree(*fgeom);CHKERRQ(ierr);
3479   ierr = DMRestoreWorkArray(dm, 0, MPIU_REAL, vol);CHKERRQ(ierr);
3480   PetscFunctionReturn(0);
3481 }
3482 
3483 PetscErrorCode DMSNESGetFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
3484 {
3485   char            composeStr[33] = {0};
3486   PetscObjectId   id;
3487   PetscContainer  container;
3488   PetscErrorCode  ierr;
3489 
3490   PetscFunctionBegin;
3491   ierr = PetscObjectGetId((PetscObject)quad,&id);CHKERRQ(ierr);
3492   ierr = PetscSNPrintf(composeStr, 32, "DMSNESGetFEGeom_%x\n", id);CHKERRQ(ierr);
3493   ierr = PetscObjectQuery((PetscObject) pointIS, composeStr, (PetscObject *) &container);CHKERRQ(ierr);
3494   if (container) {
3495     ierr = PetscContainerGetPointer(container, (void **) geom);CHKERRQ(ierr);
3496   } else {
3497     ierr = DMFieldCreateFEGeom(coordField, pointIS, quad, faceData, geom);CHKERRQ(ierr);
3498     ierr = PetscContainerCreate(PETSC_COMM_SELF,&container);CHKERRQ(ierr);
3499     ierr = PetscContainerSetPointer(container, (void *) *geom);CHKERRQ(ierr);
3500     ierr = PetscContainerSetUserDestroy(container, PetscContainerUserDestroy_PetscFEGeom);CHKERRQ(ierr);
3501     ierr = PetscObjectCompose((PetscObject) pointIS, composeStr, (PetscObject) container);CHKERRQ(ierr);
3502     ierr = PetscContainerDestroy(&container);CHKERRQ(ierr);
3503   }
3504   PetscFunctionReturn(0);
3505 }
3506 
3507 PetscErrorCode DMSNESRestoreFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
3508 {
3509   PetscFunctionBegin;
3510   *geom = NULL;
3511   PetscFunctionReturn(0);
3512 }
3513 
3514 PetscErrorCode DMPlexComputeResidual_Patch_Internal(DM dm, PetscSection section, IS cellIS, PetscReal t, Vec locX, Vec locX_t, Vec locF, void *user)
3515 {
3516   DM_Plex         *mesh       = (DM_Plex *) dm->data;
3517   const char      *name       = "Residual";
3518   DM               dmAux      = NULL;
3519   DMLabel          ghostLabel = NULL;
3520   PetscDS          prob       = NULL;
3521   PetscDS          probAux    = NULL;
3522   PetscBool        useFEM     = PETSC_FALSE;
3523   PetscBool        isImplicit = (locX_t || t == PETSC_MIN_REAL) ? PETSC_TRUE : PETSC_FALSE;
3524   DMField          coordField = NULL;
3525   Vec              locA;
3526   PetscScalar     *u = NULL, *u_t, *a, *uL = NULL, *uR = NULL;
3527   IS               chunkIS;
3528   const PetscInt  *cells;
3529   PetscInt         cStart, cEnd, numCells;
3530   PetscInt         Nf, f, totDim, totDimAux, numChunks, cellChunkSize, chunk, fStart, fEnd;
3531   PetscInt         maxDegree = PETSC_MAX_INT;
3532   PetscQuadrature  affineQuad = NULL, *quads = NULL;
3533   PetscFEGeom     *affineGeom = NULL, **geoms = NULL;
3534   PetscErrorCode   ierr;
3535 
3536   PetscFunctionBegin;
3537   ierr = PetscLogEventBegin(DMPLEX_ResidualFEM,dm,0,0,0);CHKERRQ(ierr);
3538   /* FEM+FVM */
3539   /* 1: Get sizes from dm and dmAux */
3540   ierr = DMGetLabel(dm, "ghost", &ghostLabel);CHKERRQ(ierr);
3541   ierr = DMGetDS(dm, &prob);CHKERRQ(ierr);
3542   ierr = PetscDSGetNumFields(prob, &Nf);CHKERRQ(ierr);
3543   ierr = PetscDSGetTotalDimension(prob, &totDim);CHKERRQ(ierr);
3544   ierr = PetscObjectQuery((PetscObject) dm, "A", (PetscObject *) &locA);CHKERRQ(ierr);
3545   if (locA) {
3546     ierr = VecGetDM(locA, &dmAux);CHKERRQ(ierr);
3547     ierr = DMGetDS(dmAux, &probAux);CHKERRQ(ierr);
3548     ierr = PetscDSGetTotalDimension(probAux, &totDimAux);CHKERRQ(ierr);
3549   }
3550   /* 2: Get geometric data */
3551   for (f = 0; f < Nf; ++f) {
3552     PetscObject  obj;
3553     PetscClassId id;
3554     PetscBool    fimp;
3555 
3556     ierr = PetscDSGetImplicit(prob, f, &fimp);CHKERRQ(ierr);
3557     if (isImplicit != fimp) continue;
3558     ierr = PetscDSGetDiscretization(prob, f, &obj);CHKERRQ(ierr);
3559     ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
3560     if (id == PETSCFE_CLASSID) {useFEM = PETSC_TRUE;}
3561     if (id == PETSCFV_CLASSID) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONG,"Use of FVM with PCPATCH not yet implemented");
3562   }
3563   if (useFEM) {
3564     ierr = DMGetCoordinateField(dm, &coordField);CHKERRQ(ierr);
3565     ierr = DMFieldGetDegree(coordField,cellIS,NULL,&maxDegree);CHKERRQ(ierr);
3566     if (maxDegree <= 1) {
3567       ierr = DMFieldCreateDefaultQuadrature(coordField,cellIS,&affineQuad);CHKERRQ(ierr);
3568       if (affineQuad) {
3569         ierr = DMSNESGetFEGeom(coordField,cellIS,affineQuad,PETSC_FALSE,&affineGeom);CHKERRQ(ierr);
3570       }
3571     } else {
3572       ierr = PetscCalloc2(Nf,&quads,Nf,&geoms);CHKERRQ(ierr);
3573       for (f = 0; f < Nf; ++f) {
3574         PetscObject  obj;
3575         PetscClassId id;
3576         PetscBool    fimp;
3577 
3578         ierr = PetscDSGetImplicit(prob, f, &fimp);CHKERRQ(ierr);
3579         if (isImplicit != fimp) continue;
3580         ierr = PetscDSGetDiscretization(prob, f, &obj);CHKERRQ(ierr);
3581         ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
3582         if (id == PETSCFE_CLASSID) {
3583           PetscFE fe = (PetscFE) obj;
3584 
3585           ierr = PetscFEGetQuadrature(fe, &quads[f]);CHKERRQ(ierr);
3586           ierr = PetscObjectReference((PetscObject)quads[f]);CHKERRQ(ierr);
3587           ierr = DMSNESGetFEGeom(coordField,cellIS,quads[f],PETSC_FALSE,&geoms[f]);CHKERRQ(ierr);
3588         }
3589       }
3590     }
3591   }
3592   /* Loop over chunks */
3593   ierr = ISGetPointRange(cellIS, &cStart, &cEnd, &cells);CHKERRQ(ierr);
3594   ierr = DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd);CHKERRQ(ierr);
3595   if (useFEM) {ierr = ISCreate(PETSC_COMM_SELF, &chunkIS);CHKERRQ(ierr);}
3596   numCells      = cEnd - cStart;
3597   numChunks     = 1;
3598   cellChunkSize = numCells/numChunks;
3599   numChunks     = PetscMin(1,numCells);
3600   for (chunk = 0; chunk < numChunks; ++chunk) {
3601     PetscScalar     *elemVec, *fluxL = NULL, *fluxR = NULL;
3602     PetscReal       *vol = NULL;
3603     PetscFVFaceGeom *fgeom = NULL;
3604     PetscInt         cS = cStart+chunk*cellChunkSize, cE = PetscMin(cS+cellChunkSize, cEnd), numCells = cE - cS, c;
3605     PetscInt         numFaces = 0;
3606 
3607     /* Extract field coefficients */
3608     if (useFEM) {
3609       ierr = ISGetPointSubrange(chunkIS, cS, cE, cells);CHKERRQ(ierr);
3610       ierr = DMPlexGetCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a);CHKERRQ(ierr);
3611       ierr = DMGetWorkArray(dm, numCells*totDim, MPIU_SCALAR, &elemVec);CHKERRQ(ierr);
3612       ierr = PetscArrayzero(elemVec, numCells*totDim);CHKERRQ(ierr);
3613     }
3614     /* TODO We will interlace both our field coefficients (u, u_t, uL, uR, etc.) and our output (elemVec, fL, fR). I think this works */
3615     /* Loop over fields */
3616     for (f = 0; f < Nf; ++f) {
3617       PetscObject  obj;
3618       PetscClassId id;
3619       PetscBool    fimp;
3620       PetscInt     numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;
3621 
3622       ierr = PetscDSGetImplicit(prob, f, &fimp);CHKERRQ(ierr);
3623       if (isImplicit != fimp) continue;
3624       ierr = PetscDSGetDiscretization(prob, f, &obj);CHKERRQ(ierr);
3625       ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
3626       if (id == PETSCFE_CLASSID) {
3627         PetscFE         fe = (PetscFE) obj;
3628         PetscFEGeom    *geom = affineGeom ? affineGeom : geoms[f];
3629         PetscFEGeom    *chunkGeom = NULL;
3630         PetscQuadrature quad = affineQuad ? affineQuad : quads[f];
3631         PetscInt        Nq, Nb;
3632 
3633         ierr = PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches);CHKERRQ(ierr);
3634         ierr = PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL);CHKERRQ(ierr);
3635         ierr = PetscFEGetDimension(fe, &Nb);CHKERRQ(ierr);
3636         blockSize = Nb;
3637         batchSize = numBlocks * blockSize;
3638         ierr      = PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches);CHKERRQ(ierr);
3639         numChunks = numCells / (numBatches*batchSize);
3640         Ne        = numChunks*numBatches*batchSize;
3641         Nr        = numCells % (numBatches*batchSize);
3642         offset    = numCells - Nr;
3643         /* Integrate FE residual to get elemVec (need fields at quadrature points) */
3644         /*   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) */
3645         ierr = PetscFEGeomGetChunk(geom,0,offset,&chunkGeom);CHKERRQ(ierr);
3646         ierr = PetscFEIntegrateResidual(prob, f, Ne, chunkGeom, u, u_t, probAux, a, t, elemVec);CHKERRQ(ierr);
3647         ierr = PetscFEGeomGetChunk(geom,offset,numCells,&chunkGeom);CHKERRQ(ierr);
3648         ierr = PetscFEIntegrateResidual(prob, f, Nr, chunkGeom, &u[offset*totDim], u_t ? &u_t[offset*totDim] : NULL, probAux, &a[offset*totDimAux], t, &elemVec[offset*totDim]);CHKERRQ(ierr);
3649         ierr = PetscFEGeomRestoreChunk(geom,offset,numCells,&chunkGeom);CHKERRQ(ierr);
3650       } else if (id == PETSCFV_CLASSID) {
3651         PetscFV fv = (PetscFV) obj;
3652 
3653         Ne = numFaces;
3654         /* Riemann solve over faces (need fields at face centroids) */
3655         /*   We need to evaluate FE fields at those coordinates */
3656         ierr = PetscFVIntegrateRHSFunction(fv, prob, f, Ne, fgeom, vol, uL, uR, fluxL, fluxR);CHKERRQ(ierr);
3657       } else SETERRQ1(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %D", f);
3658     }
3659     /* Loop over domain */
3660     if (useFEM) {
3661       /* Add elemVec to locX */
3662       for (c = cS; c < cE; ++c) {
3663         const PetscInt cell = cells ? cells[c] : c;
3664         const PetscInt cind = c - cStart;
3665 
3666         if (mesh->printFEM > 1) {ierr = DMPrintCellVector(cell, name, totDim, &elemVec[cind*totDim]);CHKERRQ(ierr);}
3667         if (ghostLabel) {
3668           PetscInt ghostVal;
3669 
3670           ierr = DMLabelGetValue(ghostLabel,cell,&ghostVal);CHKERRQ(ierr);
3671           if (ghostVal > 0) continue;
3672         }
3673         ierr = DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind*totDim], ADD_ALL_VALUES);CHKERRQ(ierr);
3674       }
3675     }
3676     /* Handle time derivative */
3677     if (locX_t) {
3678       PetscScalar *x_t, *fa;
3679 
3680       ierr = VecGetArray(locF, &fa);CHKERRQ(ierr);
3681       ierr = VecGetArray(locX_t, &x_t);CHKERRQ(ierr);
3682       for (f = 0; f < Nf; ++f) {
3683         PetscFV      fv;
3684         PetscObject  obj;
3685         PetscClassId id;
3686         PetscInt     pdim, d;
3687 
3688         ierr = PetscDSGetDiscretization(prob, f, &obj);CHKERRQ(ierr);
3689         ierr = PetscObjectGetClassId(obj, &id);CHKERRQ(ierr);
3690         if (id != PETSCFV_CLASSID) continue;
3691         fv   = (PetscFV) obj;
3692         ierr = PetscFVGetNumComponents(fv, &pdim);CHKERRQ(ierr);
3693         for (c = cS; c < cE; ++c) {
3694           const PetscInt cell = cells ? cells[c] : c;
3695           PetscScalar   *u_t, *r;
3696 
3697           if (ghostLabel) {
3698             PetscInt ghostVal;
3699 
3700             ierr = DMLabelGetValue(ghostLabel, cell, &ghostVal);CHKERRQ(ierr);
3701             if (ghostVal > 0) continue;
3702           }
3703           ierr = DMPlexPointLocalFieldRead(dm, cell, f, x_t, &u_t);CHKERRQ(ierr);
3704           ierr = DMPlexPointLocalFieldRef(dm, cell, f, fa, &r);CHKERRQ(ierr);
3705           for (d = 0; d < pdim; ++d) r[d] += u_t[d];
3706         }
3707       }
3708       ierr = VecRestoreArray(locX_t, &x_t);CHKERRQ(ierr);
3709       ierr = VecRestoreArray(locF, &fa);CHKERRQ(ierr);
3710     }
3711     if (useFEM) {
3712       ierr = DMPlexRestoreCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a);CHKERRQ(ierr);
3713       ierr = DMRestoreWorkArray(dm, numCells*totDim, MPIU_SCALAR, &elemVec);CHKERRQ(ierr);
3714     }
3715   }
3716   if (useFEM) {ierr = ISDestroy(&chunkIS);CHKERRQ(ierr);}
3717   ierr = ISRestorePointRange(cellIS, &cStart, &cEnd, &cells);CHKERRQ(ierr);
3718   /* TODO Could include boundary residual here (see DMPlexComputeResidual_Internal) */
3719   if (useFEM) {
3720     if (maxDegree <= 1) {
3721       ierr = DMSNESRestoreFEGeom(coordField,cellIS,affineQuad,PETSC_FALSE,&affineGeom);CHKERRQ(ierr);
3722       ierr = PetscQuadratureDestroy(&affineQuad);CHKERRQ(ierr);
3723     } else {
3724       for (f = 0; f < Nf; ++f) {
3725         ierr = DMSNESRestoreFEGeom(coordField,cellIS,quads[f],PETSC_FALSE,&geoms[f]);CHKERRQ(ierr);
3726         ierr = PetscQuadratureDestroy(&quads[f]);CHKERRQ(ierr);
3727       }
3728       ierr = PetscFree2(quads,geoms);CHKERRQ(ierr);
3729     }
3730   }
3731   ierr = PetscLogEventEnd(DMPLEX_ResidualFEM,dm,0,0,0);CHKERRQ(ierr);
3732   PetscFunctionReturn(0);
3733 }
3734 
3735 /*
3736   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
3737 
3738   X   - The local solution vector
3739   X_t - The local solution time derviative vector, or NULL
3740 */
3741 PetscErrorCode DMPlexComputeJacobian_Patch_Internal(DM dm, PetscSection section, PetscSection globalSection, IS cellIS,
3742                                                     PetscReal t, PetscReal X_tShift, Vec X, Vec X_t, Mat Jac, Mat JacP, void *ctx)
3743 {
3744   DM_Plex         *mesh  = (DM_Plex *) dm->data;
3745   const char      *name = "Jacobian", *nameP = "JacobianPre";
3746   DM               dmAux = NULL;
3747   PetscDS          prob,   probAux = NULL;
3748   PetscSection     sectionAux = NULL;
3749   Vec              A;
3750   DMField          coordField;
3751   PetscFEGeom     *cgeomFEM;
3752   PetscQuadrature  qGeom = NULL;
3753   Mat              J = Jac, JP = JacP;
3754   PetscScalar     *work, *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL, *elemMatP = NULL, *elemMatD = NULL;
3755   PetscBool        hasJac, hasPrec, hasDyn, assembleJac, isMatIS, isMatISP, *isFE, hasFV = PETSC_FALSE;
3756   const PetscInt  *cells;
3757   PetscInt         Nf, fieldI, fieldJ, maxDegree, numCells, cStart, cEnd, numChunks, chunkSize, chunk, totDim, totDimAux = 0, sz, wsz, off = 0, offCell = 0;
3758   PetscErrorCode   ierr;
3759 
3760   PetscFunctionBegin;
3761   CHKMEMQ;
3762   ierr = ISGetLocalSize(cellIS, &numCells);CHKERRQ(ierr);
3763   ierr = ISGetPointRange(cellIS, &cStart, &cEnd, &cells);CHKERRQ(ierr);
3764   ierr = PetscLogEventBegin(DMPLEX_JacobianFEM,dm,0,0,0);CHKERRQ(ierr);
3765   ierr = DMGetDS(dm, &prob);CHKERRQ(ierr);
3766   ierr = PetscObjectQuery((PetscObject) dm, "dmAux", (PetscObject *) &dmAux);CHKERRQ(ierr);
3767   ierr = PetscObjectQuery((PetscObject) dm, "A", (PetscObject *) &A);CHKERRQ(ierr);
3768   if (dmAux) {
3769     ierr = DMGetLocalSection(dmAux, &sectionAux);CHKERRQ(ierr);
3770     ierr = DMGetDS(dmAux, &probAux);CHKERRQ(ierr);
3771   }
3772   /* Get flags */
3773   ierr = PetscDSGetNumFields(prob, &Nf);CHKERRQ(ierr);
3774   ierr = DMGetWorkArray(dm, Nf, MPIU_BOOL, &isFE);CHKERRQ(ierr);
3775   for (fieldI = 0; fieldI < Nf; ++fieldI) {
3776     PetscObject  disc;
3777     PetscClassId id;
3778     ierr = PetscDSGetDiscretization(prob, fieldI, &disc);CHKERRQ(ierr);
3779     ierr = PetscObjectGetClassId(disc, &id);CHKERRQ(ierr);
3780     if (id == PETSCFE_CLASSID)      {isFE[fieldI] = PETSC_TRUE;}
3781     else if (id == PETSCFV_CLASSID) {hasFV = PETSC_TRUE; isFE[fieldI] = PETSC_FALSE;}
3782   }
3783   ierr = PetscDSHasJacobian(prob, &hasJac);CHKERRQ(ierr);
3784   ierr = PetscDSHasJacobianPreconditioner(prob, &hasPrec);CHKERRQ(ierr);
3785   ierr = PetscDSHasDynamicJacobian(prob, &hasDyn);CHKERRQ(ierr);
3786   assembleJac = hasJac && hasPrec && (Jac != JacP) ? PETSC_TRUE : PETSC_FALSE;
3787   hasDyn      = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
3788   ierr = PetscObjectTypeCompare((PetscObject) Jac,  MATIS, &isMatIS);CHKERRQ(ierr);
3789   ierr = PetscObjectTypeCompare((PetscObject) JacP, MATIS, &isMatISP);CHKERRQ(ierr);
3790   /* Setup input data and temp arrays (should be DMGetWorkArray) */
3791   if (isMatISP || isMatISP) {ierr = DMPlexGetSubdomainSection(dm, &globalSection);CHKERRQ(ierr);}
3792   if (isMatIS)  {ierr = MatISGetLocalMat(Jac,  &J);CHKERRQ(ierr);}
3793   if (isMatISP) {ierr = MatISGetLocalMat(JacP, &JP);CHKERRQ(ierr);}
3794   if (hasFV)    {ierr = MatSetOption(JP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE);CHKERRQ(ierr);} /* No allocated space for FV stuff, so ignore the zero entries */
3795   ierr = PetscObjectQuery((PetscObject) dm, "dmAux", (PetscObject *) &dmAux);CHKERRQ(ierr);
3796   ierr = PetscObjectQuery((PetscObject) dm, "A", (PetscObject *) &A);CHKERRQ(ierr);
3797   ierr = PetscDSGetTotalDimension(prob, &totDim);CHKERRQ(ierr);
3798   if (probAux) {ierr = PetscDSGetTotalDimension(probAux, &totDimAux);CHKERRQ(ierr);}
3799   CHKMEMQ;
3800   /* Compute batch sizes */
3801   if (isFE[0]) {
3802     PetscFE         fe;
3803     PetscQuadrature q;
3804     PetscInt        numQuadPoints, numBatches, batchSize, numBlocks, blockSize, Nb;
3805 
3806     ierr = PetscDSGetDiscretization(prob, 0, (PetscObject *) &fe);CHKERRQ(ierr);
3807     ierr = PetscFEGetQuadrature(fe, &q);CHKERRQ(ierr);
3808     ierr = PetscQuadratureGetData(q, NULL, NULL, &numQuadPoints, NULL, NULL);CHKERRQ(ierr);
3809     ierr = PetscFEGetDimension(fe, &Nb);CHKERRQ(ierr);
3810     ierr = PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches);CHKERRQ(ierr);
3811     blockSize = Nb*numQuadPoints;
3812     batchSize = numBlocks  * blockSize;
3813     chunkSize = numBatches * batchSize;
3814     numChunks = numCells / chunkSize + numCells % chunkSize;
3815     ierr = PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches);CHKERRQ(ierr);
3816   } else {
3817     chunkSize = numCells;
3818     numChunks = 1;
3819   }
3820   /* Get work space */
3821   wsz  = (((X?1:0) + (X_t?1:0) + (dmAux?1:0))*totDim + ((hasJac?1:0) + (hasPrec?1:0) + (hasDyn?1:0))*totDim*totDim)*chunkSize;
3822   ierr = DMGetWorkArray(dm, wsz, MPIU_SCALAR, &work);CHKERRQ(ierr);
3823   ierr = PetscArrayzero(work, wsz);CHKERRQ(ierr);
3824   off      = 0;
3825   u        = X       ? (sz = chunkSize*totDim,        off += sz, work+off-sz) : NULL;
3826   u_t      = X_t     ? (sz = chunkSize*totDim,        off += sz, work+off-sz) : NULL;
3827   a        = dmAux   ? (sz = chunkSize*totDimAux,     off += sz, work+off-sz) : NULL;
3828   elemMat  = hasJac  ? (sz = chunkSize*totDim*totDim, off += sz, work+off-sz) : NULL;
3829   elemMatP = hasPrec ? (sz = chunkSize*totDim*totDim, off += sz, work+off-sz) : NULL;
3830   elemMatD = hasDyn  ? (sz = chunkSize*totDim*totDim, off += sz, work+off-sz) : NULL;
3831   if (off != wsz) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Error is workspace size %D should be %D", off, wsz);
3832   /* Setup geometry */
3833   ierr = DMGetCoordinateField(dm, &coordField);CHKERRQ(ierr);
3834   ierr = DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree);CHKERRQ(ierr);
3835   if (maxDegree <= 1) {ierr = DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom);CHKERRQ(ierr);}
3836   if (!qGeom) {
3837     PetscFE fe;
3838 
3839     ierr = PetscDSGetDiscretization(prob, 0, (PetscObject *) &fe);CHKERRQ(ierr);
3840     ierr = PetscFEGetQuadrature(fe, &qGeom);CHKERRQ(ierr);
3841     ierr = PetscObjectReference((PetscObject) qGeom);CHKERRQ(ierr);
3842   }
3843   ierr = DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM);CHKERRQ(ierr);
3844   /* Compute volume integrals */
3845   if (assembleJac) {ierr = MatZeroEntries(J);CHKERRQ(ierr);}
3846   ierr = MatZeroEntries(JP);CHKERRQ(ierr);
3847   for (chunk = 0; chunk < numChunks; ++chunk, offCell += chunkSize) {
3848     const PetscInt   Ncell = PetscMin(chunkSize, numCells - offCell);
3849     PetscInt         c;
3850 
3851     /* Extract values */
3852     for (c = 0; c < Ncell; ++c) {
3853       const PetscInt cell = cells ? cells[c+offCell] : c+offCell;
3854       PetscScalar   *x = NULL,  *x_t = NULL;
3855       PetscInt       i;
3856 
3857       if (X) {
3858         ierr = DMPlexVecGetClosure(dm, section, X, cell, NULL, &x);CHKERRQ(ierr);
3859         for (i = 0; i < totDim; ++i) u[c*totDim+i] = x[i];
3860         ierr = DMPlexVecRestoreClosure(dm, section, X, cell, NULL, &x);CHKERRQ(ierr);
3861       }
3862       if (X_t) {
3863         ierr = DMPlexVecGetClosure(dm, section, X_t, cell, NULL, &x_t);CHKERRQ(ierr);
3864         for (i = 0; i < totDim; ++i) u_t[c*totDim+i] = x_t[i];
3865         ierr = DMPlexVecRestoreClosure(dm, section, X_t, cell, NULL, &x_t);CHKERRQ(ierr);
3866       }
3867       if (dmAux) {
3868         ierr = DMPlexVecGetClosure(dmAux, sectionAux, A, cell, NULL, &x);CHKERRQ(ierr);
3869         for (i = 0; i < totDimAux; ++i) a[c*totDimAux+i] = x[i];
3870         ierr = DMPlexVecRestoreClosure(dmAux, sectionAux, A, cell, NULL, &x);CHKERRQ(ierr);
3871       }
3872     }
3873     CHKMEMQ;
3874     for (fieldI = 0; fieldI < Nf; ++fieldI) {
3875       PetscFE fe;
3876       ierr = PetscDSGetDiscretization(prob, fieldI, (PetscObject *) &fe);CHKERRQ(ierr);
3877       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
3878         if (hasJac)  {ierr = PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN,     fieldI, fieldJ, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMat);CHKERRQ(ierr);}
3879         if (hasPrec) {ierr = PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, fieldI, fieldJ, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatP);CHKERRQ(ierr);}
3880         if (hasDyn)  {ierr = PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, fieldI, fieldJ, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatD);CHKERRQ(ierr);}
3881       }
3882       /* For finite volume, add the identity */
3883       if (!isFE[fieldI]) {
3884         PetscFV  fv;
3885         PetscInt eOffset = 0, Nc, fc, foff;
3886 
3887         ierr = PetscDSGetFieldOffset(prob, fieldI, &foff);CHKERRQ(ierr);
3888         ierr = PetscDSGetDiscretization(prob, fieldI, (PetscObject *) &fv);CHKERRQ(ierr);
3889         ierr = PetscFVGetNumComponents(fv, &Nc);CHKERRQ(ierr);
3890         for (c = 0; c < chunkSize; ++c, eOffset += totDim*totDim) {
3891           for (fc = 0; fc < Nc; ++fc) {
3892             const PetscInt i = foff + fc;
3893             if (hasJac)  {elemMat [eOffset+i*totDim+i] = 1.0;}
3894             if (hasPrec) {elemMatP[eOffset+i*totDim+i] = 1.0;}
3895           }
3896         }
3897       }
3898     }
3899     CHKMEMQ;
3900     /*   Add contribution from X_t */
3901     if (hasDyn) {for (c = 0; c < chunkSize*totDim*totDim; ++c) elemMat[c] += X_tShift*elemMatD[c];}
3902     /* Insert values into matrix */
3903     for (c = 0; c < Ncell; ++c) {
3904       const PetscInt cell = cells ? cells[c+offCell] : c+offCell;
3905       if (mesh->printFEM > 1) {
3906         if (hasJac)  {ierr = DMPrintCellMatrix(cell, name,  totDim, totDim, &elemMat[(c-cStart)*totDim*totDim]);CHKERRQ(ierr);}
3907         if (hasPrec) {ierr = DMPrintCellMatrix(cell, nameP, totDim, totDim, &elemMatP[(c-cStart)*totDim*totDim]);CHKERRQ(ierr);}
3908       }
3909       if (assembleJac) {ierr = DMPlexMatSetClosure(dm, section, globalSection, Jac, cell, &elemMat[(c-cStart)*totDim*totDim], ADD_VALUES);CHKERRQ(ierr);}
3910       ierr = DMPlexMatSetClosure(dm, section, globalSection, JP, cell, &elemMat[(c-cStart)*totDim*totDim], ADD_VALUES);CHKERRQ(ierr);
3911     }
3912     CHKMEMQ;
3913   }
3914   /* Cleanup */
3915   ierr = DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM);CHKERRQ(ierr);
3916   ierr = PetscQuadratureDestroy(&qGeom);CHKERRQ(ierr);
3917   if (hasFV) {ierr = MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE);CHKERRQ(ierr);}
3918   ierr = DMRestoreWorkArray(dm, Nf, MPIU_BOOL, &isFE);CHKERRQ(ierr);
3919   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);
3920   /* Compute boundary integrals */
3921   /* ierr = DMPlexComputeBdJacobian_Internal(dm, X, X_t, t, X_tShift, Jac, JacP, ctx);CHKERRQ(ierr); */
3922   /* Assemble matrix */
3923   if (assembleJac) {ierr = MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);ierr = MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);}
3924   ierr = MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);ierr = MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
3925   ierr = PetscLogEventEnd(DMPLEX_JacobianFEM,dm,0,0,0);CHKERRQ(ierr);
3926   CHKMEMQ;
3927   PetscFunctionReturn(0);
3928 }
3929