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