xref: /petsc/src/dm/impls/plex/plexfem.c (revision 76be6f4ff3bd4e251c19fc00ebbebfd58b6e7589)
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   Mat          interp;
2729   PetscSection fsection, globalFSection;
2730   PetscSection csection, globalCSection;
2731   PetscInt     locRows, locCols;
2732   PetscReal   *x, *v0, *J, *invJ, detJ;
2733   PetscReal   *v0c, *Jc, *invJc, detJc;
2734   PetscScalar *elemMat;
2735   PetscInt     dim, Nf, field, totDim, cStart, cEnd, cell, ccell, s;
2736 
2737   PetscFunctionBegin;
2738   PetscCall(PetscLogEventBegin(DMPLEX_InterpolatorFEM,dmc,dmf,0,0));
2739   PetscCall(DMGetCoordinateDim(dmc, &dim));
2740   PetscCall(DMGetDS(dmc, &prob));
2741   PetscCall(PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL));
2742   PetscCall(PetscDSGetNumFields(prob, &Nf));
2743   PetscCall(PetscMalloc3(dim,&v0,dim*dim,&J,dim*dim,&invJ));
2744   PetscCall(PetscMalloc3(dim,&v0c,dim*dim,&Jc,dim*dim,&invJc));
2745   PetscCall(DMGetLocalSection(dmf, &fsection));
2746   PetscCall(DMGetGlobalSection(dmf, &globalFSection));
2747   PetscCall(DMGetLocalSection(dmc, &csection));
2748   PetscCall(DMGetGlobalSection(dmc, &globalCSection));
2749   PetscCall(DMPlexGetSimplexOrBoxCells(dmf, 0, &cStart, &cEnd));
2750   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
2751   PetscCall(PetscMalloc1(totDim, &elemMat));
2752 
2753   PetscCall(MatGetLocalSize(In, &locRows, &locCols));
2754   PetscCall(MatCreate(PetscObjectComm((PetscObject) In), &interp));
2755   PetscCall(MatSetType(interp, MATPREALLOCATOR));
2756   PetscCall(MatSetSizes(interp, locRows, locCols, PETSC_DETERMINE, PETSC_DETERMINE));
2757   PetscCall(MatSetUp(interp));
2758   for (s = 0; s < 2; ++s) {
2759     for (field = 0; field < Nf; ++field) {
2760       PetscObject      obj;
2761       PetscClassId     id;
2762       PetscDualSpace   Q = NULL;
2763       PetscTabulation  T = NULL;
2764       PetscQuadrature  f;
2765       const PetscReal *qpoints, *qweights;
2766       PetscInt         Nc, qNc, Np, fpdim, off, i, d;
2767 
2768       PetscCall(PetscDSGetFieldOffset(prob, field, &off));
2769       PetscCall(PetscDSGetDiscretization(prob, field, &obj));
2770       PetscCall(PetscObjectGetClassId(obj, &id));
2771       if (id == PETSCFE_CLASSID) {
2772         PetscFE fe = (PetscFE) obj;
2773 
2774         PetscCall(PetscFEGetDualSpace(fe, &Q));
2775         PetscCall(PetscFEGetNumComponents(fe, &Nc));
2776         if (s) PetscCall(PetscFECreateTabulation(fe, 1, 1, x, 0, &T));
2777       } else if (id == PETSCFV_CLASSID) {
2778         PetscFV fv = (PetscFV) obj;
2779 
2780         PetscCall(PetscFVGetDualSpace(fv, &Q));
2781         Nc = 1;
2782       } else SETERRQ(PetscObjectComm((PetscObject) dmc), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
2783       PetscCall(PetscDualSpaceGetDimension(Q, &fpdim));
2784       /* For each fine grid cell */
2785       for (cell = cStart; cell < cEnd; ++cell) {
2786         PetscInt *findices,   *cindices;
2787         PetscInt  numFIndices, numCIndices;
2788 
2789         PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
2790         PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ));
2791         PetscCheck(numFIndices == totDim, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fine indices %" PetscInt_FMT " != %" PetscInt_FMT " dual basis vecs", numFIndices, totDim);
2792         for (i = 0; i < fpdim; ++i) {
2793           Vec                pointVec;
2794           PetscScalar       *pV;
2795           PetscSF            coarseCellSF = NULL;
2796           const PetscSFNode *coarseCells;
2797           PetscInt           numCoarseCells, cpdim, row = findices[i+off], q, c, j;
2798 
2799           /* Get points from the dual basis functional quadrature */
2800           PetscCall(PetscDualSpaceGetFunctional(Q, i, &f));
2801           PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, &qpoints, &qweights));
2802           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);
2803           PetscCall(VecCreateSeq(PETSC_COMM_SELF, Np*dim, &pointVec));
2804           PetscCall(VecSetBlockSize(pointVec, dim));
2805           PetscCall(VecGetArray(pointVec, &pV));
2806           for (q = 0; q < Np; ++q) {
2807             const PetscReal xi0[3] = {-1., -1., -1.};
2808 
2809             /* Transform point to real space */
2810             CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q*dim], x);
2811             for (d = 0; d < dim; ++d) pV[q*dim+d] = x[d];
2812           }
2813           PetscCall(VecRestoreArray(pointVec, &pV));
2814           /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
2815           /* OPT: Read this out from preallocation information */
2816           PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF));
2817           /* Update preallocation info */
2818           PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells));
2819           PetscCheck(numCoarseCells == Np, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located");
2820           PetscCall(VecGetArray(pointVec, &pV));
2821           for (ccell = 0; ccell < numCoarseCells; ++ccell) {
2822             PetscReal pVReal[3];
2823             const PetscReal xi0[3] = {-1., -1., -1.};
2824 
2825             PetscCall(DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
2826             if (id == PETSCFE_CLASSID) PetscCall(PetscFEGetDimension((PetscFE) obj, &cpdim));
2827             else                       cpdim = 1;
2828 
2829             if (s) {
2830               /* Transform points from real space to coarse reference space */
2831               PetscCall(DMPlexComputeCellGeometryFEM(dmc, coarseCells[ccell].index, NULL, v0c, Jc, invJc, &detJc));
2832               for (d = 0; d < dim; ++d) pVReal[d] = PetscRealPart(pV[ccell*dim+d]);
2833               CoordinatesRealToRef(dim, dim, xi0, v0c, invJc, pVReal, x);
2834 
2835               if (id == PETSCFE_CLASSID) {
2836                 /* Evaluate coarse basis on contained point */
2837                 PetscCall(PetscFEComputeTabulation((PetscFE) obj, 1, x, 0, T));
2838                 PetscCall(PetscArrayzero(elemMat, cpdim));
2839                 /* Get elemMat entries by multiplying by weight */
2840                 for (j = 0; j < cpdim; ++j) {
2841                   for (c = 0; c < Nc; ++c) elemMat[j] += T->T[0][j*Nc + c]*qweights[ccell*qNc + c];
2842                 }
2843               } else {
2844                 for (j = 0; j < cpdim; ++j) {
2845                   for (c = 0; c < Nc; ++c) elemMat[j] += 1.0*qweights[ccell*qNc + c];
2846                 }
2847               }
2848               if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat));
2849             }
2850             /* Update interpolator */
2851             PetscCheck(numCIndices == totDim,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, totDim);
2852             PetscCall(MatSetValues(interp, 1, &row, cpdim, &cindices[off], elemMat, INSERT_VALUES));
2853             PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
2854           }
2855           PetscCall(VecRestoreArray(pointVec, &pV));
2856           PetscCall(PetscSFDestroy(&coarseCellSF));
2857           PetscCall(VecDestroy(&pointVec));
2858         }
2859         PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
2860       }
2861       if (s && id == PETSCFE_CLASSID) PetscCall(PetscTabulationDestroy(&T));
2862     }
2863     if (!s) {
2864       PetscCall(MatAssemblyBegin(interp, MAT_FINAL_ASSEMBLY));
2865       PetscCall(MatAssemblyEnd(interp, MAT_FINAL_ASSEMBLY));
2866       PetscCall(MatPreallocatorPreallocate(interp, PETSC_TRUE, In));
2867       PetscCall(MatDestroy(&interp));
2868       interp = In;
2869     }
2870   }
2871   PetscCall(PetscFree3(v0, J, invJ));
2872   PetscCall(PetscFree3(v0c, Jc, invJc));
2873   PetscCall(PetscFree(elemMat));
2874   PetscCall(MatAssemblyBegin(In, MAT_FINAL_ASSEMBLY));
2875   PetscCall(MatAssemblyEnd(In, MAT_FINAL_ASSEMBLY));
2876   PetscCall(PetscLogEventEnd(DMPLEX_InterpolatorFEM,dmc,dmf,0,0));
2877   PetscFunctionReturn(0);
2878 }
2879 
2880 /*@
2881   DMPlexComputeMassMatrixGeneral - Form the local portion of the mass matrix M from the coarse DM to a non-nested fine DM.
2882 
2883   Input Parameters:
2884 + dmf  - The fine mesh
2885 . dmc  - The coarse mesh
2886 - user - The user context
2887 
2888   Output Parameter:
2889 . mass  - The mass matrix
2890 
2891   Level: developer
2892 
2893 .seealso: `DMPlexComputeMassMatrixNested()`, `DMPlexComputeInterpolatorNested()`, `DMPlexComputeInterpolatorGeneral()`, `DMPlexComputeJacobianFEM()`
2894 @*/
2895 PetscErrorCode DMPlexComputeMassMatrixGeneral(DM dmc, DM dmf, Mat mass, void *user)
2896 {
2897   DM_Plex       *mesh = (DM_Plex *) dmf->data;
2898   const char    *name = "Mass Matrix";
2899   PetscDS        prob;
2900   PetscSection   fsection, csection, globalFSection, globalCSection;
2901   PetscHSetIJ    ht;
2902   PetscLayout    rLayout;
2903   PetscInt      *dnz, *onz;
2904   PetscInt       locRows, rStart, rEnd;
2905   PetscReal     *x, *v0, *J, *invJ, detJ;
2906   PetscReal     *v0c, *Jc, *invJc, detJc;
2907   PetscScalar   *elemMat;
2908   PetscInt       dim, Nf, field, totDim, cStart, cEnd, cell, ccell;
2909 
2910   PetscFunctionBegin;
2911   PetscCall(DMGetCoordinateDim(dmc, &dim));
2912   PetscCall(DMGetDS(dmc, &prob));
2913   PetscCall(PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL));
2914   PetscCall(PetscDSGetNumFields(prob, &Nf));
2915   PetscCall(PetscMalloc3(dim,&v0,dim*dim,&J,dim*dim,&invJ));
2916   PetscCall(PetscMalloc3(dim,&v0c,dim*dim,&Jc,dim*dim,&invJc));
2917   PetscCall(DMGetLocalSection(dmf, &fsection));
2918   PetscCall(DMGetGlobalSection(dmf, &globalFSection));
2919   PetscCall(DMGetLocalSection(dmc, &csection));
2920   PetscCall(DMGetGlobalSection(dmc, &globalCSection));
2921   PetscCall(DMPlexGetHeightStratum(dmf, 0, &cStart, &cEnd));
2922   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
2923   PetscCall(PetscMalloc1(totDim, &elemMat));
2924 
2925   PetscCall(MatGetLocalSize(mass, &locRows, NULL));
2926   PetscCall(PetscLayoutCreate(PetscObjectComm((PetscObject) mass), &rLayout));
2927   PetscCall(PetscLayoutSetLocalSize(rLayout, locRows));
2928   PetscCall(PetscLayoutSetBlockSize(rLayout, 1));
2929   PetscCall(PetscLayoutSetUp(rLayout));
2930   PetscCall(PetscLayoutGetRange(rLayout, &rStart, &rEnd));
2931   PetscCall(PetscLayoutDestroy(&rLayout));
2932   PetscCall(PetscCalloc2(locRows,&dnz,locRows,&onz));
2933   PetscCall(PetscHSetIJCreate(&ht));
2934   for (field = 0; field < Nf; ++field) {
2935     PetscObject      obj;
2936     PetscClassId     id;
2937     PetscQuadrature  quad;
2938     const PetscReal *qpoints;
2939     PetscInt         Nq, Nc, i, d;
2940 
2941     PetscCall(PetscDSGetDiscretization(prob, field, &obj));
2942     PetscCall(PetscObjectGetClassId(obj, &id));
2943     if (id == PETSCFE_CLASSID) PetscCall(PetscFEGetQuadrature((PetscFE) obj, &quad));
2944     else                       PetscCall(PetscFVGetQuadrature((PetscFV) obj, &quad));
2945     PetscCall(PetscQuadratureGetData(quad, NULL, &Nc, &Nq, &qpoints, NULL));
2946     /* For each fine grid cell */
2947     for (cell = cStart; cell < cEnd; ++cell) {
2948       Vec                pointVec;
2949       PetscScalar       *pV;
2950       PetscSF            coarseCellSF = NULL;
2951       const PetscSFNode *coarseCells;
2952       PetscInt           numCoarseCells, q, c;
2953       PetscInt          *findices,   *cindices;
2954       PetscInt           numFIndices, numCIndices;
2955 
2956       PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
2957       PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ));
2958       /* Get points from the quadrature */
2959       PetscCall(VecCreateSeq(PETSC_COMM_SELF, Nq*dim, &pointVec));
2960       PetscCall(VecSetBlockSize(pointVec, dim));
2961       PetscCall(VecGetArray(pointVec, &pV));
2962       for (q = 0; q < Nq; ++q) {
2963         const PetscReal xi0[3] = {-1., -1., -1.};
2964 
2965         /* Transform point to real space */
2966         CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q*dim], x);
2967         for (d = 0; d < dim; ++d) pV[q*dim+d] = x[d];
2968       }
2969       PetscCall(VecRestoreArray(pointVec, &pV));
2970       /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
2971       PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF));
2972       PetscCall(PetscSFViewFromOptions(coarseCellSF, NULL, "-interp_sf_view"));
2973       /* Update preallocation info */
2974       PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells));
2975       PetscCheck(numCoarseCells == Nq,PETSC_COMM_SELF,PETSC_ERR_PLIB,"Not all closure points located");
2976       {
2977         PetscHashIJKey key;
2978         PetscBool      missing;
2979 
2980         for (i = 0; i < numFIndices; ++i) {
2981           key.i = findices[i];
2982           if (key.i >= 0) {
2983             /* Get indices for coarse elements */
2984             for (ccell = 0; ccell < numCoarseCells; ++ccell) {
2985               PetscCall(DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
2986               for (c = 0; c < numCIndices; ++c) {
2987                 key.j = cindices[c];
2988                 if (key.j < 0) continue;
2989                 PetscCall(PetscHSetIJQueryAdd(ht, key, &missing));
2990                 if (missing) {
2991                   if ((key.j >= rStart) && (key.j < rEnd)) ++dnz[key.i-rStart];
2992                   else                                     ++onz[key.i-rStart];
2993                 }
2994               }
2995               PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
2996             }
2997           }
2998         }
2999       }
3000       PetscCall(PetscSFDestroy(&coarseCellSF));
3001       PetscCall(VecDestroy(&pointVec));
3002       PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3003     }
3004   }
3005   PetscCall(PetscHSetIJDestroy(&ht));
3006   PetscCall(MatXAIJSetPreallocation(mass, 1, dnz, onz, NULL, NULL));
3007   PetscCall(MatSetOption(mass, MAT_NEW_NONZERO_ALLOCATION_ERR,PETSC_TRUE));
3008   PetscCall(PetscFree2(dnz,onz));
3009   for (field = 0; field < Nf; ++field) {
3010     PetscObject       obj;
3011     PetscClassId      id;
3012     PetscTabulation T, Tfine;
3013     PetscQuadrature   quad;
3014     const PetscReal  *qpoints, *qweights;
3015     PetscInt          Nq, Nc, i, d;
3016 
3017     PetscCall(PetscDSGetDiscretization(prob, field, &obj));
3018     PetscCall(PetscObjectGetClassId(obj, &id));
3019     if (id == PETSCFE_CLASSID) {
3020       PetscCall(PetscFEGetQuadrature((PetscFE) obj, &quad));
3021       PetscCall(PetscFEGetCellTabulation((PetscFE) obj, 1, &Tfine));
3022       PetscCall(PetscFECreateTabulation((PetscFE) obj, 1, 1, x, 0, &T));
3023     } else {
3024       PetscCall(PetscFVGetQuadrature((PetscFV) obj, &quad));
3025     }
3026     PetscCall(PetscQuadratureGetData(quad, NULL, &Nc, &Nq, &qpoints, &qweights));
3027     /* For each fine grid cell */
3028     for (cell = cStart; cell < cEnd; ++cell) {
3029       Vec                pointVec;
3030       PetscScalar       *pV;
3031       PetscSF            coarseCellSF = NULL;
3032       const PetscSFNode *coarseCells;
3033       PetscInt           numCoarseCells, cpdim, q, c, j;
3034       PetscInt          *findices,   *cindices;
3035       PetscInt           numFIndices, numCIndices;
3036 
3037       PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3038       PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ));
3039       /* Get points from the quadrature */
3040       PetscCall(VecCreateSeq(PETSC_COMM_SELF, Nq*dim, &pointVec));
3041       PetscCall(VecSetBlockSize(pointVec, dim));
3042       PetscCall(VecGetArray(pointVec, &pV));
3043       for (q = 0; q < Nq; ++q) {
3044         const PetscReal xi0[3] = {-1., -1., -1.};
3045 
3046         /* Transform point to real space */
3047         CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q*dim], x);
3048         for (d = 0; d < dim; ++d) pV[q*dim+d] = x[d];
3049       }
3050       PetscCall(VecRestoreArray(pointVec, &pV));
3051       /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
3052       PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF));
3053       /* Update matrix */
3054       PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells));
3055       PetscCheck(numCoarseCells == Nq,PETSC_COMM_SELF,PETSC_ERR_PLIB,"Not all closure points located");
3056       PetscCall(VecGetArray(pointVec, &pV));
3057       for (ccell = 0; ccell < numCoarseCells; ++ccell) {
3058         PetscReal pVReal[3];
3059         const PetscReal xi0[3] = {-1., -1., -1.};
3060 
3061         PetscCall(DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3062         /* Transform points from real space to coarse reference space */
3063         PetscCall(DMPlexComputeCellGeometryFEM(dmc, coarseCells[ccell].index, NULL, v0c, Jc, invJc, &detJc));
3064         for (d = 0; d < dim; ++d) pVReal[d] = PetscRealPart(pV[ccell*dim+d]);
3065         CoordinatesRealToRef(dim, dim, xi0, v0c, invJc, pVReal, x);
3066 
3067         if (id == PETSCFE_CLASSID) {
3068           PetscFE fe = (PetscFE) obj;
3069 
3070           /* Evaluate coarse basis on contained point */
3071           PetscCall(PetscFEGetDimension(fe, &cpdim));
3072           PetscCall(PetscFEComputeTabulation(fe, 1, x, 0, T));
3073           /* Get elemMat entries by multiplying by weight */
3074           for (i = 0; i < numFIndices; ++i) {
3075             PetscCall(PetscArrayzero(elemMat, cpdim));
3076             for (j = 0; j < cpdim; ++j) {
3077               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;
3078             }
3079             /* Update interpolator */
3080             if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat));
3081             PetscCheck(numCIndices == cpdim,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, cpdim);
3082             PetscCall(MatSetValues(mass, 1, &findices[i], numCIndices, cindices, elemMat, ADD_VALUES));
3083           }
3084         } else {
3085           cpdim = 1;
3086           for (i = 0; i < numFIndices; ++i) {
3087             PetscCall(PetscArrayzero(elemMat, cpdim));
3088             for (j = 0; j < cpdim; ++j) {
3089               for (c = 0; c < Nc; ++c) elemMat[j] += 1.0*1.0*qweights[ccell*Nc + c]*detJ;
3090             }
3091             /* Update interpolator */
3092             if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat));
3093             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));
3094             PetscCheck(numCIndices == cpdim,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, cpdim);
3095             PetscCall(MatSetValues(mass, 1, &findices[i], numCIndices, cindices, elemMat, ADD_VALUES));
3096           }
3097         }
3098         PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3099       }
3100       PetscCall(VecRestoreArray(pointVec, &pV));
3101       PetscCall(PetscSFDestroy(&coarseCellSF));
3102       PetscCall(VecDestroy(&pointVec));
3103       PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3104     }
3105     if (id == PETSCFE_CLASSID) PetscCall(PetscTabulationDestroy(&T));
3106   }
3107   PetscCall(PetscFree3(v0,J,invJ));
3108   PetscCall(PetscFree3(v0c,Jc,invJc));
3109   PetscCall(PetscFree(elemMat));
3110   PetscCall(MatAssemblyBegin(mass, MAT_FINAL_ASSEMBLY));
3111   PetscCall(MatAssemblyEnd(mass, MAT_FINAL_ASSEMBLY));
3112   PetscFunctionReturn(0);
3113 }
3114 
3115 /*@
3116   DMPlexComputeInjectorFEM - Compute a mapping from coarse unknowns to fine unknowns
3117 
3118   Input Parameters:
3119 + dmc  - The coarse mesh
3120 - dmf  - The fine mesh
3121 - user - The user context
3122 
3123   Output Parameter:
3124 . sc   - The mapping
3125 
3126   Level: developer
3127 
3128 .seealso: `DMPlexComputeInterpolatorNested()`, `DMPlexComputeJacobianFEM()`
3129 @*/
3130 PetscErrorCode DMPlexComputeInjectorFEM(DM dmc, DM dmf, VecScatter *sc, void *user)
3131 {
3132   PetscDS        prob;
3133   PetscFE       *feRef;
3134   PetscFV       *fvRef;
3135   Vec            fv, cv;
3136   IS             fis, cis;
3137   PetscSection   fsection, fglobalSection, csection, cglobalSection;
3138   PetscInt      *cmap, *cellCIndices, *cellFIndices, *cindices, *findices;
3139   PetscInt       cTotDim, fTotDim = 0, Nf, f, field, cStart, cEnd, c, dim, d, startC, endC, offsetC, offsetF, m;
3140   PetscBool     *needAvg;
3141 
3142   PetscFunctionBegin;
3143   PetscCall(PetscLogEventBegin(DMPLEX_InjectorFEM,dmc,dmf,0,0));
3144   PetscCall(DMGetDimension(dmf, &dim));
3145   PetscCall(DMGetLocalSection(dmf, &fsection));
3146   PetscCall(DMGetGlobalSection(dmf, &fglobalSection));
3147   PetscCall(DMGetLocalSection(dmc, &csection));
3148   PetscCall(DMGetGlobalSection(dmc, &cglobalSection));
3149   PetscCall(PetscSectionGetNumFields(fsection, &Nf));
3150   PetscCall(DMPlexGetSimplexOrBoxCells(dmc, 0, &cStart, &cEnd));
3151   PetscCall(DMGetDS(dmc, &prob));
3152   PetscCall(PetscCalloc3(Nf,&feRef,Nf,&fvRef,Nf,&needAvg));
3153   for (f = 0; f < Nf; ++f) {
3154     PetscObject  obj;
3155     PetscClassId id;
3156     PetscInt     fNb = 0, Nc = 0;
3157 
3158     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
3159     PetscCall(PetscObjectGetClassId(obj, &id));
3160     if (id == PETSCFE_CLASSID) {
3161       PetscFE    fe = (PetscFE) obj;
3162       PetscSpace sp;
3163       PetscInt   maxDegree;
3164 
3165       PetscCall(PetscFERefine(fe, &feRef[f]));
3166       PetscCall(PetscFEGetDimension(feRef[f], &fNb));
3167       PetscCall(PetscFEGetNumComponents(fe, &Nc));
3168       PetscCall(PetscFEGetBasisSpace(fe, &sp));
3169       PetscCall(PetscSpaceGetDegree(sp, NULL, &maxDegree));
3170       if (!maxDegree) needAvg[f] = PETSC_TRUE;
3171     } else if (id == PETSCFV_CLASSID) {
3172       PetscFV        fv = (PetscFV) obj;
3173       PetscDualSpace Q;
3174 
3175       PetscCall(PetscFVRefine(fv, &fvRef[f]));
3176       PetscCall(PetscFVGetDualSpace(fvRef[f], &Q));
3177       PetscCall(PetscDualSpaceGetDimension(Q, &fNb));
3178       PetscCall(PetscFVGetNumComponents(fv, &Nc));
3179       needAvg[f] = PETSC_TRUE;
3180     }
3181     fTotDim += fNb;
3182   }
3183   PetscCall(PetscDSGetTotalDimension(prob, &cTotDim));
3184   PetscCall(PetscMalloc1(cTotDim,&cmap));
3185   for (field = 0, offsetC = 0, offsetF = 0; field < Nf; ++field) {
3186     PetscFE        feC;
3187     PetscFV        fvC;
3188     PetscDualSpace QF, QC;
3189     PetscInt       order = -1, NcF, NcC, fpdim, cpdim;
3190 
3191     if (feRef[field]) {
3192       PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *) &feC));
3193       PetscCall(PetscFEGetNumComponents(feC, &NcC));
3194       PetscCall(PetscFEGetNumComponents(feRef[field], &NcF));
3195       PetscCall(PetscFEGetDualSpace(feRef[field], &QF));
3196       PetscCall(PetscDualSpaceGetOrder(QF, &order));
3197       PetscCall(PetscDualSpaceGetDimension(QF, &fpdim));
3198       PetscCall(PetscFEGetDualSpace(feC, &QC));
3199       PetscCall(PetscDualSpaceGetDimension(QC, &cpdim));
3200     } else {
3201       PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *) &fvC));
3202       PetscCall(PetscFVGetNumComponents(fvC, &NcC));
3203       PetscCall(PetscFVGetNumComponents(fvRef[field], &NcF));
3204       PetscCall(PetscFVGetDualSpace(fvRef[field], &QF));
3205       PetscCall(PetscDualSpaceGetDimension(QF, &fpdim));
3206       PetscCall(PetscFVGetDualSpace(fvC, &QC));
3207       PetscCall(PetscDualSpaceGetDimension(QC, &cpdim));
3208     }
3209     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);
3210     for (c = 0; c < cpdim; ++c) {
3211       PetscQuadrature  cfunc;
3212       const PetscReal *cqpoints, *cqweights;
3213       PetscInt         NqcC, NpC;
3214       PetscBool        found = PETSC_FALSE;
3215 
3216       PetscCall(PetscDualSpaceGetFunctional(QC, c, &cfunc));
3217       PetscCall(PetscQuadratureGetData(cfunc, NULL, &NqcC, &NpC, &cqpoints, &cqweights));
3218       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);
3219       PetscCheck(NpC == 1 || !feRef[field],PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Do not know how to do injection for moments");
3220       for (f = 0; f < fpdim; ++f) {
3221         PetscQuadrature  ffunc;
3222         const PetscReal *fqpoints, *fqweights;
3223         PetscReal        sum = 0.0;
3224         PetscInt         NqcF, NpF;
3225 
3226         PetscCall(PetscDualSpaceGetFunctional(QF, f, &ffunc));
3227         PetscCall(PetscQuadratureGetData(ffunc, NULL, &NqcF, &NpF, &fqpoints, &fqweights));
3228         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);
3229         if (NpC != NpF) continue;
3230         for (d = 0; d < dim; ++d) sum += PetscAbsReal(cqpoints[d] - fqpoints[d]);
3231         if (sum > 1.0e-9) continue;
3232         for (d = 0; d < NcC; ++d) sum += PetscAbsReal(cqweights[d]*fqweights[d]);
3233         if (sum < 1.0e-9) continue;
3234         cmap[offsetC+c] = offsetF+f;
3235         found = PETSC_TRUE;
3236         break;
3237       }
3238       if (!found) {
3239         /* TODO We really want the average here, but some asshole put VecScatter in the interface */
3240         if (fvRef[field] || (feRef[field] && order == 0)) {
3241           cmap[offsetC+c] = offsetF+0;
3242         } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Could not locate matching functional for injection");
3243       }
3244     }
3245     offsetC += cpdim;
3246     offsetF += fpdim;
3247   }
3248   for (f = 0; f < Nf; ++f) {PetscCall(PetscFEDestroy(&feRef[f]));PetscCall(PetscFVDestroy(&fvRef[f]));}
3249   PetscCall(PetscFree3(feRef,fvRef,needAvg));
3250 
3251   PetscCall(DMGetGlobalVector(dmf, &fv));
3252   PetscCall(DMGetGlobalVector(dmc, &cv));
3253   PetscCall(VecGetOwnershipRange(cv, &startC, &endC));
3254   PetscCall(PetscSectionGetConstrainedStorageSize(cglobalSection, &m));
3255   PetscCall(PetscMalloc2(cTotDim,&cellCIndices,fTotDim,&cellFIndices));
3256   PetscCall(PetscMalloc1(m,&cindices));
3257   PetscCall(PetscMalloc1(m,&findices));
3258   for (d = 0; d < m; ++d) cindices[d] = findices[d] = -1;
3259   for (c = cStart; c < cEnd; ++c) {
3260     PetscCall(DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, c, cellCIndices, cellFIndices));
3261     for (d = 0; d < cTotDim; ++d) {
3262       if ((cellCIndices[d] < startC) || (cellCIndices[d] >= endC)) continue;
3263       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]]);
3264       cindices[cellCIndices[d]-startC] = cellCIndices[d];
3265       findices[cellCIndices[d]-startC] = cellFIndices[cmap[d]];
3266     }
3267   }
3268   PetscCall(PetscFree(cmap));
3269   PetscCall(PetscFree2(cellCIndices,cellFIndices));
3270 
3271   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, m, cindices, PETSC_OWN_POINTER, &cis));
3272   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, m, findices, PETSC_OWN_POINTER, &fis));
3273   PetscCall(VecScatterCreate(cv, cis, fv, fis, sc));
3274   PetscCall(ISDestroy(&cis));
3275   PetscCall(ISDestroy(&fis));
3276   PetscCall(DMRestoreGlobalVector(dmf, &fv));
3277   PetscCall(DMRestoreGlobalVector(dmc, &cv));
3278   PetscCall(PetscLogEventEnd(DMPLEX_InjectorFEM,dmc,dmf,0,0));
3279   PetscFunctionReturn(0);
3280 }
3281 
3282 /*@C
3283   DMPlexGetCellFields - Retrieve the field values values for a chunk of cells
3284 
3285   Input Parameters:
3286 + dm     - The DM
3287 . cellIS - The cells to include
3288 . locX   - A local vector with the solution fields
3289 . locX_t - A local vector with solution field time derivatives, or NULL
3290 - locA   - A local vector with auxiliary fields, or NULL
3291 
3292   Output Parameters:
3293 + u   - The field coefficients
3294 . u_t - The fields derivative coefficients
3295 - a   - The auxiliary field coefficients
3296 
3297   Level: developer
3298 
3299 .seealso: `DMPlexGetFaceFields()`
3300 @*/
3301 PetscErrorCode DMPlexGetCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
3302 {
3303   DM              plex, plexA = NULL;
3304   DMEnclosureType encAux;
3305   PetscSection    section, sectionAux;
3306   PetscDS         prob;
3307   const PetscInt *cells;
3308   PetscInt        cStart, cEnd, numCells, totDim, totDimAux, c;
3309 
3310   PetscFunctionBegin;
3311   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3312   PetscValidHeaderSpecific(locX, VEC_CLASSID, 3);
3313   if (locX_t) {PetscValidHeaderSpecific(locX_t, VEC_CLASSID, 4);}
3314   if (locA)   {PetscValidHeaderSpecific(locA, VEC_CLASSID, 5);}
3315   PetscValidPointer(u, 6);
3316   PetscValidPointer(u_t, 7);
3317   PetscValidPointer(a, 8);
3318   PetscCall(DMPlexConvertPlex(dm, &plex, PETSC_FALSE));
3319   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
3320   PetscCall(DMGetLocalSection(dm, &section));
3321   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob));
3322   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
3323   if (locA) {
3324     DM      dmAux;
3325     PetscDS probAux;
3326 
3327     PetscCall(VecGetDM(locA, &dmAux));
3328     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
3329     PetscCall(DMPlexConvertPlex(dmAux, &plexA, PETSC_FALSE));
3330     PetscCall(DMGetLocalSection(dmAux, &sectionAux));
3331     PetscCall(DMGetDS(dmAux, &probAux));
3332     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
3333   }
3334   numCells = cEnd - cStart;
3335   PetscCall(DMGetWorkArray(dm, numCells*totDim, MPIU_SCALAR, u));
3336   if (locX_t) PetscCall(DMGetWorkArray(dm, numCells*totDim, MPIU_SCALAR, u_t)); else {*u_t = NULL;}
3337   if (locA)   PetscCall(DMGetWorkArray(dm, numCells*totDimAux, MPIU_SCALAR, a)); else {*a = NULL;}
3338   for (c = cStart; c < cEnd; ++c) {
3339     const PetscInt cell = cells ? cells[c] : c;
3340     const PetscInt cind = c - cStart;
3341     PetscScalar   *x = NULL, *x_t = NULL, *ul = *u, *ul_t = *u_t, *al = *a;
3342     PetscInt       i;
3343 
3344     PetscCall(DMPlexVecGetClosure(plex, section, locX, cell, NULL, &x));
3345     for (i = 0; i < totDim; ++i) ul[cind*totDim+i] = x[i];
3346     PetscCall(DMPlexVecRestoreClosure(plex, section, locX, cell, NULL, &x));
3347     if (locX_t) {
3348       PetscCall(DMPlexVecGetClosure(plex, section, locX_t, cell, NULL, &x_t));
3349       for (i = 0; i < totDim; ++i) ul_t[cind*totDim+i] = x_t[i];
3350       PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, cell, NULL, &x_t));
3351     }
3352     if (locA) {
3353       PetscInt subcell;
3354       PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, cell, &subcell));
3355       PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subcell, NULL, &x));
3356       for (i = 0; i < totDimAux; ++i) al[cind*totDimAux+i] = x[i];
3357       PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subcell, NULL, &x));
3358     }
3359   }
3360   PetscCall(DMDestroy(&plex));
3361   if (locA) PetscCall(DMDestroy(&plexA));
3362   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
3363   PetscFunctionReturn(0);
3364 }
3365 
3366 /*@C
3367   DMPlexRestoreCellFields - Restore the field values values for a chunk of cells
3368 
3369   Input Parameters:
3370 + dm     - The DM
3371 . cellIS - The cells to include
3372 . locX   - A local vector with the solution fields
3373 . locX_t - A local vector with solution field time derivatives, or NULL
3374 - locA   - A local vector with auxiliary fields, or NULL
3375 
3376   Output Parameters:
3377 + u   - The field coefficients
3378 . u_t - The fields derivative coefficients
3379 - a   - The auxiliary field coefficients
3380 
3381   Level: developer
3382 
3383 .seealso: `DMPlexGetFaceFields()`
3384 @*/
3385 PetscErrorCode DMPlexRestoreCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
3386 {
3387   PetscFunctionBegin;
3388   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u));
3389   if (locX_t) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u_t));
3390   if (locA)   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, a));
3391   PetscFunctionReturn(0);
3392 }
3393 
3394 /*
3395   Get the auxiliary field vectors for the negative side (s = 0) and positive side (s = 1) of the interfaace
3396 */
3397 static PetscErrorCode DMPlexGetHybridAuxFields(DM dm, DM dmAux[], PetscDS dsAux[], IS cellIS, Vec locA[], PetscScalar *a[])
3398 {
3399   DM              plexA[2];
3400   DMEnclosureType encAux[2];
3401   PetscSection    sectionAux[2];
3402   const PetscInt *cells;
3403   PetscInt        cStart, cEnd, numCells, c, s, totDimAux[2];
3404 
3405   PetscFunctionBegin;
3406   PetscValidPointer(locA, 5);
3407   if (!locA[0] || !locA[1]) PetscFunctionReturn(0);
3408   PetscValidPointer(dmAux, 2);
3409   PetscValidPointer(dsAux, 3);
3410   PetscValidPointer(a, 6);
3411   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
3412   numCells = cEnd - cStart;
3413   for (s = 0; s < 2; ++s) {
3414     PetscValidHeaderSpecific(dmAux[s], DM_CLASSID, 2);
3415     PetscValidHeaderSpecific(dsAux[s], PETSCDS_CLASSID, 3);
3416     PetscValidHeaderSpecific(locA[s], VEC_CLASSID, 5);
3417     PetscCall(DMPlexConvertPlex(dmAux[s], &plexA[s], PETSC_FALSE));
3418     PetscCall(DMGetEnclosureRelation(dmAux[s], dm, &encAux[s]));
3419     PetscCall(DMGetLocalSection(dmAux[s], &sectionAux[s]));
3420     PetscCall(PetscDSGetTotalDimension(dsAux[s], &totDimAux[s]));
3421     PetscCall(DMGetWorkArray(dmAux[s], numCells*totDimAux[s], MPIU_SCALAR, &a[s]));
3422   }
3423   for (c = cStart; c < cEnd; ++c) {
3424     const PetscInt  cell = cells ? cells[c] : c;
3425     const PetscInt  cind = c - cStart;
3426     const PetscInt *cone, *ornt;
3427 
3428     PetscCall(DMPlexGetCone(dm, cell, &cone));
3429     PetscCall(DMPlexGetConeOrientation(dm, cell, &ornt));
3430     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]);
3431     for (s = 0; s < 2; ++s) {
3432       const PetscInt *support;
3433       PetscScalar    *x = NULL, *al = a[s];
3434       const PetscInt  tdA = totDimAux[s];
3435       PetscInt        ssize, scell;
3436       PetscInt        subface, Na, i;
3437 
3438       PetscCall(DMPlexGetSupport(dm, cone[s], &support));
3439       PetscCall(DMPlexGetSupportSize(dm, cone[s], &ssize));
3440       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);
3441       if      (support[0] == cell) scell = support[1];
3442       else if (support[1] == cell) scell = support[0];
3443       else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[s], cell);
3444 
3445       PetscCall(DMGetEnclosurePoint(plexA[s], dm, encAux[s], scell, &subface));
3446       PetscCall(DMPlexVecGetClosure(plexA[s], sectionAux[s], locA[s], subface, &Na, &x));
3447       for (i = 0; i < Na; ++i) al[cind*tdA+i] = x[i];
3448       PetscCall(DMPlexVecRestoreClosure(plexA[s], sectionAux[s], locA[s], subface, &Na, &x));
3449     }
3450   }
3451   for (s = 0; s < 2; ++s) PetscCall(DMDestroy(&plexA[s]));
3452   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
3453   PetscFunctionReturn(0);
3454 }
3455 
3456 static PetscErrorCode DMPlexRestoreHybridAuxFields(DM dmAux[], PetscDS dsAux[], IS cellIS, Vec locA[], PetscScalar *a[])
3457 {
3458   PetscFunctionBegin;
3459   if (!locA[0] || !locA[1]) PetscFunctionReturn(0);
3460   PetscCall(DMRestoreWorkArray(dmAux[0], 0, MPIU_SCALAR, &a[0]));
3461   PetscCall(DMRestoreWorkArray(dmAux[1], 0, MPIU_SCALAR, &a[1]));
3462   PetscFunctionReturn(0);
3463 }
3464 
3465 /*@C
3466   DMPlexGetFaceFields - Retrieve the field values values for a chunk of faces
3467 
3468   Input Parameters:
3469 + dm     - The DM
3470 . fStart - The first face to include
3471 . fEnd   - The first face to exclude
3472 . locX   - A local vector with the solution fields
3473 . locX_t - A local vector with solution field time derivatives, or NULL
3474 . faceGeometry - A local vector with face geometry
3475 . cellGeometry - A local vector with cell geometry
3476 - locaGrad - A local vector with field gradients, or NULL
3477 
3478   Output Parameters:
3479 + Nface - The number of faces with field values
3480 . uL - The field values at the left side of the face
3481 - uR - The field values at the right side of the face
3482 
3483   Level: developer
3484 
3485 .seealso: `DMPlexGetCellFields()`
3486 @*/
3487 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)
3488 {
3489   DM                 dmFace, dmCell, dmGrad = NULL;
3490   PetscSection       section;
3491   PetscDS            prob;
3492   DMLabel            ghostLabel;
3493   const PetscScalar *facegeom, *cellgeom, *x, *lgrad;
3494   PetscBool         *isFE;
3495   PetscInt           dim, Nf, f, Nc, numFaces = fEnd - fStart, iface, face;
3496 
3497   PetscFunctionBegin;
3498   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3499   PetscValidHeaderSpecific(locX, VEC_CLASSID, 4);
3500   if (locX_t) {PetscValidHeaderSpecific(locX_t, VEC_CLASSID, 5);}
3501   PetscValidHeaderSpecific(faceGeometry, VEC_CLASSID, 6);
3502   PetscValidHeaderSpecific(cellGeometry, VEC_CLASSID, 7);
3503   if (locGrad) {PetscValidHeaderSpecific(locGrad, VEC_CLASSID, 8);}
3504   PetscValidPointer(uL, 10);
3505   PetscValidPointer(uR, 11);
3506   PetscCall(DMGetDimension(dm, &dim));
3507   PetscCall(DMGetDS(dm, &prob));
3508   PetscCall(DMGetLocalSection(dm, &section));
3509   PetscCall(PetscDSGetNumFields(prob, &Nf));
3510   PetscCall(PetscDSGetTotalComponents(prob, &Nc));
3511   PetscCall(PetscMalloc1(Nf, &isFE));
3512   for (f = 0; f < Nf; ++f) {
3513     PetscObject  obj;
3514     PetscClassId id;
3515 
3516     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
3517     PetscCall(PetscObjectGetClassId(obj, &id));
3518     if (id == PETSCFE_CLASSID)      {isFE[f] = PETSC_TRUE;}
3519     else if (id == PETSCFV_CLASSID) {isFE[f] = PETSC_FALSE;}
3520     else                            {isFE[f] = PETSC_FALSE;}
3521   }
3522   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
3523   PetscCall(VecGetArrayRead(locX, &x));
3524   PetscCall(VecGetDM(faceGeometry, &dmFace));
3525   PetscCall(VecGetArrayRead(faceGeometry, &facegeom));
3526   PetscCall(VecGetDM(cellGeometry, &dmCell));
3527   PetscCall(VecGetArrayRead(cellGeometry, &cellgeom));
3528   if (locGrad) {
3529     PetscCall(VecGetDM(locGrad, &dmGrad));
3530     PetscCall(VecGetArrayRead(locGrad, &lgrad));
3531   }
3532   PetscCall(DMGetWorkArray(dm, numFaces*Nc, MPIU_SCALAR, uL));
3533   PetscCall(DMGetWorkArray(dm, numFaces*Nc, MPIU_SCALAR, uR));
3534   /* Right now just eat the extra work for FE (could make a cell loop) */
3535   for (face = fStart, iface = 0; face < fEnd; ++face) {
3536     const PetscInt        *cells;
3537     PetscFVFaceGeom       *fg;
3538     PetscFVCellGeom       *cgL, *cgR;
3539     PetscScalar           *xL, *xR, *gL, *gR;
3540     PetscScalar           *uLl = *uL, *uRl = *uR;
3541     PetscInt               ghost, nsupp, nchild;
3542 
3543     PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
3544     PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
3545     PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
3546     if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
3547     PetscCall(DMPlexPointLocalRead(dmFace, face, facegeom, &fg));
3548     PetscCall(DMPlexGetSupport(dm, face, &cells));
3549     PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL));
3550     PetscCall(DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR));
3551     for (f = 0; f < Nf; ++f) {
3552       PetscInt off;
3553 
3554       PetscCall(PetscDSGetComponentOffset(prob, f, &off));
3555       if (isFE[f]) {
3556         const PetscInt *cone;
3557         PetscInt        comp, coneSizeL, coneSizeR, faceLocL, faceLocR, ldof, rdof, d;
3558 
3559         xL = xR = NULL;
3560         PetscCall(PetscSectionGetFieldComponents(section, f, &comp));
3561         PetscCall(DMPlexVecGetClosure(dm, section, locX, cells[0], &ldof, (PetscScalar **) &xL));
3562         PetscCall(DMPlexVecGetClosure(dm, section, locX, cells[1], &rdof, (PetscScalar **) &xR));
3563         PetscCall(DMPlexGetCone(dm, cells[0], &cone));
3564         PetscCall(DMPlexGetConeSize(dm, cells[0], &coneSizeL));
3565         for (faceLocL = 0; faceLocL < coneSizeL; ++faceLocL) if (cone[faceLocL] == face) break;
3566         PetscCall(DMPlexGetCone(dm, cells[1], &cone));
3567         PetscCall(DMPlexGetConeSize(dm, cells[1], &coneSizeR));
3568         for (faceLocR = 0; faceLocR < coneSizeR; ++faceLocR) if (cone[faceLocR] == face) break;
3569         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]);
3570         /* Check that FEM field has values in the right cell (sometimes its an FV ghost cell) */
3571         /* TODO: this is a hack that might not be right for nonconforming */
3572         if (faceLocL < coneSizeL) {
3573           PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocL, xL, &uLl[iface*Nc+off]));
3574           if (rdof == ldof && faceLocR < coneSizeR) PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface*Nc+off]));
3575           else              {for (d = 0; d < comp; ++d) uRl[iface*Nc+off+d] = uLl[iface*Nc+off+d];}
3576         }
3577         else {
3578           PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface*Nc+off]));
3579           PetscCall(PetscSectionGetFieldComponents(section, f, &comp));
3580           for (d = 0; d < comp; ++d) uLl[iface*Nc+off+d] = uRl[iface*Nc+off+d];
3581         }
3582         PetscCall(DMPlexVecRestoreClosure(dm, section, locX, cells[0], &ldof, (PetscScalar **) &xL));
3583         PetscCall(DMPlexVecRestoreClosure(dm, section, locX, cells[1], &rdof, (PetscScalar **) &xR));
3584       } else {
3585         PetscFV  fv;
3586         PetscInt numComp, c;
3587 
3588         PetscCall(PetscDSGetDiscretization(prob, f, (PetscObject *) &fv));
3589         PetscCall(PetscFVGetNumComponents(fv, &numComp));
3590         PetscCall(DMPlexPointLocalFieldRead(dm, cells[0], f, x, &xL));
3591         PetscCall(DMPlexPointLocalFieldRead(dm, cells[1], f, x, &xR));
3592         if (dmGrad) {
3593           PetscReal dxL[3], dxR[3];
3594 
3595           PetscCall(DMPlexPointLocalRead(dmGrad, cells[0], lgrad, &gL));
3596           PetscCall(DMPlexPointLocalRead(dmGrad, cells[1], lgrad, &gR));
3597           DMPlex_WaxpyD_Internal(dim, -1, cgL->centroid, fg->centroid, dxL);
3598           DMPlex_WaxpyD_Internal(dim, -1, cgR->centroid, fg->centroid, dxR);
3599           for (c = 0; c < numComp; ++c) {
3600             uLl[iface*Nc+off+c] = xL[c] + DMPlex_DotD_Internal(dim, &gL[c*dim], dxL);
3601             uRl[iface*Nc+off+c] = xR[c] + DMPlex_DotD_Internal(dim, &gR[c*dim], dxR);
3602           }
3603         } else {
3604           for (c = 0; c < numComp; ++c) {
3605             uLl[iface*Nc+off+c] = xL[c];
3606             uRl[iface*Nc+off+c] = xR[c];
3607           }
3608         }
3609       }
3610     }
3611     ++iface;
3612   }
3613   *Nface = iface;
3614   PetscCall(VecRestoreArrayRead(locX, &x));
3615   PetscCall(VecRestoreArrayRead(faceGeometry, &facegeom));
3616   PetscCall(VecRestoreArrayRead(cellGeometry, &cellgeom));
3617   if (locGrad) {
3618     PetscCall(VecRestoreArrayRead(locGrad, &lgrad));
3619   }
3620   PetscCall(PetscFree(isFE));
3621   PetscFunctionReturn(0);
3622 }
3623 
3624 /*@C
3625   DMPlexRestoreFaceFields - Restore the field values values for a chunk of faces
3626 
3627   Input Parameters:
3628 + dm     - The DM
3629 . fStart - The first face to include
3630 . fEnd   - The first face to exclude
3631 . locX   - A local vector with the solution fields
3632 . locX_t - A local vector with solution field time derivatives, or NULL
3633 . faceGeometry - A local vector with face geometry
3634 . cellGeometry - A local vector with cell geometry
3635 - locaGrad - A local vector with field gradients, or NULL
3636 
3637   Output Parameters:
3638 + Nface - The number of faces with field values
3639 . uL - The field values at the left side of the face
3640 - uR - The field values at the right side of the face
3641 
3642   Level: developer
3643 
3644 .seealso: `DMPlexGetFaceFields()`
3645 @*/
3646 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)
3647 {
3648   PetscFunctionBegin;
3649   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uL));
3650   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uR));
3651   PetscFunctionReturn(0);
3652 }
3653 
3654 /*@C
3655   DMPlexGetFaceGeometry - Retrieve the geometric values for a chunk of faces
3656 
3657   Input Parameters:
3658 + dm     - The DM
3659 . fStart - The first face to include
3660 . fEnd   - The first face to exclude
3661 . faceGeometry - A local vector with face geometry
3662 - cellGeometry - A local vector with cell geometry
3663 
3664   Output Parameters:
3665 + Nface - The number of faces with field values
3666 . fgeom - The extract the face centroid and normal
3667 - vol   - The cell volume
3668 
3669   Level: developer
3670 
3671 .seealso: `DMPlexGetCellFields()`
3672 @*/
3673 PetscErrorCode DMPlexGetFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom **fgeom, PetscReal **vol)
3674 {
3675   DM                 dmFace, dmCell;
3676   DMLabel            ghostLabel;
3677   const PetscScalar *facegeom, *cellgeom;
3678   PetscInt           dim, numFaces = fEnd - fStart, iface, face;
3679 
3680   PetscFunctionBegin;
3681   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3682   PetscValidHeaderSpecific(faceGeometry, VEC_CLASSID, 4);
3683   PetscValidHeaderSpecific(cellGeometry, VEC_CLASSID, 5);
3684   PetscValidPointer(fgeom, 7);
3685   PetscValidPointer(vol, 8);
3686   PetscCall(DMGetDimension(dm, &dim));
3687   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
3688   PetscCall(VecGetDM(faceGeometry, &dmFace));
3689   PetscCall(VecGetArrayRead(faceGeometry, &facegeom));
3690   PetscCall(VecGetDM(cellGeometry, &dmCell));
3691   PetscCall(VecGetArrayRead(cellGeometry, &cellgeom));
3692   PetscCall(PetscMalloc1(numFaces, fgeom));
3693   PetscCall(DMGetWorkArray(dm, numFaces*2, MPIU_SCALAR, vol));
3694   for (face = fStart, iface = 0; face < fEnd; ++face) {
3695     const PetscInt        *cells;
3696     PetscFVFaceGeom       *fg;
3697     PetscFVCellGeom       *cgL, *cgR;
3698     PetscFVFaceGeom       *fgeoml = *fgeom;
3699     PetscReal             *voll   = *vol;
3700     PetscInt               ghost, d, nchild, nsupp;
3701 
3702     PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
3703     PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
3704     PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
3705     if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
3706     PetscCall(DMPlexPointLocalRead(dmFace, face, facegeom, &fg));
3707     PetscCall(DMPlexGetSupport(dm, face, &cells));
3708     PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL));
3709     PetscCall(DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR));
3710     for (d = 0; d < dim; ++d) {
3711       fgeoml[iface].centroid[d] = fg->centroid[d];
3712       fgeoml[iface].normal[d]   = fg->normal[d];
3713     }
3714     voll[iface*2+0] = cgL->volume;
3715     voll[iface*2+1] = cgR->volume;
3716     ++iface;
3717   }
3718   *Nface = iface;
3719   PetscCall(VecRestoreArrayRead(faceGeometry, &facegeom));
3720   PetscCall(VecRestoreArrayRead(cellGeometry, &cellgeom));
3721   PetscFunctionReturn(0);
3722 }
3723 
3724 /*@C
3725   DMPlexRestoreFaceGeometry - Restore the field values values for a chunk of faces
3726 
3727   Input Parameters:
3728 + dm     - The DM
3729 . fStart - The first face to include
3730 . fEnd   - The first face to exclude
3731 . faceGeometry - A local vector with face geometry
3732 - cellGeometry - A local vector with cell geometry
3733 
3734   Output Parameters:
3735 + Nface - The number of faces with field values
3736 . fgeom - The extract the face centroid and normal
3737 - vol   - The cell volume
3738 
3739   Level: developer
3740 
3741 .seealso: `DMPlexGetFaceFields()`
3742 @*/
3743 PetscErrorCode DMPlexRestoreFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom **fgeom, PetscReal **vol)
3744 {
3745   PetscFunctionBegin;
3746   PetscCall(PetscFree(*fgeom));
3747   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_REAL, vol));
3748   PetscFunctionReturn(0);
3749 }
3750 
3751 PetscErrorCode DMSNESGetFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
3752 {
3753   char            composeStr[33] = {0};
3754   PetscObjectId   id;
3755   PetscContainer  container;
3756 
3757   PetscFunctionBegin;
3758   PetscCall(PetscObjectGetId((PetscObject)quad,&id));
3759   PetscCall(PetscSNPrintf(composeStr, 32, "DMSNESGetFEGeom_%" PetscInt64_FMT "\n", id));
3760   PetscCall(PetscObjectQuery((PetscObject) pointIS, composeStr, (PetscObject *) &container));
3761   if (container) {
3762     PetscCall(PetscContainerGetPointer(container, (void **) geom));
3763   } else {
3764     PetscCall(DMFieldCreateFEGeom(coordField, pointIS, quad, faceData, geom));
3765     PetscCall(PetscContainerCreate(PETSC_COMM_SELF,&container));
3766     PetscCall(PetscContainerSetPointer(container, (void *) *geom));
3767     PetscCall(PetscContainerSetUserDestroy(container, PetscContainerUserDestroy_PetscFEGeom));
3768     PetscCall(PetscObjectCompose((PetscObject) pointIS, composeStr, (PetscObject) container));
3769     PetscCall(PetscContainerDestroy(&container));
3770   }
3771   PetscFunctionReturn(0);
3772 }
3773 
3774 PetscErrorCode DMSNESRestoreFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
3775 {
3776   PetscFunctionBegin;
3777   *geom = NULL;
3778   PetscFunctionReturn(0);
3779 }
3780 
3781 PetscErrorCode DMPlexComputeResidual_Patch_Internal(DM dm, PetscSection section, IS cellIS, PetscReal t, Vec locX, Vec locX_t, Vec locF, void *user)
3782 {
3783   DM_Plex         *mesh       = (DM_Plex *) dm->data;
3784   const char      *name       = "Residual";
3785   DM               dmAux      = NULL;
3786   DMLabel          ghostLabel = NULL;
3787   PetscDS          prob       = NULL;
3788   PetscDS          probAux    = NULL;
3789   PetscBool        useFEM     = PETSC_FALSE;
3790   PetscBool        isImplicit = (locX_t || t == PETSC_MIN_REAL) ? PETSC_TRUE : PETSC_FALSE;
3791   DMField          coordField = NULL;
3792   Vec              locA;
3793   PetscScalar     *u = NULL, *u_t, *a, *uL = NULL, *uR = NULL;
3794   IS               chunkIS;
3795   const PetscInt  *cells;
3796   PetscInt         cStart, cEnd, numCells;
3797   PetscInt         Nf, f, totDim, totDimAux, numChunks, cellChunkSize, chunk, fStart, fEnd;
3798   PetscInt         maxDegree = PETSC_MAX_INT;
3799   PetscFormKey key;
3800   PetscQuadrature  affineQuad = NULL, *quads = NULL;
3801   PetscFEGeom     *affineGeom = NULL, **geoms = NULL;
3802 
3803   PetscFunctionBegin;
3804   PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM,dm,0,0,0));
3805   /* FEM+FVM */
3806   /* 1: Get sizes from dm and dmAux */
3807   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
3808   PetscCall(DMGetDS(dm, &prob));
3809   PetscCall(PetscDSGetNumFields(prob, &Nf));
3810   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
3811   PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA));
3812   if (locA) {
3813     PetscCall(VecGetDM(locA, &dmAux));
3814     PetscCall(DMGetDS(dmAux, &probAux));
3815     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
3816   }
3817   /* 2: Get geometric data */
3818   for (f = 0; f < Nf; ++f) {
3819     PetscObject  obj;
3820     PetscClassId id;
3821     PetscBool    fimp;
3822 
3823     PetscCall(PetscDSGetImplicit(prob, f, &fimp));
3824     if (isImplicit != fimp) continue;
3825     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
3826     PetscCall(PetscObjectGetClassId(obj, &id));
3827     if (id == PETSCFE_CLASSID) {useFEM = PETSC_TRUE;}
3828     PetscCheck(id != PETSCFV_CLASSID,PETSC_COMM_SELF,PETSC_ERR_ARG_WRONG,"Use of FVM with PCPATCH not yet implemented");
3829   }
3830   if (useFEM) {
3831     PetscCall(DMGetCoordinateField(dm, &coordField));
3832     PetscCall(DMFieldGetDegree(coordField,cellIS,NULL,&maxDegree));
3833     if (maxDegree <= 1) {
3834       PetscCall(DMFieldCreateDefaultQuadrature(coordField,cellIS,&affineQuad));
3835       if (affineQuad) {
3836         PetscCall(DMSNESGetFEGeom(coordField,cellIS,affineQuad,PETSC_FALSE,&affineGeom));
3837       }
3838     } else {
3839       PetscCall(PetscCalloc2(Nf,&quads,Nf,&geoms));
3840       for (f = 0; f < Nf; ++f) {
3841         PetscObject  obj;
3842         PetscClassId id;
3843         PetscBool    fimp;
3844 
3845         PetscCall(PetscDSGetImplicit(prob, f, &fimp));
3846         if (isImplicit != fimp) continue;
3847         PetscCall(PetscDSGetDiscretization(prob, f, &obj));
3848         PetscCall(PetscObjectGetClassId(obj, &id));
3849         if (id == PETSCFE_CLASSID) {
3850           PetscFE fe = (PetscFE) obj;
3851 
3852           PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
3853           PetscCall(PetscObjectReference((PetscObject)quads[f]));
3854           PetscCall(DMSNESGetFEGeom(coordField,cellIS,quads[f],PETSC_FALSE,&geoms[f]));
3855         }
3856       }
3857     }
3858   }
3859   /* Loop over chunks */
3860   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
3861   PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
3862   if (useFEM) PetscCall(ISCreate(PETSC_COMM_SELF, &chunkIS));
3863   numCells      = cEnd - cStart;
3864   numChunks     = 1;
3865   cellChunkSize = numCells/numChunks;
3866   numChunks     = PetscMin(1,numCells);
3867   key.label     = NULL;
3868   key.value     = 0;
3869   key.part      = 0;
3870   for (chunk = 0; chunk < numChunks; ++chunk) {
3871     PetscScalar     *elemVec, *fluxL = NULL, *fluxR = NULL;
3872     PetscReal       *vol = NULL;
3873     PetscFVFaceGeom *fgeom = NULL;
3874     PetscInt         cS = cStart+chunk*cellChunkSize, cE = PetscMin(cS+cellChunkSize, cEnd), numCells = cE - cS, c;
3875     PetscInt         numFaces = 0;
3876 
3877     /* Extract field coefficients */
3878     if (useFEM) {
3879       PetscCall(ISGetPointSubrange(chunkIS, cS, cE, cells));
3880       PetscCall(DMPlexGetCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
3881       PetscCall(DMGetWorkArray(dm, numCells*totDim, MPIU_SCALAR, &elemVec));
3882       PetscCall(PetscArrayzero(elemVec, numCells*totDim));
3883     }
3884     /* TODO We will interlace both our field coefficients (u, u_t, uL, uR, etc.) and our output (elemVec, fL, fR). I think this works */
3885     /* Loop over fields */
3886     for (f = 0; f < Nf; ++f) {
3887       PetscObject  obj;
3888       PetscClassId id;
3889       PetscBool    fimp;
3890       PetscInt     numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;
3891 
3892       key.field = f;
3893       PetscCall(PetscDSGetImplicit(prob, f, &fimp));
3894       if (isImplicit != fimp) continue;
3895       PetscCall(PetscDSGetDiscretization(prob, f, &obj));
3896       PetscCall(PetscObjectGetClassId(obj, &id));
3897       if (id == PETSCFE_CLASSID) {
3898         PetscFE         fe = (PetscFE) obj;
3899         PetscFEGeom    *geom = affineGeom ? affineGeom : geoms[f];
3900         PetscFEGeom    *chunkGeom = NULL;
3901         PetscQuadrature quad = affineQuad ? affineQuad : quads[f];
3902         PetscInt        Nq, Nb;
3903 
3904         PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
3905         PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
3906         PetscCall(PetscFEGetDimension(fe, &Nb));
3907         blockSize = Nb;
3908         batchSize = numBlocks * blockSize;
3909         PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
3910         numChunks = numCells / (numBatches*batchSize);
3911         Ne        = numChunks*numBatches*batchSize;
3912         Nr        = numCells % (numBatches*batchSize);
3913         offset    = numCells - Nr;
3914         /* Integrate FE residual to get elemVec (need fields at quadrature points) */
3915         /*   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) */
3916         PetscCall(PetscFEGeomGetChunk(geom,0,offset,&chunkGeom));
3917         PetscCall(PetscFEIntegrateResidual(prob, key, Ne, chunkGeom, u, u_t, probAux, a, t, elemVec));
3918         PetscCall(PetscFEGeomGetChunk(geom,offset,numCells,&chunkGeom));
3919         PetscCall(PetscFEIntegrateResidual(prob, key, Nr, chunkGeom, &u[offset*totDim], u_t ? &u_t[offset*totDim] : NULL, probAux, &a[offset*totDimAux], t, &elemVec[offset*totDim]));
3920         PetscCall(PetscFEGeomRestoreChunk(geom,offset,numCells,&chunkGeom));
3921       } else if (id == PETSCFV_CLASSID) {
3922         PetscFV fv = (PetscFV) obj;
3923 
3924         Ne = numFaces;
3925         /* Riemann solve over faces (need fields at face centroids) */
3926         /*   We need to evaluate FE fields at those coordinates */
3927         PetscCall(PetscFVIntegrateRHSFunction(fv, prob, f, Ne, fgeom, vol, uL, uR, fluxL, fluxR));
3928       } else SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
3929     }
3930     /* Loop over domain */
3931     if (useFEM) {
3932       /* Add elemVec to locX */
3933       for (c = cS; c < cE; ++c) {
3934         const PetscInt cell = cells ? cells[c] : c;
3935         const PetscInt cind = c - cStart;
3936 
3937         if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, name, totDim, &elemVec[cind*totDim]));
3938         if (ghostLabel) {
3939           PetscInt ghostVal;
3940 
3941           PetscCall(DMLabelGetValue(ghostLabel,cell,&ghostVal));
3942           if (ghostVal > 0) continue;
3943         }
3944         PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind*totDim], ADD_ALL_VALUES));
3945       }
3946     }
3947     /* Handle time derivative */
3948     if (locX_t) {
3949       PetscScalar *x_t, *fa;
3950 
3951       PetscCall(VecGetArray(locF, &fa));
3952       PetscCall(VecGetArray(locX_t, &x_t));
3953       for (f = 0; f < Nf; ++f) {
3954         PetscFV      fv;
3955         PetscObject  obj;
3956         PetscClassId id;
3957         PetscInt     pdim, d;
3958 
3959         PetscCall(PetscDSGetDiscretization(prob, f, &obj));
3960         PetscCall(PetscObjectGetClassId(obj, &id));
3961         if (id != PETSCFV_CLASSID) continue;
3962         fv   = (PetscFV) obj;
3963         PetscCall(PetscFVGetNumComponents(fv, &pdim));
3964         for (c = cS; c < cE; ++c) {
3965           const PetscInt cell = cells ? cells[c] : c;
3966           PetscScalar   *u_t, *r;
3967 
3968           if (ghostLabel) {
3969             PetscInt ghostVal;
3970 
3971             PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
3972             if (ghostVal > 0) continue;
3973           }
3974           PetscCall(DMPlexPointLocalFieldRead(dm, cell, f, x_t, &u_t));
3975           PetscCall(DMPlexPointLocalFieldRef(dm, cell, f, fa, &r));
3976           for (d = 0; d < pdim; ++d) r[d] += u_t[d];
3977         }
3978       }
3979       PetscCall(VecRestoreArray(locX_t, &x_t));
3980       PetscCall(VecRestoreArray(locF, &fa));
3981     }
3982     if (useFEM) {
3983       PetscCall(DMPlexRestoreCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
3984       PetscCall(DMRestoreWorkArray(dm, numCells*totDim, MPIU_SCALAR, &elemVec));
3985     }
3986   }
3987   if (useFEM) PetscCall(ISDestroy(&chunkIS));
3988   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
3989   /* TODO Could include boundary residual here (see DMPlexComputeResidual_Internal) */
3990   if (useFEM) {
3991     if (maxDegree <= 1) {
3992       PetscCall(DMSNESRestoreFEGeom(coordField,cellIS,affineQuad,PETSC_FALSE,&affineGeom));
3993       PetscCall(PetscQuadratureDestroy(&affineQuad));
3994     } else {
3995       for (f = 0; f < Nf; ++f) {
3996         PetscCall(DMSNESRestoreFEGeom(coordField,cellIS,quads[f],PETSC_FALSE,&geoms[f]));
3997         PetscCall(PetscQuadratureDestroy(&quads[f]));
3998       }
3999       PetscCall(PetscFree2(quads,geoms));
4000     }
4001   }
4002   PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM,dm,0,0,0));
4003   PetscFunctionReturn(0);
4004 }
4005 
4006 /*
4007   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
4008 
4009   X   - The local solution vector
4010   X_t - The local solution time derivative vector, or NULL
4011 */
4012 PetscErrorCode DMPlexComputeJacobian_Patch_Internal(DM dm, PetscSection section, PetscSection globalSection, IS cellIS,
4013                                                     PetscReal t, PetscReal X_tShift, Vec X, Vec X_t, Mat Jac, Mat JacP, void *ctx)
4014 {
4015   DM_Plex         *mesh  = (DM_Plex *) dm->data;
4016   const char      *name = "Jacobian", *nameP = "JacobianPre";
4017   DM               dmAux = NULL;
4018   PetscDS          prob,   probAux = NULL;
4019   PetscSection     sectionAux = NULL;
4020   Vec              A;
4021   DMField          coordField;
4022   PetscFEGeom     *cgeomFEM;
4023   PetscQuadrature  qGeom = NULL;
4024   Mat              J = Jac, JP = JacP;
4025   PetscScalar     *work, *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL, *elemMatP = NULL, *elemMatD = NULL;
4026   PetscBool        hasJac, hasPrec, hasDyn, assembleJac, *isFE, hasFV = PETSC_FALSE;
4027   const PetscInt  *cells;
4028   PetscFormKey key;
4029   PetscInt         Nf, fieldI, fieldJ, maxDegree, numCells, cStart, cEnd, numChunks, chunkSize, chunk, totDim, totDimAux = 0, sz, wsz, off = 0, offCell = 0;
4030 
4031   PetscFunctionBegin;
4032   PetscCall(ISGetLocalSize(cellIS, &numCells));
4033   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
4034   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM,dm,0,0,0));
4035   PetscCall(DMGetDS(dm, &prob));
4036   PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &A));
4037   if (A) {
4038     PetscCall(VecGetDM(A, &dmAux));
4039     PetscCall(DMGetLocalSection(dmAux, &sectionAux));
4040     PetscCall(DMGetDS(dmAux, &probAux));
4041   }
4042   /* Get flags */
4043   PetscCall(PetscDSGetNumFields(prob, &Nf));
4044   PetscCall(DMGetWorkArray(dm, Nf, MPIU_BOOL, &isFE));
4045   for (fieldI = 0; fieldI < Nf; ++fieldI) {
4046     PetscObject  disc;
4047     PetscClassId id;
4048     PetscCall(PetscDSGetDiscretization(prob, fieldI, &disc));
4049     PetscCall(PetscObjectGetClassId(disc, &id));
4050     if (id == PETSCFE_CLASSID)      {isFE[fieldI] = PETSC_TRUE;}
4051     else if (id == PETSCFV_CLASSID) {hasFV = PETSC_TRUE; isFE[fieldI] = PETSC_FALSE;}
4052   }
4053   PetscCall(PetscDSHasJacobian(prob, &hasJac));
4054   PetscCall(PetscDSHasJacobianPreconditioner(prob, &hasPrec));
4055   PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
4056   assembleJac = hasJac && hasPrec && (Jac != JacP) ? PETSC_TRUE : PETSC_FALSE;
4057   hasDyn      = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
4058   if (hasFV) PetscCall(MatSetOption(JP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE)); /* No allocated space for FV stuff, so ignore the zero entries */
4059   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
4060   if (probAux) PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
4061   /* Compute batch sizes */
4062   if (isFE[0]) {
4063     PetscFE         fe;
4064     PetscQuadrature q;
4065     PetscInt        numQuadPoints, numBatches, batchSize, numBlocks, blockSize, Nb;
4066 
4067     PetscCall(PetscDSGetDiscretization(prob, 0, (PetscObject *) &fe));
4068     PetscCall(PetscFEGetQuadrature(fe, &q));
4069     PetscCall(PetscQuadratureGetData(q, NULL, NULL, &numQuadPoints, NULL, NULL));
4070     PetscCall(PetscFEGetDimension(fe, &Nb));
4071     PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
4072     blockSize = Nb*numQuadPoints;
4073     batchSize = numBlocks  * blockSize;
4074     chunkSize = numBatches * batchSize;
4075     numChunks = numCells / chunkSize + numCells % chunkSize;
4076     PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
4077   } else {
4078     chunkSize = numCells;
4079     numChunks = 1;
4080   }
4081   /* Get work space */
4082   wsz  = (((X?1:0) + (X_t?1:0) + (dmAux?1:0))*totDim + ((hasJac?1:0) + (hasPrec?1:0) + (hasDyn?1:0))*totDim*totDim)*chunkSize;
4083   PetscCall(DMGetWorkArray(dm, wsz, MPIU_SCALAR, &work));
4084   PetscCall(PetscArrayzero(work, wsz));
4085   off      = 0;
4086   u        = X       ? (sz = chunkSize*totDim,        off += sz, work+off-sz) : NULL;
4087   u_t      = X_t     ? (sz = chunkSize*totDim,        off += sz, work+off-sz) : NULL;
4088   a        = dmAux   ? (sz = chunkSize*totDimAux,     off += sz, work+off-sz) : NULL;
4089   elemMat  = hasJac  ? (sz = chunkSize*totDim*totDim, off += sz, work+off-sz) : NULL;
4090   elemMatP = hasPrec ? (sz = chunkSize*totDim*totDim, off += sz, work+off-sz) : NULL;
4091   elemMatD = hasDyn  ? (sz = chunkSize*totDim*totDim, off += sz, work+off-sz) : NULL;
4092   PetscCheck(off == wsz,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Error is workspace size %" PetscInt_FMT " should be %" PetscInt_FMT, off, wsz);
4093   /* Setup geometry */
4094   PetscCall(DMGetCoordinateField(dm, &coordField));
4095   PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
4096   if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
4097   if (!qGeom) {
4098     PetscFE fe;
4099 
4100     PetscCall(PetscDSGetDiscretization(prob, 0, (PetscObject *) &fe));
4101     PetscCall(PetscFEGetQuadrature(fe, &qGeom));
4102     PetscCall(PetscObjectReference((PetscObject) qGeom));
4103   }
4104   PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
4105   /* Compute volume integrals */
4106   if (assembleJac) PetscCall(MatZeroEntries(J));
4107   PetscCall(MatZeroEntries(JP));
4108   key.label = NULL;
4109   key.value = 0;
4110   key.part  = 0;
4111   for (chunk = 0; chunk < numChunks; ++chunk, offCell += chunkSize) {
4112     const PetscInt   Ncell = PetscMin(chunkSize, numCells - offCell);
4113     PetscInt         c;
4114 
4115     /* Extract values */
4116     for (c = 0; c < Ncell; ++c) {
4117       const PetscInt cell = cells ? cells[c+offCell] : c+offCell;
4118       PetscScalar   *x = NULL,  *x_t = NULL;
4119       PetscInt       i;
4120 
4121       if (X) {
4122         PetscCall(DMPlexVecGetClosure(dm, section, X, cell, NULL, &x));
4123         for (i = 0; i < totDim; ++i) u[c*totDim+i] = x[i];
4124         PetscCall(DMPlexVecRestoreClosure(dm, section, X, cell, NULL, &x));
4125       }
4126       if (X_t) {
4127         PetscCall(DMPlexVecGetClosure(dm, section, X_t, cell, NULL, &x_t));
4128         for (i = 0; i < totDim; ++i) u_t[c*totDim+i] = x_t[i];
4129         PetscCall(DMPlexVecRestoreClosure(dm, section, X_t, cell, NULL, &x_t));
4130       }
4131       if (dmAux) {
4132         PetscCall(DMPlexVecGetClosure(dmAux, sectionAux, A, cell, NULL, &x));
4133         for (i = 0; i < totDimAux; ++i) a[c*totDimAux+i] = x[i];
4134         PetscCall(DMPlexVecRestoreClosure(dmAux, sectionAux, A, cell, NULL, &x));
4135       }
4136     }
4137     for (fieldI = 0; fieldI < Nf; ++fieldI) {
4138       PetscFE fe;
4139       PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *) &fe));
4140       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
4141         key.field = fieldI*Nf + fieldJ;
4142         if (hasJac)  PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN,     key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMat));
4143         if (hasPrec) PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatP));
4144         if (hasDyn)  PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatD));
4145       }
4146       /* For finite volume, add the identity */
4147       if (!isFE[fieldI]) {
4148         PetscFV  fv;
4149         PetscInt eOffset = 0, Nc, fc, foff;
4150 
4151         PetscCall(PetscDSGetFieldOffset(prob, fieldI, &foff));
4152         PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *) &fv));
4153         PetscCall(PetscFVGetNumComponents(fv, &Nc));
4154         for (c = 0; c < chunkSize; ++c, eOffset += totDim*totDim) {
4155           for (fc = 0; fc < Nc; ++fc) {
4156             const PetscInt i = foff + fc;
4157             if (hasJac)  {elemMat [eOffset+i*totDim+i] = 1.0;}
4158             if (hasPrec) {elemMatP[eOffset+i*totDim+i] = 1.0;}
4159           }
4160         }
4161       }
4162     }
4163     /*   Add contribution from X_t */
4164     if (hasDyn) {for (c = 0; c < chunkSize*totDim*totDim; ++c) elemMat[c] += X_tShift*elemMatD[c];}
4165     /* Insert values into matrix */
4166     for (c = 0; c < Ncell; ++c) {
4167       const PetscInt cell = cells ? cells[c+offCell] : c+offCell;
4168       if (mesh->printFEM > 1) {
4169         if (hasJac)  PetscCall(DMPrintCellMatrix(cell, name,  totDim, totDim, &elemMat[(c-cStart)*totDim*totDim]));
4170         if (hasPrec) PetscCall(DMPrintCellMatrix(cell, nameP, totDim, totDim, &elemMatP[(c-cStart)*totDim*totDim]));
4171       }
4172       if (assembleJac) PetscCall(DMPlexMatSetClosure(dm, section, globalSection, Jac, cell, &elemMat[(c-cStart)*totDim*totDim], ADD_VALUES));
4173       PetscCall(DMPlexMatSetClosure(dm, section, globalSection, JP, cell, &elemMat[(c-cStart)*totDim*totDim], ADD_VALUES));
4174     }
4175   }
4176   /* Cleanup */
4177   PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
4178   PetscCall(PetscQuadratureDestroy(&qGeom));
4179   if (hasFV) PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE));
4180   PetscCall(DMRestoreWorkArray(dm, Nf, MPIU_BOOL, &isFE));
4181   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));
4182   /* Compute boundary integrals */
4183   /* PetscCall(DMPlexComputeBdJacobian_Internal(dm, X, X_t, t, X_tShift, Jac, JacP, ctx)); */
4184   /* Assemble matrix */
4185   if (assembleJac) {PetscCall(MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY));PetscCall(MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY));}
4186   PetscCall(MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY));PetscCall(MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY));
4187   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM,dm,0,0,0));
4188   PetscFunctionReturn(0);
4189 }
4190 
4191 /******** FEM Assembly Function ********/
4192 
4193 static PetscErrorCode DMConvertPlex_Internal(DM dm, DM *plex, PetscBool copy)
4194 {
4195   PetscBool      isPlex;
4196 
4197   PetscFunctionBegin;
4198   PetscCall(PetscObjectTypeCompare((PetscObject) dm, DMPLEX, &isPlex));
4199   if (isPlex) {
4200     *plex = dm;
4201     PetscCall(PetscObjectReference((PetscObject) dm));
4202   } else {
4203     PetscCall(PetscObjectQuery((PetscObject) dm, "dm_plex", (PetscObject *) plex));
4204     if (!*plex) {
4205       PetscCall(DMConvert(dm,DMPLEX,plex));
4206       PetscCall(PetscObjectCompose((PetscObject) dm, "dm_plex", (PetscObject) *plex));
4207       if (copy) {
4208         PetscCall(DMCopyAuxiliaryVec(dm, *plex));
4209       }
4210     } else {
4211       PetscCall(PetscObjectReference((PetscObject) *plex));
4212     }
4213   }
4214   PetscFunctionReturn(0);
4215 }
4216 
4217 /*@
4218   DMPlexGetGeometryFVM - Return precomputed geometric data
4219 
4220   Collective on DM
4221 
4222   Input Parameter:
4223 . dm - The DM
4224 
4225   Output Parameters:
4226 + facegeom - The values precomputed from face geometry
4227 . cellgeom - The values precomputed from cell geometry
4228 - minRadius - The minimum radius over the mesh of an inscribed sphere in a cell
4229 
4230   Level: developer
4231 
4232 .seealso: `DMTSSetRHSFunctionLocal()`
4233 @*/
4234 PetscErrorCode DMPlexGetGeometryFVM(DM dm, Vec *facegeom, Vec *cellgeom, PetscReal *minRadius)
4235 {
4236   DM             plex;
4237 
4238   PetscFunctionBegin;
4239   PetscValidHeaderSpecific(dm,DM_CLASSID,1);
4240   PetscCall(DMConvertPlex_Internal(dm,&plex,PETSC_TRUE));
4241   PetscCall(DMPlexGetDataFVM(plex, NULL, cellgeom, facegeom, NULL));
4242   if (minRadius) PetscCall(DMPlexGetMinRadius(plex, minRadius));
4243   PetscCall(DMDestroy(&plex));
4244   PetscFunctionReturn(0);
4245 }
4246 
4247 /*@
4248   DMPlexGetGradientDM - Return gradient data layout
4249 
4250   Collective on DM
4251 
4252   Input Parameters:
4253 + dm - The DM
4254 - fv - The PetscFV
4255 
4256   Output Parameter:
4257 . dmGrad - The layout for gradient values
4258 
4259   Level: developer
4260 
4261 .seealso: `DMPlexGetGeometryFVM()`
4262 @*/
4263 PetscErrorCode DMPlexGetGradientDM(DM dm, PetscFV fv, DM *dmGrad)
4264 {
4265   DM             plex;
4266   PetscBool      computeGradients;
4267 
4268   PetscFunctionBegin;
4269   PetscValidHeaderSpecific(dm,DM_CLASSID,1);
4270   PetscValidHeaderSpecific(fv,PETSCFV_CLASSID,2);
4271   PetscValidPointer(dmGrad,3);
4272   PetscCall(PetscFVGetComputeGradients(fv, &computeGradients));
4273   if (!computeGradients) {*dmGrad = NULL; PetscFunctionReturn(0);}
4274   PetscCall(DMConvertPlex_Internal(dm,&plex,PETSC_TRUE));
4275   PetscCall(DMPlexGetDataFVM(plex, fv, NULL, NULL, dmGrad));
4276   PetscCall(DMDestroy(&plex));
4277   PetscFunctionReturn(0);
4278 }
4279 
4280 static PetscErrorCode DMPlexComputeBdResidual_Single_Internal(DM dm, PetscReal t, PetscWeakForm wf, PetscFormKey key, Vec locX, Vec locX_t, Vec locF, DMField coordField, IS facetIS)
4281 {
4282   DM_Plex         *mesh = (DM_Plex *) dm->data;
4283   DM               plex = NULL, plexA = NULL;
4284   DMEnclosureType  encAux;
4285   PetscDS          prob, probAux = NULL;
4286   PetscSection     section, sectionAux = NULL;
4287   Vec              locA = NULL;
4288   PetscScalar     *u = NULL, *u_t = NULL, *a = NULL, *elemVec = NULL;
4289   PetscInt         totDim, totDimAux = 0;
4290 
4291   PetscFunctionBegin;
4292   PetscCall(DMConvert(dm, DMPLEX, &plex));
4293   PetscCall(DMGetLocalSection(dm, &section));
4294   PetscCall(DMGetDS(dm, &prob));
4295   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
4296   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &locA));
4297   if (locA) {
4298     DM dmAux;
4299 
4300     PetscCall(VecGetDM(locA, &dmAux));
4301     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
4302     PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
4303     PetscCall(DMGetDS(plexA, &probAux));
4304     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
4305     PetscCall(DMGetLocalSection(plexA, &sectionAux));
4306   }
4307   {
4308     PetscFEGeom     *fgeom;
4309     PetscInt         maxDegree;
4310     PetscQuadrature  qGeom = NULL;
4311     IS               pointIS;
4312     const PetscInt  *points;
4313     PetscInt         numFaces, face, Nq;
4314 
4315     PetscCall(DMLabelGetStratumIS(key.label, key.value, &pointIS));
4316     if (!pointIS) goto end; /* No points with that id on this process */
4317     {
4318       IS isectIS;
4319 
4320       /* TODO: Special cases of ISIntersect where it is quick to check a priori if one is a superset of the other */
4321       PetscCall(ISIntersect_Caching_Internal(facetIS,pointIS,&isectIS));
4322       PetscCall(ISDestroy(&pointIS));
4323       pointIS = isectIS;
4324     }
4325     PetscCall(ISGetLocalSize(pointIS,&numFaces));
4326     PetscCall(ISGetIndices(pointIS,&points));
4327     PetscCall(PetscMalloc4(numFaces*totDim, &u, locX_t ? numFaces*totDim : 0, &u_t, numFaces*totDim, &elemVec, locA ? numFaces*totDimAux : 0, &a));
4328     PetscCall(DMFieldGetDegree(coordField,pointIS,NULL,&maxDegree));
4329     if (maxDegree <= 1) {
4330       PetscCall(DMFieldCreateDefaultQuadrature(coordField,pointIS,&qGeom));
4331     }
4332     if (!qGeom) {
4333       PetscFE fe;
4334 
4335       PetscCall(PetscDSGetDiscretization(prob, key.field, (PetscObject *) &fe));
4336       PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
4337       PetscCall(PetscObjectReference((PetscObject)qGeom));
4338     }
4339     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
4340     PetscCall(DMSNESGetFEGeom(coordField,pointIS,qGeom,PETSC_TRUE,&fgeom));
4341     for (face = 0; face < numFaces; ++face) {
4342       const PetscInt point = points[face], *support;
4343       PetscScalar   *x     = NULL;
4344       PetscInt       i;
4345 
4346       PetscCall(DMPlexGetSupport(dm, point, &support));
4347       PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
4348       for (i = 0; i < totDim; ++i) u[face*totDim+i] = x[i];
4349       PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
4350       if (locX_t) {
4351         PetscCall(DMPlexVecGetClosure(plex, section, locX_t, support[0], NULL, &x));
4352         for (i = 0; i < totDim; ++i) u_t[face*totDim+i] = x[i];
4353         PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, support[0], NULL, &x));
4354       }
4355       if (locA) {
4356         PetscInt subp;
4357 
4358         PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
4359         PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
4360         for (i = 0; i < totDimAux; ++i) a[face*totDimAux+i] = x[i];
4361         PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
4362       }
4363     }
4364     PetscCall(PetscArrayzero(elemVec, numFaces*totDim));
4365     {
4366       PetscFE         fe;
4367       PetscInt        Nb;
4368       PetscFEGeom     *chunkGeom = NULL;
4369       /* Conforming batches */
4370       PetscInt        numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
4371       /* Remainder */
4372       PetscInt        Nr, offset;
4373 
4374       PetscCall(PetscDSGetDiscretization(prob, key.field, (PetscObject *) &fe));
4375       PetscCall(PetscFEGetDimension(fe, &Nb));
4376       PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
4377       /* TODO: documentation is unclear about what is going on with these numbers: how should Nb / Nq factor in ? */
4378       blockSize = Nb;
4379       batchSize = numBlocks * blockSize;
4380       PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
4381       numChunks = numFaces / (numBatches*batchSize);
4382       Ne        = numChunks*numBatches*batchSize;
4383       Nr        = numFaces % (numBatches*batchSize);
4384       offset    = numFaces - Nr;
4385       PetscCall(PetscFEGeomGetChunk(fgeom,0,offset,&chunkGeom));
4386       PetscCall(PetscFEIntegrateBdResidual(prob, wf, key, Ne, chunkGeom, u, u_t, probAux, a, t, elemVec));
4387       PetscCall(PetscFEGeomRestoreChunk(fgeom, 0, offset, &chunkGeom));
4388       PetscCall(PetscFEGeomGetChunk(fgeom,offset,numFaces,&chunkGeom));
4389       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]));
4390       PetscCall(PetscFEGeomRestoreChunk(fgeom,offset,numFaces,&chunkGeom));
4391     }
4392     for (face = 0; face < numFaces; ++face) {
4393       const PetscInt point = points[face], *support;
4394 
4395       if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(point, "BdResidual", totDim, &elemVec[face*totDim]));
4396       PetscCall(DMPlexGetSupport(plex, point, &support));
4397       PetscCall(DMPlexVecSetClosure(plex, NULL, locF, support[0], &elemVec[face*totDim], ADD_ALL_VALUES));
4398     }
4399     PetscCall(DMSNESRestoreFEGeom(coordField,pointIS,qGeom,PETSC_TRUE,&fgeom));
4400     PetscCall(PetscQuadratureDestroy(&qGeom));
4401     PetscCall(ISRestoreIndices(pointIS, &points));
4402     PetscCall(ISDestroy(&pointIS));
4403     PetscCall(PetscFree4(u, u_t, elemVec, a));
4404   }
4405   end:
4406   PetscCall(DMDestroy(&plex));
4407   PetscCall(DMDestroy(&plexA));
4408   PetscFunctionReturn(0);
4409 }
4410 
4411 PetscErrorCode DMPlexComputeBdResidualSingle(DM dm, PetscReal t, PetscWeakForm wf, PetscFormKey key, Vec locX, Vec locX_t, Vec locF)
4412 {
4413   DMField        coordField;
4414   DMLabel        depthLabel;
4415   IS             facetIS;
4416   PetscInt       dim;
4417 
4418   PetscFunctionBegin;
4419   PetscCall(DMGetDimension(dm, &dim));
4420   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
4421   PetscCall(DMLabelGetStratumIS(depthLabel, dim-1, &facetIS));
4422   PetscCall(DMGetCoordinateField(dm, &coordField));
4423   PetscCall(DMPlexComputeBdResidual_Single_Internal(dm, t, wf, key, locX, locX_t, locF, coordField, facetIS));
4424   PetscCall(ISDestroy(&facetIS));
4425   PetscFunctionReturn(0);
4426 }
4427 
4428 PetscErrorCode DMPlexComputeBdResidual_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
4429 {
4430   PetscDS        prob;
4431   PetscInt       numBd, bd;
4432   DMField        coordField = NULL;
4433   IS             facetIS    = NULL;
4434   DMLabel        depthLabel;
4435   PetscInt       dim;
4436 
4437   PetscFunctionBegin;
4438   PetscCall(DMGetDS(dm, &prob));
4439   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
4440   PetscCall(DMGetDimension(dm, &dim));
4441   PetscCall(DMLabelGetStratumIS(depthLabel,dim - 1,&facetIS));
4442   PetscCall(PetscDSGetNumBoundary(prob, &numBd));
4443   for (bd = 0; bd < numBd; ++bd) {
4444     PetscWeakForm           wf;
4445     DMBoundaryConditionType type;
4446     DMLabel                 label;
4447     const PetscInt         *values;
4448     PetscInt                field, numValues, v;
4449     PetscObject             obj;
4450     PetscClassId            id;
4451     PetscFormKey            key;
4452 
4453     PetscCall(PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &field, NULL, NULL, NULL, NULL, NULL));
4454     PetscCall(PetscDSGetDiscretization(prob, field, &obj));
4455     PetscCall(PetscObjectGetClassId(obj, &id));
4456     if ((id != PETSCFE_CLASSID) || (type & DM_BC_ESSENTIAL)) continue;
4457     if (!facetIS) {
4458       DMLabel  depthLabel;
4459       PetscInt dim;
4460 
4461       PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
4462       PetscCall(DMGetDimension(dm, &dim));
4463       PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
4464     }
4465     PetscCall(DMGetCoordinateField(dm, &coordField));
4466     for (v = 0; v < numValues; ++v) {
4467       key.label = label;
4468       key.value = values[v];
4469       key.field = field;
4470       key.part  = 0;
4471       PetscCall(DMPlexComputeBdResidual_Single_Internal(dm, t, wf, key, locX, locX_t, locF, coordField, facetIS));
4472     }
4473   }
4474   PetscCall(ISDestroy(&facetIS));
4475   PetscFunctionReturn(0);
4476 }
4477 
4478 PetscErrorCode DMPlexComputeResidual_Internal(DM dm, PetscFormKey key, IS cellIS, PetscReal time, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
4479 {
4480   DM_Plex         *mesh       = (DM_Plex *) dm->data;
4481   const char      *name       = "Residual";
4482   DM               dmAux      = NULL;
4483   DM               dmGrad     = NULL;
4484   DMLabel          ghostLabel = NULL;
4485   PetscDS          ds         = NULL;
4486   PetscDS          dsAux      = NULL;
4487   PetscSection     section    = NULL;
4488   PetscBool        useFEM     = PETSC_FALSE;
4489   PetscBool        useFVM     = PETSC_FALSE;
4490   PetscBool        isImplicit = (locX_t || time == PETSC_MIN_REAL) ? PETSC_TRUE : PETSC_FALSE;
4491   PetscFV          fvm        = NULL;
4492   PetscFVCellGeom *cgeomFVM   = NULL;
4493   PetscFVFaceGeom *fgeomFVM   = NULL;
4494   DMField          coordField = NULL;
4495   Vec              locA, cellGeometryFVM = NULL, faceGeometryFVM = NULL, grad, locGrad = NULL;
4496   PetscScalar     *u = NULL, *u_t, *a, *uL, *uR;
4497   IS               chunkIS;
4498   const PetscInt  *cells;
4499   PetscInt         cStart, cEnd, numCells;
4500   PetscInt         Nf, f, totDim, totDimAux, numChunks, cellChunkSize, faceChunkSize, chunk, fStart, fEnd;
4501   PetscInt         maxDegree = PETSC_MAX_INT;
4502   PetscQuadrature  affineQuad = NULL, *quads = NULL;
4503   PetscFEGeom     *affineGeom = NULL, **geoms = NULL;
4504 
4505   PetscFunctionBegin;
4506   PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM,dm,0,0,0));
4507   /* TODO The places where we have to use isFE are probably the member functions for the PetscDisc class */
4508   /* TODO The FVM geometry is over-manipulated. Make the precalc functions return exactly what we need */
4509   /* FEM+FVM */
4510   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
4511   PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
4512   /* 1: Get sizes from dm and dmAux */
4513   PetscCall(DMGetLocalSection(dm, &section));
4514   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
4515   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds));
4516   PetscCall(PetscDSGetNumFields(ds, &Nf));
4517   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
4518   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &locA));
4519   if (locA) {
4520     PetscInt subcell;
4521     PetscCall(VecGetDM(locA, &dmAux));
4522     PetscCall(DMGetEnclosurePoint(dmAux, dm, DM_ENC_UNKNOWN, cells ? cells[cStart] : cStart, &subcell));
4523     PetscCall(DMGetCellDS(dmAux, subcell, &dsAux));
4524     PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
4525   }
4526   /* 2: Get geometric data */
4527   for (f = 0; f < Nf; ++f) {
4528     PetscObject  obj;
4529     PetscClassId id;
4530     PetscBool    fimp;
4531 
4532     PetscCall(PetscDSGetImplicit(ds, f, &fimp));
4533     if (isImplicit != fimp) continue;
4534     PetscCall(PetscDSGetDiscretization(ds, f, &obj));
4535     PetscCall(PetscObjectGetClassId(obj, &id));
4536     if (id == PETSCFE_CLASSID) {useFEM = PETSC_TRUE;}
4537     if (id == PETSCFV_CLASSID) {useFVM = PETSC_TRUE; fvm = (PetscFV) obj;}
4538   }
4539   if (useFEM) {
4540     PetscCall(DMGetCoordinateField(dm, &coordField));
4541     PetscCall(DMFieldGetDegree(coordField,cellIS,NULL,&maxDegree));
4542     if (maxDegree <= 1) {
4543       PetscCall(DMFieldCreateDefaultQuadrature(coordField,cellIS,&affineQuad));
4544       if (affineQuad) {
4545         PetscCall(DMSNESGetFEGeom(coordField,cellIS,affineQuad,PETSC_FALSE,&affineGeom));
4546       }
4547     } else {
4548       PetscCall(PetscCalloc2(Nf,&quads,Nf,&geoms));
4549       for (f = 0; f < Nf; ++f) {
4550         PetscObject  obj;
4551         PetscClassId id;
4552         PetscBool    fimp;
4553 
4554         PetscCall(PetscDSGetImplicit(ds, f, &fimp));
4555         if (isImplicit != fimp) continue;
4556         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
4557         PetscCall(PetscObjectGetClassId(obj, &id));
4558         if (id == PETSCFE_CLASSID) {
4559           PetscFE fe = (PetscFE) obj;
4560 
4561           PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
4562           PetscCall(PetscObjectReference((PetscObject)quads[f]));
4563           PetscCall(DMSNESGetFEGeom(coordField,cellIS,quads[f],PETSC_FALSE,&geoms[f]));
4564         }
4565       }
4566     }
4567   }
4568   if (useFVM) {
4569     PetscCall(DMPlexGetGeometryFVM(dm, &faceGeometryFVM, &cellGeometryFVM, NULL));
4570     PetscCall(VecGetArrayRead(faceGeometryFVM, (const PetscScalar **) &fgeomFVM));
4571     PetscCall(VecGetArrayRead(cellGeometryFVM, (const PetscScalar **) &cgeomFVM));
4572     /* Reconstruct and limit cell gradients */
4573     PetscCall(DMPlexGetGradientDM(dm, fvm, &dmGrad));
4574     if (dmGrad) {
4575       PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
4576       PetscCall(DMGetGlobalVector(dmGrad, &grad));
4577       PetscCall(DMPlexReconstructGradients_Internal(dm, fvm, fStart, fEnd, faceGeometryFVM, cellGeometryFVM, locX, grad));
4578       /* Communicate gradient values */
4579       PetscCall(DMGetLocalVector(dmGrad, &locGrad));
4580       PetscCall(DMGlobalToLocalBegin(dmGrad, grad, INSERT_VALUES, locGrad));
4581       PetscCall(DMGlobalToLocalEnd(dmGrad, grad, INSERT_VALUES, locGrad));
4582       PetscCall(DMRestoreGlobalVector(dmGrad, &grad));
4583     }
4584     /* Handle non-essential (e.g. outflow) boundary values */
4585     PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_FALSE, locX, time, faceGeometryFVM, cellGeometryFVM, locGrad));
4586   }
4587   /* Loop over chunks */
4588   if (useFEM) PetscCall(ISCreate(PETSC_COMM_SELF, &chunkIS));
4589   numCells      = cEnd - cStart;
4590   numChunks     = 1;
4591   cellChunkSize = numCells/numChunks;
4592   faceChunkSize = (fEnd - fStart)/numChunks;
4593   numChunks     = PetscMin(1,numCells);
4594   for (chunk = 0; chunk < numChunks; ++chunk) {
4595     PetscScalar     *elemVec, *fluxL, *fluxR;
4596     PetscReal       *vol;
4597     PetscFVFaceGeom *fgeom;
4598     PetscInt         cS = cStart+chunk*cellChunkSize, cE = PetscMin(cS+cellChunkSize, cEnd), numCells = cE - cS, c;
4599     PetscInt         fS = fStart+chunk*faceChunkSize, fE = PetscMin(fS+faceChunkSize, fEnd), numFaces = 0, face;
4600 
4601     /* Extract field coefficients */
4602     if (useFEM) {
4603       PetscCall(ISGetPointSubrange(chunkIS, cS, cE, cells));
4604       PetscCall(DMPlexGetCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
4605       PetscCall(DMGetWorkArray(dm, numCells*totDim, MPIU_SCALAR, &elemVec));
4606       PetscCall(PetscArrayzero(elemVec, numCells*totDim));
4607     }
4608     if (useFVM) {
4609       PetscCall(DMPlexGetFaceFields(dm, fS, fE, locX, locX_t, faceGeometryFVM, cellGeometryFVM, locGrad, &numFaces, &uL, &uR));
4610       PetscCall(DMPlexGetFaceGeometry(dm, fS, fE, faceGeometryFVM, cellGeometryFVM, &numFaces, &fgeom, &vol));
4611       PetscCall(DMGetWorkArray(dm, numFaces*totDim, MPIU_SCALAR, &fluxL));
4612       PetscCall(DMGetWorkArray(dm, numFaces*totDim, MPIU_SCALAR, &fluxR));
4613       PetscCall(PetscArrayzero(fluxL, numFaces*totDim));
4614       PetscCall(PetscArrayzero(fluxR, numFaces*totDim));
4615     }
4616     /* TODO We will interlace both our field coefficients (u, u_t, uL, uR, etc.) and our output (elemVec, fL, fR). I think this works */
4617     /* Loop over fields */
4618     for (f = 0; f < Nf; ++f) {
4619       PetscObject  obj;
4620       PetscClassId id;
4621       PetscBool    fimp;
4622       PetscInt     numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;
4623 
4624       key.field = f;
4625       PetscCall(PetscDSGetImplicit(ds, f, &fimp));
4626       if (isImplicit != fimp) continue;
4627       PetscCall(PetscDSGetDiscretization(ds, f, &obj));
4628       PetscCall(PetscObjectGetClassId(obj, &id));
4629       if (id == PETSCFE_CLASSID) {
4630         PetscFE         fe = (PetscFE) obj;
4631         PetscFEGeom    *geom = affineGeom ? affineGeom : geoms[f];
4632         PetscFEGeom    *chunkGeom = NULL;
4633         PetscQuadrature quad = affineQuad ? affineQuad : quads[f];
4634         PetscInt        Nq, Nb;
4635 
4636         PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
4637         PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
4638         PetscCall(PetscFEGetDimension(fe, &Nb));
4639         blockSize = Nb;
4640         batchSize = numBlocks * blockSize;
4641         PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
4642         numChunks = numCells / (numBatches*batchSize);
4643         Ne        = numChunks*numBatches*batchSize;
4644         Nr        = numCells % (numBatches*batchSize);
4645         offset    = numCells - Nr;
4646         /* Integrate FE residual to get elemVec (need fields at quadrature points) */
4647         /*   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) */
4648         PetscCall(PetscFEGeomGetChunk(geom,0,offset,&chunkGeom));
4649         PetscCall(PetscFEIntegrateResidual(ds, key, Ne, chunkGeom, u, u_t, dsAux, a, t, elemVec));
4650         PetscCall(PetscFEGeomGetChunk(geom,offset,numCells,&chunkGeom));
4651         PetscCall(PetscFEIntegrateResidual(ds, key, Nr, chunkGeom, &u[offset*totDim], u_t ? &u_t[offset*totDim] : NULL, dsAux, &a[offset*totDimAux], t, &elemVec[offset*totDim]));
4652         PetscCall(PetscFEGeomRestoreChunk(geom,offset,numCells,&chunkGeom));
4653       } else if (id == PETSCFV_CLASSID) {
4654         PetscFV fv = (PetscFV) obj;
4655 
4656         Ne = numFaces;
4657         /* Riemann solve over faces (need fields at face centroids) */
4658         /*   We need to evaluate FE fields at those coordinates */
4659         PetscCall(PetscFVIntegrateRHSFunction(fv, ds, f, Ne, fgeom, vol, uL, uR, fluxL, fluxR));
4660       } else SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
4661     }
4662     /* Loop over domain */
4663     if (useFEM) {
4664       /* Add elemVec to locX */
4665       for (c = cS; c < cE; ++c) {
4666         const PetscInt cell = cells ? cells[c] : c;
4667         const PetscInt cind = c - cStart;
4668 
4669         if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, name, totDim, &elemVec[cind*totDim]));
4670         if (ghostLabel) {
4671           PetscInt ghostVal;
4672 
4673           PetscCall(DMLabelGetValue(ghostLabel,cell,&ghostVal));
4674           if (ghostVal > 0) continue;
4675         }
4676         PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind*totDim], ADD_ALL_VALUES));
4677       }
4678     }
4679     if (useFVM) {
4680       PetscScalar *fa;
4681       PetscInt     iface;
4682 
4683       PetscCall(VecGetArray(locF, &fa));
4684       for (f = 0; f < Nf; ++f) {
4685         PetscFV      fv;
4686         PetscObject  obj;
4687         PetscClassId id;
4688         PetscInt     foff, pdim;
4689 
4690         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
4691         PetscCall(PetscDSGetFieldOffset(ds, f, &foff));
4692         PetscCall(PetscObjectGetClassId(obj, &id));
4693         if (id != PETSCFV_CLASSID) continue;
4694         fv   = (PetscFV) obj;
4695         PetscCall(PetscFVGetNumComponents(fv, &pdim));
4696         /* Accumulate fluxes to cells */
4697         for (face = fS, iface = 0; face < fE; ++face) {
4698           const PetscInt *scells;
4699           PetscScalar    *fL = NULL, *fR = NULL;
4700           PetscInt        ghost, d, nsupp, nchild;
4701 
4702           PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
4703           PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
4704           PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
4705           if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
4706           PetscCall(DMPlexGetSupport(dm, face, &scells));
4707           PetscCall(DMLabelGetValue(ghostLabel,scells[0],&ghost));
4708           if (ghost <= 0) PetscCall(DMPlexPointLocalFieldRef(dm, scells[0], f, fa, &fL));
4709           PetscCall(DMLabelGetValue(ghostLabel,scells[1],&ghost));
4710           if (ghost <= 0) PetscCall(DMPlexPointLocalFieldRef(dm, scells[1], f, fa, &fR));
4711           for (d = 0; d < pdim; ++d) {
4712             if (fL) fL[d] -= fluxL[iface*totDim+foff+d];
4713             if (fR) fR[d] += fluxR[iface*totDim+foff+d];
4714           }
4715           ++iface;
4716         }
4717       }
4718       PetscCall(VecRestoreArray(locF, &fa));
4719     }
4720     /* Handle time derivative */
4721     if (locX_t) {
4722       PetscScalar *x_t, *fa;
4723 
4724       PetscCall(VecGetArray(locF, &fa));
4725       PetscCall(VecGetArray(locX_t, &x_t));
4726       for (f = 0; f < Nf; ++f) {
4727         PetscFV      fv;
4728         PetscObject  obj;
4729         PetscClassId id;
4730         PetscInt     pdim, d;
4731 
4732         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
4733         PetscCall(PetscObjectGetClassId(obj, &id));
4734         if (id != PETSCFV_CLASSID) continue;
4735         fv   = (PetscFV) obj;
4736         PetscCall(PetscFVGetNumComponents(fv, &pdim));
4737         for (c = cS; c < cE; ++c) {
4738           const PetscInt cell = cells ? cells[c] : c;
4739           PetscScalar   *u_t, *r;
4740 
4741           if (ghostLabel) {
4742             PetscInt ghostVal;
4743 
4744             PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
4745             if (ghostVal > 0) continue;
4746           }
4747           PetscCall(DMPlexPointLocalFieldRead(dm, cell, f, x_t, &u_t));
4748           PetscCall(DMPlexPointLocalFieldRef(dm, cell, f, fa, &r));
4749           for (d = 0; d < pdim; ++d) r[d] += u_t[d];
4750         }
4751       }
4752       PetscCall(VecRestoreArray(locX_t, &x_t));
4753       PetscCall(VecRestoreArray(locF, &fa));
4754     }
4755     if (useFEM) {
4756       PetscCall(DMPlexRestoreCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
4757       PetscCall(DMRestoreWorkArray(dm, numCells*totDim, MPIU_SCALAR, &elemVec));
4758     }
4759     if (useFVM) {
4760       PetscCall(DMPlexRestoreFaceFields(dm, fS, fE, locX, locX_t, faceGeometryFVM, cellGeometryFVM, locGrad, &numFaces, &uL, &uR));
4761       PetscCall(DMPlexRestoreFaceGeometry(dm, fS, fE, faceGeometryFVM, cellGeometryFVM, &numFaces, &fgeom, &vol));
4762       PetscCall(DMRestoreWorkArray(dm, numFaces*totDim, MPIU_SCALAR, &fluxL));
4763       PetscCall(DMRestoreWorkArray(dm, numFaces*totDim, MPIU_SCALAR, &fluxR));
4764       if (dmGrad) PetscCall(DMRestoreLocalVector(dmGrad, &locGrad));
4765     }
4766   }
4767   if (useFEM) PetscCall(ISDestroy(&chunkIS));
4768   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
4769 
4770   if (useFEM) {
4771     PetscCall(DMPlexComputeBdResidual_Internal(dm, locX, locX_t, t, locF, user));
4772 
4773     if (maxDegree <= 1) {
4774       PetscCall(DMSNESRestoreFEGeom(coordField,cellIS,affineQuad,PETSC_FALSE,&affineGeom));
4775       PetscCall(PetscQuadratureDestroy(&affineQuad));
4776     } else {
4777       for (f = 0; f < Nf; ++f) {
4778         PetscCall(DMSNESRestoreFEGeom(coordField,cellIS,quads[f],PETSC_FALSE,&geoms[f]));
4779         PetscCall(PetscQuadratureDestroy(&quads[f]));
4780       }
4781       PetscCall(PetscFree2(quads,geoms));
4782     }
4783   }
4784 
4785   /* FEM */
4786   /* 1: Get sizes from dm and dmAux */
4787   /* 2: Get geometric data */
4788   /* 3: Handle boundary values */
4789   /* 4: Loop over domain */
4790   /*   Extract coefficients */
4791   /* Loop over fields */
4792   /*   Set tiling for FE*/
4793   /*   Integrate FE residual to get elemVec */
4794   /*     Loop over subdomain */
4795   /*       Loop over quad points */
4796   /*         Transform coords to real space */
4797   /*         Evaluate field and aux fields at point */
4798   /*         Evaluate residual at point */
4799   /*         Transform residual to real space */
4800   /*       Add residual to elemVec */
4801   /* Loop over domain */
4802   /*   Add elemVec to locX */
4803 
4804   /* FVM */
4805   /* Get geometric data */
4806   /* If using gradients */
4807   /*   Compute gradient data */
4808   /*   Loop over domain faces */
4809   /*     Count computational faces */
4810   /*     Reconstruct cell gradient */
4811   /*   Loop over domain cells */
4812   /*     Limit cell gradients */
4813   /* Handle boundary values */
4814   /* Loop over domain faces */
4815   /*   Read out field, centroid, normal, volume for each side of face */
4816   /* Riemann solve over faces */
4817   /* Loop over domain faces */
4818   /*   Accumulate fluxes to cells */
4819   /* TODO Change printFEM to printDisc here */
4820   if (mesh->printFEM) {
4821     Vec         locFbc;
4822     PetscInt    pStart, pEnd, p, maxDof;
4823     PetscScalar *zeroes;
4824 
4825     PetscCall(VecDuplicate(locF,&locFbc));
4826     PetscCall(VecCopy(locF,locFbc));
4827     PetscCall(PetscSectionGetChart(section,&pStart,&pEnd));
4828     PetscCall(PetscSectionGetMaxDof(section,&maxDof));
4829     PetscCall(PetscCalloc1(maxDof,&zeroes));
4830     for (p = pStart; p < pEnd; p++) {
4831       PetscCall(VecSetValuesSection(locFbc,section,p,zeroes,INSERT_BC_VALUES));
4832     }
4833     PetscCall(PetscFree(zeroes));
4834     PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc));
4835     PetscCall(VecDestroy(&locFbc));
4836   }
4837   PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM,dm,0,0,0));
4838   PetscFunctionReturn(0);
4839 }
4840 
4841 /*
4842   1) Allow multiple kernels for BdResidual for hybrid DS
4843 
4844   DONE 2) Get out dsAux for either side at the same time as cohesive cell dsAux
4845 
4846   DONE 3) Change DMGetCellFields() to get different aux data a[] for each side
4847      - I think I just need to replace a[] with the closure from each face
4848 
4849   4) Run both kernels for each non-hybrid field with correct dsAux, and then hybrid field as before
4850 */
4851 PetscErrorCode DMPlexComputeResidual_Hybrid_Internal(DM dm, PetscFormKey key[], IS cellIS, PetscReal time, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
4852 {
4853   DM_Plex         *mesh       = (DM_Plex *) dm->data;
4854   const char      *name       = "Hybrid Residual";
4855   DM               dmAux[3]   = {NULL, NULL, NULL};
4856   DMLabel          ghostLabel = NULL;
4857   PetscDS          ds         = NULL;
4858   PetscDS          dsAux[3]   = {NULL, NULL, NULL};
4859   Vec              locA[3]    = {NULL, NULL, NULL};
4860   PetscSection     section    = NULL;
4861   DMField          coordField = NULL;
4862   PetscScalar     *u = NULL, *u_t, *a[3];
4863   PetscScalar     *elemVec;
4864   IS               chunkIS;
4865   const PetscInt  *cells;
4866   PetscInt        *faces;
4867   PetscInt         cStart, cEnd, numCells;
4868   PetscInt         Nf, f, totDim, totDimAux[3], numChunks, cellChunkSize, chunk;
4869   PetscInt         maxDegree = PETSC_MAX_INT;
4870   PetscQuadrature  affineQuad = NULL, *quads = NULL;
4871   PetscFEGeom     *affineGeom = NULL, **geoms = NULL;
4872 
4873   PetscFunctionBegin;
4874   if ((key[0].label == key[1].label) && (key[0].value == key[1].value) && (key[0].part == key[1].part)) {
4875     const char *name;
4876     PetscCall(PetscObjectGetName((PetscObject) key[0].label, &name));
4877     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);
4878   }
4879   PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM,dm,0,0,0));
4880   /* TODO The places where we have to use isFE are probably the member functions for the PetscDisc class */
4881   /* FEM */
4882   PetscCall(ISGetLocalSize(cellIS, &numCells));
4883   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
4884   /* 1: Get sizes from dm and dmAux */
4885   PetscCall(DMGetSection(dm, &section));
4886   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
4887   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds));
4888   PetscCall(PetscDSGetNumFields(ds, &Nf));
4889   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
4890   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, key[2].value, key[2].part, &locA[2]));
4891   if (locA[2]) {
4892     const PetscInt cellStart = cells ? cells[cStart] : cStart;
4893 
4894     PetscCall(VecGetDM(locA[2], &dmAux[2]));
4895     PetscCall(DMGetCellDS(dmAux[2], cellStart, &dsAux[2]));
4896     PetscCall(PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]));
4897     {
4898       const PetscInt *cone;
4899       PetscInt        c;
4900 
4901       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
4902       for (c = 0; c < 2; ++c) {
4903         const PetscInt *support;
4904         PetscInt ssize, s;
4905 
4906         PetscCall(DMPlexGetSupport(dm, cone[c], &support));
4907         PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
4908         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], cellStart, ssize);
4909         if      (support[0] == cellStart) s = 1;
4910         else if (support[1] == cellStart) s = 0;
4911         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
4912         PetscCall(DMGetAuxiliaryVec(dm, key[c].label, key[c].value, key[c].part, &locA[c]));
4913         if (locA[c]) PetscCall(VecGetDM(locA[c], &dmAux[c]));
4914         else         {dmAux[c] = dmAux[2];}
4915         PetscCall(DMGetCellDS(dmAux[c], support[s], &dsAux[c]));
4916         PetscCall(PetscDSGetTotalDimension(dsAux[c], &totDimAux[c]));
4917       }
4918     }
4919   }
4920   /* 2: Setup geometric data */
4921   PetscCall(DMGetCoordinateField(dm, &coordField));
4922   PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
4923   if (maxDegree > 1) {
4924     PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
4925     for (f = 0; f < Nf; ++f) {
4926       PetscFE fe;
4927 
4928       PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *) &fe));
4929       if (fe) {
4930         PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
4931         PetscCall(PetscObjectReference((PetscObject) quads[f]));
4932       }
4933     }
4934   }
4935   /* Loop over chunks */
4936   cellChunkSize = numCells;
4937   numChunks     = !numCells ? 0 : PetscCeilReal(((PetscReal) numCells)/cellChunkSize);
4938   PetscCall(PetscCalloc1(1*cellChunkSize, &faces));
4939   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 1*cellChunkSize, faces, PETSC_USE_POINTER, &chunkIS));
4940   /* Extract field coefficients */
4941   /* NOTE This needs the end cap faces to have identical orientations */
4942   PetscCall(DMPlexGetCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
4943   PetscCall(DMPlexGetHybridAuxFields(dm, dmAux, dsAux, cellIS, locA, a));
4944   PetscCall(DMGetWorkArray(dm, cellChunkSize*totDim, MPIU_SCALAR, &elemVec));
4945   for (chunk = 0; chunk < numChunks; ++chunk) {
4946     PetscInt cS = cStart+chunk*cellChunkSize, cE = PetscMin(cS+cellChunkSize, cEnd), numCells = cE - cS, c;
4947 
4948     PetscCall(PetscMemzero(elemVec, cellChunkSize*totDim * sizeof(PetscScalar)));
4949     /* Get faces */
4950     for (c = cS; c < cE; ++c) {
4951       const PetscInt  cell = cells ? cells[c] : c;
4952       const PetscInt *cone;
4953       PetscCall(DMPlexGetCone(dm, cell, &cone));
4954       faces[0*cellChunkSize+(c-cS)] = cone[0];
4955       /*faces[1*cellChunkSize+(c-cS)] = cone[1];*/
4956     }
4957     PetscCall(ISGeneralSetIndices(chunkIS, 1*cellChunkSize, faces, PETSC_USE_POINTER));
4958     /* Get geometric data */
4959     if (maxDegree <= 1) {
4960       if (!affineQuad) PetscCall(DMFieldCreateDefaultQuadrature(coordField, chunkIS, &affineQuad));
4961       if (affineQuad)  PetscCall(DMSNESGetFEGeom(coordField, chunkIS, affineQuad, PETSC_TRUE, &affineGeom));
4962     } else {
4963       for (f = 0; f < Nf; ++f) {
4964         if (quads[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, quads[f], PETSC_TRUE, &geoms[f]));
4965       }
4966     }
4967     /* Loop over fields */
4968     for (f = 0; f < Nf; ++f) {
4969       PetscFE         fe;
4970       PetscFEGeom    *geom = affineGeom ? affineGeom : geoms[f];
4971       PetscFEGeom    *chunkGeom = NULL, *remGeom = NULL;
4972       PetscQuadrature quad = affineQuad ? affineQuad : quads[f];
4973       PetscInt        numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb;
4974       PetscBool       isCohesiveField;
4975 
4976       PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *) &fe));
4977       if (!fe) continue;
4978       PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
4979       PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
4980       PetscCall(PetscFEGetDimension(fe, &Nb));
4981       blockSize = Nb;
4982       batchSize = numBlocks * blockSize;
4983       PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
4984       numChunks = numCells / (numBatches*batchSize);
4985       Ne        = numChunks*numBatches*batchSize;
4986       Nr        = numCells % (numBatches*batchSize);
4987       offset    = numCells - Nr;
4988       PetscCall(PetscFEGeomGetChunk(geom,0,offset,&chunkGeom));
4989       PetscCall(PetscFEGeomGetChunk(geom,offset,numCells,&remGeom));
4990       PetscCall(PetscDSGetCohesive(ds, f, &isCohesiveField));
4991       chunkGeom->isCohesive = remGeom->isCohesive = PETSC_TRUE;
4992       key[0].field = f;
4993       key[1].field = f;
4994       key[2].field = f;
4995       PetscCall(PetscFEIntegrateHybridResidual(ds, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, elemVec));
4996       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]));
4997       PetscCall(PetscFEIntegrateHybridResidual(ds, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, elemVec));
4998       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]));
4999       PetscCall(PetscFEIntegrateHybridResidual(ds, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, elemVec));
5000       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]));
5001       PetscCall(PetscFEGeomRestoreChunk(geom,offset,numCells,&remGeom));
5002       PetscCall(PetscFEGeomRestoreChunk(geom,0,offset,&chunkGeom));
5003     }
5004     /* Add elemVec to locX */
5005     for (c = cS; c < cE; ++c) {
5006       const PetscInt cell = cells ? cells[c] : c;
5007       const PetscInt cind = c - cStart;
5008 
5009       if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, name, totDim, &elemVec[cind*totDim]));
5010       if (ghostLabel) {
5011         PetscInt ghostVal;
5012 
5013         PetscCall(DMLabelGetValue(ghostLabel,cell,&ghostVal));
5014         if (ghostVal > 0) continue;
5015       }
5016       PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind*totDim], ADD_ALL_VALUES));
5017     }
5018   }
5019   PetscCall(DMPlexRestoreCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
5020   PetscCall(DMPlexRestoreHybridAuxFields(dmAux, dsAux, cellIS, locA, a));
5021   PetscCall(DMRestoreWorkArray(dm, numCells*totDim, MPIU_SCALAR, &elemVec));
5022   PetscCall(PetscFree(faces));
5023   PetscCall(ISDestroy(&chunkIS));
5024   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
5025   if (maxDegree <= 1) {
5026     PetscCall(DMSNESRestoreFEGeom(coordField,cellIS,affineQuad,PETSC_FALSE,&affineGeom));
5027     PetscCall(PetscQuadratureDestroy(&affineQuad));
5028   } else {
5029     for (f = 0; f < Nf; ++f) {
5030       if (geoms) PetscCall(DMSNESRestoreFEGeom(coordField,cellIS,quads[f],PETSC_FALSE,&geoms[f]));
5031       if (quads) PetscCall(PetscQuadratureDestroy(&quads[f]));
5032     }
5033     PetscCall(PetscFree2(quads,geoms));
5034   }
5035   PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM,dm,0,0,0));
5036   PetscFunctionReturn(0);
5037 }
5038 
5039 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)
5040 {
5041   DM_Plex        *mesh = (DM_Plex *) dm->data;
5042   DM              plex = NULL, plexA = NULL, tdm;
5043   DMEnclosureType encAux;
5044   PetscDS         prob, probAux = NULL;
5045   PetscSection    section, sectionAux = NULL;
5046   PetscSection    globalSection;
5047   Vec             locA = NULL, tv;
5048   PetscScalar    *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL;
5049   PetscInt        v;
5050   PetscInt        Nf, totDim, totDimAux = 0;
5051   PetscBool       transform;
5052 
5053   PetscFunctionBegin;
5054   PetscCall(DMConvert(dm, DMPLEX, &plex));
5055   PetscCall(DMHasBasisTransform(dm, &transform));
5056   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
5057   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
5058   PetscCall(DMGetLocalSection(dm, &section));
5059   PetscCall(DMGetDS(dm, &prob));
5060   PetscCall(PetscDSGetNumFields(prob, &Nf));
5061   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
5062   PetscCall(DMGetAuxiliaryVec(dm, label, values[0], 0, &locA));
5063   if (locA) {
5064     DM dmAux;
5065 
5066     PetscCall(VecGetDM(locA, &dmAux));
5067     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
5068     PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
5069     PetscCall(DMGetDS(plexA, &probAux));
5070     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
5071     PetscCall(DMGetLocalSection(plexA, &sectionAux));
5072   }
5073 
5074   PetscCall(DMGetGlobalSection(dm, &globalSection));
5075   for (v = 0; v < numValues; ++v) {
5076     PetscFEGeom     *fgeom;
5077     PetscInt         maxDegree;
5078     PetscQuadrature  qGeom = NULL;
5079     IS               pointIS;
5080     const PetscInt  *points;
5081     PetscFormKey key;
5082     PetscInt         numFaces, face, Nq;
5083 
5084     key.label = label;
5085     key.value = values[v];
5086     key.part  = 0;
5087     PetscCall(DMLabelGetStratumIS(label, values[v], &pointIS));
5088     if (!pointIS) continue; /* No points with that id on this process */
5089     {
5090       IS isectIS;
5091 
5092       /* TODO: Special cases of ISIntersect where it is quick to check a prior if one is a superset of the other */
5093       PetscCall(ISIntersect_Caching_Internal(facetIS,pointIS,&isectIS));
5094       PetscCall(ISDestroy(&pointIS));
5095       pointIS = isectIS;
5096     }
5097     PetscCall(ISGetLocalSize(pointIS, &numFaces));
5098     PetscCall(ISGetIndices(pointIS, &points));
5099     PetscCall(PetscMalloc4(numFaces*totDim, &u, locX_t ? numFaces*totDim : 0, &u_t, numFaces*totDim*totDim, &elemMat, locA ? numFaces*totDimAux : 0, &a));
5100     PetscCall(DMFieldGetDegree(coordField,pointIS,NULL,&maxDegree));
5101     if (maxDegree <= 1) {
5102       PetscCall(DMFieldCreateDefaultQuadrature(coordField,pointIS,&qGeom));
5103     }
5104     if (!qGeom) {
5105       PetscFE fe;
5106 
5107       PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *) &fe));
5108       PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
5109       PetscCall(PetscObjectReference((PetscObject)qGeom));
5110     }
5111     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
5112     PetscCall(DMSNESGetFEGeom(coordField,pointIS,qGeom,PETSC_TRUE,&fgeom));
5113     for (face = 0; face < numFaces; ++face) {
5114       const PetscInt point = points[face], *support;
5115       PetscScalar   *x     = NULL;
5116       PetscInt       i;
5117 
5118       PetscCall(DMPlexGetSupport(dm, point, &support));
5119       PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
5120       for (i = 0; i < totDim; ++i) u[face*totDim+i] = x[i];
5121       PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
5122       if (locX_t) {
5123         PetscCall(DMPlexVecGetClosure(plex, section, locX_t, support[0], NULL, &x));
5124         for (i = 0; i < totDim; ++i) u_t[face*totDim+i] = x[i];
5125         PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, support[0], NULL, &x));
5126       }
5127       if (locA) {
5128         PetscInt subp;
5129         PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
5130         PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
5131         for (i = 0; i < totDimAux; ++i) a[face*totDimAux+i] = x[i];
5132         PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
5133       }
5134     }
5135     PetscCall(PetscArrayzero(elemMat, numFaces*totDim*totDim));
5136     {
5137       PetscFE         fe;
5138       PetscInt        Nb;
5139       /* Conforming batches */
5140       PetscInt        numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
5141       /* Remainder */
5142       PetscFEGeom    *chunkGeom = NULL;
5143       PetscInt        fieldJ, Nr, offset;
5144 
5145       PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *) &fe));
5146       PetscCall(PetscFEGetDimension(fe, &Nb));
5147       PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5148       blockSize = Nb;
5149       batchSize = numBlocks * blockSize;
5150       PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5151       numChunks = numFaces / (numBatches*batchSize);
5152       Ne        = numChunks*numBatches*batchSize;
5153       Nr        = numFaces % (numBatches*batchSize);
5154       offset    = numFaces - Nr;
5155       PetscCall(PetscFEGeomGetChunk(fgeom,0,offset,&chunkGeom));
5156       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5157         key.field = fieldI*Nf+fieldJ;
5158         PetscCall(PetscFEIntegrateBdJacobian(prob, wf, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat));
5159       }
5160       PetscCall(PetscFEGeomGetChunk(fgeom,offset,numFaces,&chunkGeom));
5161       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5162         key.field = fieldI*Nf+fieldJ;
5163         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]));
5164       }
5165       PetscCall(PetscFEGeomRestoreChunk(fgeom,offset,numFaces,&chunkGeom));
5166     }
5167     for (face = 0; face < numFaces; ++face) {
5168       const PetscInt point = points[face], *support;
5169 
5170       /* Transform to global basis before insertion in Jacobian */
5171       PetscCall(DMPlexGetSupport(plex, point, &support));
5172       if (transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, support[0], PETSC_TRUE, totDim, &elemMat[face*totDim*totDim]));
5173       if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(point, "BdJacobian", totDim, totDim, &elemMat[face*totDim*totDim]));
5174       PetscCall(DMPlexMatSetClosure(plex, section, globalSection, JacP, support[0], &elemMat[face*totDim*totDim], ADD_VALUES));
5175     }
5176     PetscCall(DMSNESRestoreFEGeom(coordField,pointIS,qGeom,PETSC_TRUE,&fgeom));
5177     PetscCall(PetscQuadratureDestroy(&qGeom));
5178     PetscCall(ISRestoreIndices(pointIS, &points));
5179     PetscCall(ISDestroy(&pointIS));
5180     PetscCall(PetscFree4(u, u_t, elemMat, a));
5181   }
5182   if (plex)  PetscCall(DMDestroy(&plex));
5183   if (plexA) PetscCall(DMDestroy(&plexA));
5184   PetscFunctionReturn(0);
5185 }
5186 
5187 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)
5188 {
5189   DMField        coordField;
5190   DMLabel        depthLabel;
5191   IS             facetIS;
5192   PetscInt       dim;
5193 
5194   PetscFunctionBegin;
5195   PetscCall(DMGetDimension(dm, &dim));
5196   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5197   PetscCall(DMLabelGetStratumIS(depthLabel, dim-1, &facetIS));
5198   PetscCall(DMGetCoordinateField(dm, &coordField));
5199   PetscCall(DMPlexComputeBdJacobian_Single_Internal(dm, t, wf, label, numValues, values, field, locX, locX_t, X_tShift, Jac, JacP, coordField, facetIS));
5200   PetscCall(ISDestroy(&facetIS));
5201   PetscFunctionReturn(0);
5202 }
5203 
5204 PetscErrorCode DMPlexComputeBdJacobian_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, PetscReal X_tShift, Mat Jac, Mat JacP, void *user)
5205 {
5206   PetscDS          prob;
5207   PetscInt         dim, numBd, bd;
5208   DMLabel          depthLabel;
5209   DMField          coordField = NULL;
5210   IS               facetIS;
5211 
5212   PetscFunctionBegin;
5213   PetscCall(DMGetDS(dm, &prob));
5214   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5215   PetscCall(DMGetDimension(dm, &dim));
5216   PetscCall(DMLabelGetStratumIS(depthLabel, dim-1, &facetIS));
5217   PetscCall(PetscDSGetNumBoundary(prob, &numBd));
5218   PetscCall(DMGetCoordinateField(dm, &coordField));
5219   for (bd = 0; bd < numBd; ++bd) {
5220     PetscWeakForm           wf;
5221     DMBoundaryConditionType type;
5222     DMLabel                 label;
5223     const PetscInt         *values;
5224     PetscInt                fieldI, numValues;
5225     PetscObject             obj;
5226     PetscClassId            id;
5227 
5228     PetscCall(PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &fieldI, NULL, NULL, NULL, NULL, NULL));
5229     PetscCall(PetscDSGetDiscretization(prob, fieldI, &obj));
5230     PetscCall(PetscObjectGetClassId(obj, &id));
5231     if ((id != PETSCFE_CLASSID) || (type & DM_BC_ESSENTIAL)) continue;
5232     PetscCall(DMPlexComputeBdJacobian_Single_Internal(dm, t, wf, label, numValues, values, fieldI, locX, locX_t, X_tShift, Jac, JacP, coordField, facetIS));
5233   }
5234   PetscCall(ISDestroy(&facetIS));
5235   PetscFunctionReturn(0);
5236 }
5237 
5238 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)
5239 {
5240   DM_Plex        *mesh  = (DM_Plex *) dm->data;
5241   const char     *name  = "Jacobian";
5242   DM              dmAux = NULL, plex, tdm;
5243   DMEnclosureType encAux;
5244   Vec             A, tv;
5245   DMField         coordField;
5246   PetscDS         prob, probAux = NULL;
5247   PetscSection    section, globalSection, sectionAux;
5248   PetscScalar    *elemMat, *elemMatP, *elemMatD, *u, *u_t, *a = NULL;
5249   const PetscInt *cells;
5250   PetscInt        Nf, fieldI, fieldJ;
5251   PetscInt        totDim, totDimAux, cStart, cEnd, numCells, c;
5252   PetscBool       hasJac, hasPrec, hasDyn, hasFV = PETSC_FALSE, transform;
5253 
5254   PetscFunctionBegin;
5255   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM,dm,0,0,0));
5256   PetscCall(ISGetLocalSize(cellIS, &numCells));
5257   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
5258   PetscCall(DMHasBasisTransform(dm, &transform));
5259   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
5260   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
5261   PetscCall(DMGetLocalSection(dm, &section));
5262   PetscCall(DMGetGlobalSection(dm, &globalSection));
5263   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob));
5264   PetscCall(PetscDSGetNumFields(prob, &Nf));
5265   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
5266   PetscCall(PetscDSHasJacobian(prob, &hasJac));
5267   PetscCall(PetscDSHasJacobianPreconditioner(prob, &hasPrec));
5268   /* user passed in the same matrix, avoid double contributions and
5269      only assemble the Jacobian */
5270   if (hasJac && Jac == JacP) hasPrec = PETSC_FALSE;
5271   PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
5272   hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
5273   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &A));
5274   if (A) {
5275     PetscCall(VecGetDM(A, &dmAux));
5276     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
5277     PetscCall(DMConvert(dmAux, DMPLEX, &plex));
5278     PetscCall(DMGetLocalSection(plex, &sectionAux));
5279     PetscCall(DMGetDS(dmAux, &probAux));
5280     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
5281   }
5282   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));
5283   if (dmAux) PetscCall(PetscMalloc1(numCells*totDimAux, &a));
5284   PetscCall(DMGetCoordinateField(dm, &coordField));
5285   for (c = cStart; c < cEnd; ++c) {
5286     const PetscInt cell = cells ? cells[c] : c;
5287     const PetscInt cind = c - cStart;
5288     PetscScalar   *x = NULL,  *x_t = NULL;
5289     PetscInt       i;
5290 
5291     PetscCall(DMPlexVecGetClosure(dm, section, X, cell, NULL, &x));
5292     for (i = 0; i < totDim; ++i) u[cind*totDim+i] = x[i];
5293     PetscCall(DMPlexVecRestoreClosure(dm, section, X, cell, NULL, &x));
5294     if (X_t) {
5295       PetscCall(DMPlexVecGetClosure(dm, section, X_t, cell, NULL, &x_t));
5296       for (i = 0; i < totDim; ++i) u_t[cind*totDim+i] = x_t[i];
5297       PetscCall(DMPlexVecRestoreClosure(dm, section, X_t, cell, NULL, &x_t));
5298     }
5299     if (dmAux) {
5300       PetscInt subcell;
5301       PetscCall(DMGetEnclosurePoint(dmAux, dm, encAux, cell, &subcell));
5302       PetscCall(DMPlexVecGetClosure(plex, sectionAux, A, subcell, NULL, &x));
5303       for (i = 0; i < totDimAux; ++i) a[cind*totDimAux+i] = x[i];
5304       PetscCall(DMPlexVecRestoreClosure(plex, sectionAux, A, subcell, NULL, &x));
5305     }
5306   }
5307   if (hasJac)  PetscCall(PetscArrayzero(elemMat,  numCells*totDim*totDim));
5308   if (hasPrec) PetscCall(PetscArrayzero(elemMatP, numCells*totDim*totDim));
5309   if (hasDyn)  PetscCall(PetscArrayzero(elemMatD, numCells*totDim*totDim));
5310   for (fieldI = 0; fieldI < Nf; ++fieldI) {
5311     PetscClassId    id;
5312     PetscFE         fe;
5313     PetscQuadrature qGeom = NULL;
5314     PetscInt        Nb;
5315     /* Conforming batches */
5316     PetscInt        numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
5317     /* Remainder */
5318     PetscInt        Nr, offset, Nq;
5319     PetscInt        maxDegree;
5320     PetscFEGeom     *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL;
5321 
5322     PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *) &fe));
5323     PetscCall(PetscObjectGetClassId((PetscObject) fe, &id));
5324     if (id == PETSCFV_CLASSID) {hasFV = PETSC_TRUE; continue;}
5325     PetscCall(PetscFEGetDimension(fe, &Nb));
5326     PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5327     PetscCall(DMFieldGetDegree(coordField,cellIS,NULL,&maxDegree));
5328     if (maxDegree <= 1) {
5329       PetscCall(DMFieldCreateDefaultQuadrature(coordField,cellIS,&qGeom));
5330     }
5331     if (!qGeom) {
5332       PetscCall(PetscFEGetQuadrature(fe,&qGeom));
5333       PetscCall(PetscObjectReference((PetscObject)qGeom));
5334     }
5335     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
5336     PetscCall(DMSNESGetFEGeom(coordField,cellIS,qGeom,PETSC_FALSE,&cgeomFEM));
5337     blockSize = Nb;
5338     batchSize = numBlocks * blockSize;
5339     PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5340     numChunks = numCells / (numBatches*batchSize);
5341     Ne        = numChunks*numBatches*batchSize;
5342     Nr        = numCells % (numBatches*batchSize);
5343     offset    = numCells - Nr;
5344     PetscCall(PetscFEGeomGetChunk(cgeomFEM,0,offset,&chunkGeom));
5345     PetscCall(PetscFEGeomGetChunk(cgeomFEM,offset,numCells,&remGeom));
5346     for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5347       key.field = fieldI*Nf+fieldJ;
5348       if (hasJac) {
5349         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat));
5350         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]));
5351       }
5352       if (hasPrec) {
5353         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatP));
5354         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]));
5355       }
5356       if (hasDyn) {
5357         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatD));
5358         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]));
5359       }
5360     }
5361     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM,offset,numCells,&remGeom));
5362     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM,0,offset,&chunkGeom));
5363     PetscCall(DMSNESRestoreFEGeom(coordField,cellIS,qGeom,PETSC_FALSE,&cgeomFEM));
5364     PetscCall(PetscQuadratureDestroy(&qGeom));
5365   }
5366   /*   Add contribution from X_t */
5367   if (hasDyn) {for (c = 0; c < numCells*totDim*totDim; ++c) elemMat[c] += X_tShift*elemMatD[c];}
5368   if (hasFV) {
5369     PetscClassId id;
5370     PetscFV      fv;
5371     PetscInt     offsetI, NcI, NbI = 1, fc, f;
5372 
5373     for (fieldI = 0; fieldI < Nf; ++fieldI) {
5374       PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *) &fv));
5375       PetscCall(PetscDSGetFieldOffset(prob, fieldI, &offsetI));
5376       PetscCall(PetscObjectGetClassId((PetscObject) fv, &id));
5377       if (id != PETSCFV_CLASSID) continue;
5378       /* Put in the identity */
5379       PetscCall(PetscFVGetNumComponents(fv, &NcI));
5380       for (c = cStart; c < cEnd; ++c) {
5381         const PetscInt cind    = c - cStart;
5382         const PetscInt eOffset = cind*totDim*totDim;
5383         for (fc = 0; fc < NcI; ++fc) {
5384           for (f = 0; f < NbI; ++f) {
5385             const PetscInt i = offsetI + f*NcI+fc;
5386             if (hasPrec) {
5387               if (hasJac) {elemMat[eOffset+i*totDim+i] = 1.0;}
5388               elemMatP[eOffset+i*totDim+i] = 1.0;
5389             } else {elemMat[eOffset+i*totDim+i] = 1.0;}
5390           }
5391         }
5392       }
5393     }
5394     /* No allocated space for FV stuff, so ignore the zero entries */
5395     PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE));
5396   }
5397   /* Insert values into matrix */
5398   for (c = cStart; c < cEnd; ++c) {
5399     const PetscInt cell = cells ? cells[c] : c;
5400     const PetscInt cind = c - cStart;
5401 
5402     /* Transform to global basis before insertion in Jacobian */
5403     if (transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, cell, PETSC_TRUE, totDim, &elemMat[cind*totDim*totDim]));
5404     if (hasPrec) {
5405       if (hasJac) {
5406         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind*totDim*totDim]));
5407         PetscCall(DMPlexMatSetClosure(dm, section, globalSection, Jac, cell, &elemMat[cind*totDim*totDim], ADD_VALUES));
5408       }
5409       if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatP[cind*totDim*totDim]));
5410       PetscCall(DMPlexMatSetClosure(dm, section, globalSection, JacP, cell, &elemMatP[cind*totDim*totDim], ADD_VALUES));
5411     } else {
5412       if (hasJac) {
5413         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind*totDim*totDim]));
5414         PetscCall(DMPlexMatSetClosure(dm, section, globalSection, JacP, cell, &elemMat[cind*totDim*totDim], ADD_VALUES));
5415       }
5416     }
5417   }
5418   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
5419   if (hasFV) PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE));
5420   PetscCall(PetscFree5(u,u_t,elemMat,elemMatP,elemMatD));
5421   if (dmAux) {
5422     PetscCall(PetscFree(a));
5423     PetscCall(DMDestroy(&plex));
5424   }
5425   /* Compute boundary integrals */
5426   PetscCall(DMPlexComputeBdJacobian_Internal(dm, X, X_t, t, X_tShift, Jac, JacP, user));
5427   /* Assemble matrix */
5428   if (hasJac && hasPrec) {
5429     PetscCall(MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY));
5430     PetscCall(MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY));
5431   }
5432   PetscCall(MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY));
5433   PetscCall(MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY));
5434   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM,dm,0,0,0));
5435   PetscFunctionReturn(0);
5436 }
5437 
5438 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)
5439 {
5440   DM_Plex         *mesh          = (DM_Plex *) dm->data;
5441   const char      *name          = "Hybrid Jacobian";
5442   DM               dmAux[3]      = {NULL, NULL, NULL};
5443   DMLabel          ghostLabel    = NULL;
5444   DM               plex          = NULL;
5445   DM               plexA         = NULL;
5446   PetscDS          ds            = NULL;
5447   PetscDS          dsAux[3]      = {NULL, NULL, NULL};
5448   Vec              locA[3]       = {NULL, NULL, NULL};
5449   PetscSection     section       = NULL;
5450   PetscSection     sectionAux[3] = {NULL, NULL, NULL};
5451   DMField          coordField    = NULL;
5452   PetscScalar     *u = NULL, *u_t, *a[3];
5453   PetscScalar     *elemMat, *elemMatP;
5454   PetscSection     globalSection;
5455   IS               chunkIS;
5456   const PetscInt  *cells;
5457   PetscInt        *faces;
5458   PetscInt         cStart, cEnd, numCells;
5459   PetscInt         Nf, fieldI, fieldJ, totDim, totDimAux[3], numChunks, cellChunkSize, chunk;
5460   PetscInt         maxDegree = PETSC_MAX_INT;
5461   PetscQuadrature  affineQuad = NULL, *quads = NULL;
5462   PetscFEGeom     *affineGeom = NULL, **geoms = NULL;
5463   PetscBool        hasBdJac, hasBdPrec;
5464 
5465   PetscFunctionBegin;
5466   if ((key[0].label == key[1].label) && (key[0].value == key[1].value) && (key[0].part == key[1].part)) {
5467     const char *name;
5468     PetscCall(PetscObjectGetName((PetscObject) key[0].label, &name));
5469     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);
5470   }
5471   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM,dm,0,0,0));
5472   PetscCall(ISGetLocalSize(cellIS, &numCells));
5473   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
5474   PetscCall(DMConvert(dm, DMPLEX, &plex));
5475   PetscCall(DMGetSection(dm, &section));
5476   PetscCall(DMGetGlobalSection(dm, &globalSection));
5477   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
5478   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds));
5479   PetscCall(PetscDSGetNumFields(ds, &Nf));
5480   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
5481   PetscCall(PetscDSHasBdJacobian(ds, &hasBdJac));
5482   PetscCall(PetscDSHasBdJacobianPreconditioner(ds, &hasBdPrec));
5483   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, key[2].value, key[2].part, &locA[2]));
5484   if (locA[2]) {
5485     const PetscInt cellStart = cells ? cells[cStart] : cStart;
5486 
5487     PetscCall(VecGetDM(locA[2], &dmAux[2]));
5488     PetscCall(DMConvert(dmAux[2], DMPLEX, &plexA));
5489     PetscCall(DMGetSection(dmAux[2], &sectionAux[2]));
5490     PetscCall(DMGetCellDS(dmAux[2], cellStart, &dsAux[2]));
5491     PetscCall(PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]));
5492     {
5493       const PetscInt *cone;
5494       PetscInt        c;
5495 
5496       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
5497       for (c = 0; c < 2; ++c) {
5498         const PetscInt *support;
5499         PetscInt ssize, s;
5500 
5501         PetscCall(DMPlexGetSupport(dm, cone[c], &support));
5502         PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
5503         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], cellStart, ssize);
5504         if      (support[0] == cellStart) s = 1;
5505         else if (support[1] == cellStart) s = 0;
5506         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
5507         PetscCall(DMGetAuxiliaryVec(dm, key[c].label, key[c].value, key[c].part, &locA[c]));
5508         if (locA[c]) PetscCall(VecGetDM(locA[c], &dmAux[c]));
5509         else         {dmAux[c] = dmAux[2];}
5510         PetscCall(DMGetCellDS(dmAux[c], support[s], &dsAux[c]));
5511         PetscCall(PetscDSGetTotalDimension(dsAux[c], &totDimAux[c]));
5512       }
5513     }
5514   }
5515   PetscCall(DMGetCoordinateField(dm, &coordField));
5516   PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
5517   if (maxDegree > 1) {
5518     PetscInt f;
5519     PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
5520     for (f = 0; f < Nf; ++f) {
5521       PetscFE fe;
5522 
5523       PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *) &fe));
5524       if (fe) {
5525         PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
5526         PetscCall(PetscObjectReference((PetscObject) quads[f]));
5527       }
5528     }
5529   }
5530   cellChunkSize = numCells;
5531   numChunks     = !numCells ? 0 : PetscCeilReal(((PetscReal) numCells)/cellChunkSize);
5532   PetscCall(PetscCalloc1(1*cellChunkSize, &faces));
5533   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 1*cellChunkSize, faces, PETSC_USE_POINTER, &chunkIS));
5534   PetscCall(DMPlexGetCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
5535   PetscCall(DMPlexGetHybridAuxFields(dm, dmAux, dsAux, cellIS, locA, a));
5536   PetscCall(DMGetWorkArray(dm, hasBdJac  ? cellChunkSize*totDim*totDim : 0, MPIU_SCALAR, &elemMat));
5537   PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize*totDim*totDim : 0, MPIU_SCALAR, &elemMatP));
5538   for (chunk = 0; chunk < numChunks; ++chunk) {
5539     PetscInt cS = cStart+chunk*cellChunkSize, cE = PetscMin(cS+cellChunkSize, cEnd), numCells = cE - cS, c;
5540 
5541     if (hasBdJac)  PetscCall(PetscMemzero(elemMat,  numCells*totDim*totDim * sizeof(PetscScalar)));
5542     if (hasBdPrec) PetscCall(PetscMemzero(elemMatP, numCells*totDim*totDim * sizeof(PetscScalar)));
5543     /* Get faces */
5544     for (c = cS; c < cE; ++c) {
5545       const PetscInt  cell = cells ? cells[c] : c;
5546       const PetscInt *cone;
5547       PetscCall(DMPlexGetCone(plex, cell, &cone));
5548       faces[0*cellChunkSize+(c-cS)] = cone[0];
5549       /*faces[2*cellChunkSize+(c-cS)] = cone[1];*/
5550     }
5551     PetscCall(ISGeneralSetIndices(chunkIS, 1*cellChunkSize, faces, PETSC_USE_POINTER));
5552     if (maxDegree <= 1) {
5553       if (!affineQuad) PetscCall(DMFieldCreateDefaultQuadrature(coordField, chunkIS, &affineQuad));
5554       if (affineQuad)  PetscCall(DMSNESGetFEGeom(coordField, chunkIS, affineQuad, PETSC_TRUE, &affineGeom));
5555     } else {
5556       PetscInt f;
5557       for (f = 0; f < Nf; ++f) {
5558         if (quads[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, quads[f], PETSC_TRUE, &geoms[f]));
5559       }
5560     }
5561 
5562     for (fieldI = 0; fieldI < Nf; ++fieldI) {
5563       PetscFE         feI;
5564       PetscFEGeom    *geom = affineGeom ? affineGeom : geoms[fieldI];
5565       PetscFEGeom    *chunkGeom = NULL, *remGeom = NULL;
5566       PetscQuadrature quad = affineQuad ? affineQuad : quads[fieldI];
5567       PetscInt        numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb;
5568       PetscBool       isCohesiveField;
5569 
5570       PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *) &feI));
5571       if (!feI) continue;
5572       PetscCall(PetscFEGetTileSizes(feI, NULL, &numBlocks, NULL, &numBatches));
5573       PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
5574       PetscCall(PetscFEGetDimension(feI, &Nb));
5575       blockSize = Nb;
5576       batchSize = numBlocks * blockSize;
5577       PetscCall(PetscFESetTileSizes(feI, blockSize, numBlocks, batchSize, numBatches));
5578       numChunks = numCells / (numBatches*batchSize);
5579       Ne        = numChunks*numBatches*batchSize;
5580       Nr        = numCells % (numBatches*batchSize);
5581       offset    = numCells - Nr;
5582       PetscCall(PetscFEGeomGetChunk(geom,0,offset,&chunkGeom));
5583       PetscCall(PetscFEGeomGetChunk(geom,offset,numCells,&remGeom));
5584       PetscCall(PetscDSGetCohesive(ds, fieldI, &isCohesiveField));
5585       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5586         PetscFE feJ;
5587 
5588         PetscCall(PetscDSGetDiscretization(ds, fieldJ, (PetscObject *) &feJ));
5589         if (!feJ) continue;
5590         key[0].field = fieldI*Nf+fieldJ;
5591         key[1].field = fieldI*Nf+fieldJ;
5592         key[2].field = fieldI*Nf+fieldJ;
5593         if (hasBdJac) {
5594           PetscCall(PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, X_tShift, elemMat));
5595           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]));
5596           PetscCall(PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, X_tShift, elemMat));
5597           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]));
5598         }
5599         if (hasBdPrec) {
5600           PetscCall(PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN_PRE, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, X_tShift, elemMatP));
5601           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]));
5602           PetscCall(PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN_PRE, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, X_tShift, elemMatP));
5603           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]));
5604         }
5605         if (hasBdJac) {
5606           PetscCall(PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, X_tShift, elemMat));
5607           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]));
5608         }
5609         if (hasBdPrec) {
5610           PetscCall(PetscFEIntegrateHybridJacobian(ds, PETSCFE_JACOBIAN_PRE, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, X_tShift, elemMatP));
5611           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]));
5612         }
5613       }
5614       PetscCall(PetscFEGeomRestoreChunk(geom,offset,numCells,&remGeom));
5615       PetscCall(PetscFEGeomRestoreChunk(geom,0,offset,&chunkGeom));
5616     }
5617     /* Insert values into matrix */
5618     for (c = cS; c < cE; ++c) {
5619       const PetscInt cell = cells ? cells[c] : c;
5620       const PetscInt cind = c - cS;
5621 
5622       if (hasBdPrec) {
5623         if (hasBdJac) {
5624           if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind*totDim*totDim]));
5625           PetscCall(DMPlexMatSetClosure(plex, section, globalSection, Jac, cell, &elemMat[cind*totDim*totDim], ADD_VALUES));
5626         }
5627         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatP[cind*totDim*totDim]));
5628         PetscCall(DMPlexMatSetClosure(plex, section, globalSection, JacP, cell, &elemMatP[cind*totDim*totDim], ADD_VALUES));
5629       } else if (hasBdJac) {
5630         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind*totDim*totDim]));
5631         PetscCall(DMPlexMatSetClosure(plex, section, globalSection, JacP, cell, &elemMat[cind*totDim*totDim], ADD_VALUES));
5632       }
5633     }
5634   }
5635   PetscCall(DMPlexRestoreCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
5636   PetscCall(DMPlexRestoreHybridAuxFields(dmAux, dsAux, cellIS, locA, a));
5637   PetscCall(DMRestoreWorkArray(dm, hasBdJac  ? cellChunkSize*totDim*totDim : 0, MPIU_SCALAR, &elemMat));
5638   PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize*totDim*totDim : 0, MPIU_SCALAR, &elemMatP));
5639   PetscCall(PetscFree(faces));
5640   PetscCall(ISDestroy(&chunkIS));
5641   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
5642   if (maxDegree <= 1) {
5643     PetscCall(DMSNESRestoreFEGeom(coordField,cellIS,affineQuad,PETSC_FALSE,&affineGeom));
5644     PetscCall(PetscQuadratureDestroy(&affineQuad));
5645   } else {
5646     PetscInt f;
5647     for (f = 0; f < Nf; ++f) {
5648       if (geoms) PetscCall(DMSNESRestoreFEGeom(coordField,cellIS,quads[f],PETSC_FALSE, &geoms[f]));
5649       if (quads) PetscCall(PetscQuadratureDestroy(&quads[f]));
5650     }
5651     PetscCall(PetscFree2(quads,geoms));
5652   }
5653   if (dmAux[2]) PetscCall(DMDestroy(&plexA));
5654   PetscCall(DMDestroy(&plex));
5655   /* Assemble matrix */
5656   if (hasBdJac && hasBdPrec) {
5657     PetscCall(MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY));
5658     PetscCall(MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY));
5659   }
5660   PetscCall(MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY));
5661   PetscCall(MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY));
5662   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM,dm,0,0,0));
5663   PetscFunctionReturn(0);
5664 }
5665 
5666 /*
5667   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.
5668 
5669   Input Parameters:
5670 + dm     - The mesh
5671 . key    - The PetscWeakFormKey indcating where integration should happen
5672 . cellIS - The cells to integrate over
5673 . t      - The time
5674 . X_tShift - The multiplier for the Jacobian with repsect to X_t
5675 . X      - Local solution vector
5676 . X_t    - Time-derivative of the local solution vector
5677 . Y      - Local input vector
5678 - user   - the user context
5679 
5680   Output Parameter:
5681 . Z - Local output vector
5682 
5683   Note:
5684   We form the residual one batch of elements at a time. This allows us to offload work onto an accelerator,
5685   like a GPU, or vectorize on a multicore machine.
5686 */
5687 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)
5688 {
5689   DM_Plex        *mesh  = (DM_Plex *) dm->data;
5690   const char     *name  = "Jacobian";
5691   DM              dmAux = NULL, plex, plexAux = NULL;
5692   DMEnclosureType encAux;
5693   Vec             A;
5694   DMField         coordField;
5695   PetscDS         prob, probAux = NULL;
5696   PetscQuadrature quad;
5697   PetscSection    section, globalSection, sectionAux;
5698   PetscScalar    *elemMat, *elemMatD, *u, *u_t, *a = NULL, *y, *z;
5699   const PetscInt *cells;
5700   PetscInt        Nf, fieldI, fieldJ;
5701   PetscInt        totDim, totDimAux = 0, cStart, cEnd, numCells, c;
5702   PetscBool       hasDyn;
5703 
5704   PetscFunctionBegin;
5705   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM,dm,0,0,0));
5706   PetscCall(DMConvert(dm, DMPLEX, &plex));
5707   if (!cellIS) {
5708     PetscInt depth;
5709 
5710     PetscCall(DMPlexGetDepth(plex, &depth));
5711     PetscCall(DMGetStratumIS(plex, "dim", depth, &cellIS));
5712     if (!cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, &cellIS));
5713   } else {
5714     PetscCall(PetscObjectReference((PetscObject) cellIS));
5715   }
5716   PetscCall(ISGetLocalSize(cellIS, &numCells));
5717   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
5718   PetscCall(DMGetLocalSection(dm, &section));
5719   PetscCall(DMGetGlobalSection(dm, &globalSection));
5720   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob));
5721   PetscCall(PetscDSGetNumFields(prob, &Nf));
5722   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
5723   PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
5724   hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
5725   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &A));
5726   if (A) {
5727     PetscCall(VecGetDM(A, &dmAux));
5728     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
5729     PetscCall(DMConvert(dmAux, DMPLEX, &plexAux));
5730     PetscCall(DMGetLocalSection(plexAux, &sectionAux));
5731     PetscCall(DMGetDS(dmAux, &probAux));
5732     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
5733   }
5734   PetscCall(VecSet(Z, 0.0));
5735   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));
5736   if (dmAux) PetscCall(PetscMalloc1(numCells*totDimAux, &a));
5737   PetscCall(DMGetCoordinateField(dm, &coordField));
5738   for (c = cStart; c < cEnd; ++c) {
5739     const PetscInt cell = cells ? cells[c] : c;
5740     const PetscInt cind = c - cStart;
5741     PetscScalar   *x = NULL,  *x_t = NULL;
5742     PetscInt       i;
5743 
5744     PetscCall(DMPlexVecGetClosure(plex, section, X, cell, NULL, &x));
5745     for (i = 0; i < totDim; ++i) u[cind*totDim+i] = x[i];
5746     PetscCall(DMPlexVecRestoreClosure(plex, section, X, cell, NULL, &x));
5747     if (X_t) {
5748       PetscCall(DMPlexVecGetClosure(plex, section, X_t, cell, NULL, &x_t));
5749       for (i = 0; i < totDim; ++i) u_t[cind*totDim+i] = x_t[i];
5750       PetscCall(DMPlexVecRestoreClosure(plex, section, X_t, cell, NULL, &x_t));
5751     }
5752     if (dmAux) {
5753       PetscInt subcell;
5754       PetscCall(DMGetEnclosurePoint(dmAux, dm, encAux, cell, &subcell));
5755       PetscCall(DMPlexVecGetClosure(plexAux, sectionAux, A, subcell, NULL, &x));
5756       for (i = 0; i < totDimAux; ++i) a[cind*totDimAux+i] = x[i];
5757       PetscCall(DMPlexVecRestoreClosure(plexAux, sectionAux, A, subcell, NULL, &x));
5758     }
5759     PetscCall(DMPlexVecGetClosure(plex, section, Y, cell, NULL, &x));
5760     for (i = 0; i < totDim; ++i) y[cind*totDim+i] = x[i];
5761     PetscCall(DMPlexVecRestoreClosure(plex, section, Y, cell, NULL, &x));
5762   }
5763   PetscCall(PetscArrayzero(elemMat, numCells*totDim*totDim));
5764   if (hasDyn)  PetscCall(PetscArrayzero(elemMatD, numCells*totDim*totDim));
5765   for (fieldI = 0; fieldI < Nf; ++fieldI) {
5766     PetscFE  fe;
5767     PetscInt Nb;
5768     /* Conforming batches */
5769     PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
5770     /* Remainder */
5771     PetscInt Nr, offset, Nq;
5772     PetscQuadrature qGeom = NULL;
5773     PetscInt    maxDegree;
5774     PetscFEGeom *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL;
5775 
5776     PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *) &fe));
5777     PetscCall(PetscFEGetQuadrature(fe, &quad));
5778     PetscCall(PetscFEGetDimension(fe, &Nb));
5779     PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5780     PetscCall(DMFieldGetDegree(coordField,cellIS,NULL,&maxDegree));
5781     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField,cellIS,&qGeom));
5782     if (!qGeom) {
5783       PetscCall(PetscFEGetQuadrature(fe,&qGeom));
5784       PetscCall(PetscObjectReference((PetscObject)qGeom));
5785     }
5786     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
5787     PetscCall(DMSNESGetFEGeom(coordField,cellIS,qGeom,PETSC_FALSE,&cgeomFEM));
5788     blockSize = Nb;
5789     batchSize = numBlocks * blockSize;
5790     PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5791     numChunks = numCells / (numBatches*batchSize);
5792     Ne        = numChunks*numBatches*batchSize;
5793     Nr        = numCells % (numBatches*batchSize);
5794     offset    = numCells - Nr;
5795     PetscCall(PetscFEGeomGetChunk(cgeomFEM,0,offset,&chunkGeom));
5796     PetscCall(PetscFEGeomGetChunk(cgeomFEM,offset,numCells,&remGeom));
5797     for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5798       key.field = fieldI*Nf + fieldJ;
5799       PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat));
5800       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]));
5801       if (hasDyn) {
5802         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatD));
5803         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]));
5804       }
5805     }
5806     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM,offset,numCells,&remGeom));
5807     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM,0,offset,&chunkGeom));
5808     PetscCall(DMSNESRestoreFEGeom(coordField,cellIS,qGeom,PETSC_FALSE,&cgeomFEM));
5809     PetscCall(PetscQuadratureDestroy(&qGeom));
5810   }
5811   if (hasDyn) {
5812     for (c = 0; c < numCells*totDim*totDim; ++c) elemMat[c] += X_tShift*elemMatD[c];
5813   }
5814   for (c = cStart; c < cEnd; ++c) {
5815     const PetscInt     cell = cells ? cells[c] : c;
5816     const PetscInt     cind = c - cStart;
5817     const PetscBLASInt M = totDim, one = 1;
5818     const PetscScalar  a = 1.0, b = 0.0;
5819 
5820     PetscStackCallBLAS("BLASgemv", BLASgemv_("N", &M, &M, &a, &elemMat[cind*totDim*totDim], &M, &y[cind*totDim], &one, &b, z, &one));
5821     if (mesh->printFEM > 1) {
5822       PetscCall(DMPrintCellMatrix(c, name, totDim, totDim, &elemMat[cind*totDim*totDim]));
5823       PetscCall(DMPrintCellVector(c, "Y",  totDim, &y[cind*totDim]));
5824       PetscCall(DMPrintCellVector(c, "Z",  totDim, z));
5825     }
5826     PetscCall(DMPlexVecSetClosure(dm, section, Z, cell, z, ADD_VALUES));
5827   }
5828   PetscCall(PetscFree6(u,u_t,elemMat,elemMatD,y,z));
5829   if (mesh->printFEM) {
5830     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)Z), "Z:\n"));
5831     PetscCall(VecView(Z, NULL));
5832   }
5833   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
5834   PetscCall(PetscFree(a));
5835   PetscCall(ISDestroy(&cellIS));
5836   PetscCall(DMDestroy(&plexAux));
5837   PetscCall(DMDestroy(&plex));
5838   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM,dm,0,0,0));
5839   PetscFunctionReturn(0);
5840 }
5841