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