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