xref: /petsc/src/dm/impls/plex/plexproject.c (revision 476787b73747423b72d8c77dc2f682bd229b3ce2)
1 #include <petsc/private/dmpleximpl.h> /*I      "petscdmplex.h"   I*/
2 
3 #include <petsc/private/petscfeimpl.h>
4 
5 /*@
6   DMPlexGetActivePoint - Get the point on which projection is currently working
7 
8   Not Collective
9 
10   Input Parameter:
11 . dm - the `DM`
12 
13   Output Parameter:
14 . point - The mesh point involved in the current projection
15 
16   Level: developer
17 
18 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetActivePoint()`
19 @*/
20 PetscErrorCode DMPlexGetActivePoint(DM dm, PetscInt *point)
21 {
22   PetscFunctionBeginHot;
23   *point = ((DM_Plex *)dm->data)->activePoint;
24   PetscFunctionReturn(PETSC_SUCCESS);
25 }
26 
27 /*@
28   DMPlexSetActivePoint - Set the point on which projection is currently working
29 
30   Not Collective
31 
32   Input Parameters:
33 + dm    - the `DM`
34 - point - The mesh point involved in the current projection
35 
36   Level: developer
37 
38 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetActivePoint()`
39 @*/
40 PetscErrorCode DMPlexSetActivePoint(DM dm, PetscInt point)
41 {
42   PetscFunctionBeginHot;
43   ((DM_Plex *)dm->data)->activePoint = point;
44   PetscFunctionReturn(PETSC_SUCCESS);
45 }
46 
47 /*
48   DMProjectPoint_Func_Private - Interpolate the given function in the output basis on the given point
49 
50   Input Parameters:
51 + dm     - The output `DM`
52 . ds     - The output `DS`
53 . dmIn   - The input `DM`
54 . dsIn   - The input `DS`
55 . time   - The time for this evaluation
56 . fegeom - The FE geometry for this point
57 . fvgeom - The FV geometry for this point
58 . isFE   - Flag indicating whether each output field has an FE discretization
59 . sp     - The output `PetscDualSpace` for each field
60 . funcs  - The evaluation function for each field
61 - ctxs   - The user context for each field
62 
63   Output Parameter:
64 . values - The value for each dual basis vector in the output dual space
65 
66   Level: developer
67 
68 .seealso:[](ch_unstructured), `DM`, `DMPLEX`, `PetscDS`, `PetscFEGeom`, `PetscFVCellGeom`, `PetscDualSpace`
69 */
70 static PetscErrorCode DMProjectPoint_Func_Private(DM dm, PetscDS ds, DM dmIn, PetscDS dsIn, PetscReal time, PetscFEGeom *fegeom, PetscFVCellGeom *fvgeom, PetscBool isFE[], PetscDualSpace sp[], PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, PetscScalar values[])
71 {
72   PetscInt  coordDim, Nf, *Nc, f, spDim, d, v, tp;
73   PetscBool isAffine, isCohesive, transform;
74 
75   PetscFunctionBeginHot;
76   PetscCall(DMGetCoordinateDim(dmIn, &coordDim));
77   PetscCall(DMHasBasisTransform(dmIn, &transform));
78   PetscCall(PetscDSGetNumFields(ds, &Nf));
79   PetscCall(PetscDSGetComponents(ds, &Nc));
80   PetscCall(PetscDSIsCohesive(ds, &isCohesive));
81   /* Get values for closure */
82   isAffine = fegeom->isAffine;
83   for (f = 0, v = 0, tp = 0; f < Nf; ++f) {
84     void *const ctx = ctxs ? ctxs[f] : NULL;
85     PetscBool   cohesive;
86 
87     if (!sp[f]) continue;
88     PetscCall(PetscDSGetCohesive(ds, f, &cohesive));
89     PetscCall(PetscDualSpaceGetDimension(sp[f], &spDim));
90     if (funcs[f]) {
91       if (isFE[f]) {
92         PetscQuadrature  allPoints;
93         PetscInt         q, dim, numPoints;
94         const PetscReal *points;
95         PetscScalar     *pointEval;
96         PetscReal       *x;
97         DM               rdm;
98 
99         PetscCall(PetscDualSpaceGetDM(sp[f], &rdm));
100         PetscCall(PetscDualSpaceGetAllData(sp[f], &allPoints, NULL));
101         PetscCall(PetscQuadratureGetData(allPoints, &dim, NULL, &numPoints, &points, NULL));
102         PetscCall(DMGetWorkArray(rdm, numPoints * Nc[f], MPIU_SCALAR, &pointEval));
103         PetscCall(DMGetWorkArray(rdm, coordDim, MPIU_REAL, &x));
104         PetscCall(PetscArrayzero(pointEval, numPoints * Nc[f]));
105         for (q = 0; q < numPoints; q++, tp++) {
106           const PetscReal *v0;
107 
108           if (isAffine) {
109             const PetscReal *refpoint    = &points[q * dim];
110             PetscReal        injpoint[3] = {0., 0., 0.};
111 
112             if (dim != fegeom->dim) {
113               if (isCohesive) {
114                 /* We just need to inject into the higher dimensional space assuming the last dimension is collapsed */
115                 for (d = 0; d < dim; ++d) injpoint[d] = refpoint[d];
116                 refpoint = injpoint;
117               } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Reference spatial dimension %" PetscInt_FMT " != %" PetscInt_FMT " dual basis spatial dimension", fegeom->dim, dim);
118             }
119             CoordinatesRefToReal(coordDim, fegeom->dim, fegeom->xi, fegeom->v, fegeom->J, refpoint, x);
120             v0 = x;
121           } else {
122             v0 = &fegeom->v[tp * coordDim];
123           }
124           if (transform) {
125             PetscCall(DMPlexBasisTransformApplyReal_Internal(dmIn, v0, PETSC_TRUE, coordDim, v0, x, dm->transformCtx));
126             v0 = x;
127           }
128           PetscCall((*funcs[f])(coordDim, time, v0, Nc[f], &pointEval[Nc[f] * q], ctx));
129         }
130         /* Transform point evaluations pointEval[q,c] */
131         PetscCall(PetscDualSpacePullback(sp[f], fegeom, numPoints, Nc[f], pointEval));
132         PetscCall(PetscDualSpaceApplyAll(sp[f], pointEval, &values[v]));
133         PetscCall(DMRestoreWorkArray(rdm, coordDim, MPIU_REAL, &x));
134         PetscCall(DMRestoreWorkArray(rdm, numPoints * Nc[f], MPIU_SCALAR, &pointEval));
135         v += spDim;
136         if (isCohesive && !cohesive) {
137           for (d = 0; d < spDim; d++, v++) values[v] = values[v - spDim];
138         }
139       } else {
140         for (d = 0; d < spDim; ++d, ++v) PetscCall(PetscDualSpaceApplyFVM(sp[f], d, time, fvgeom, Nc[f], funcs[f], ctx, &values[v]));
141       }
142     } else {
143       for (d = 0; d < spDim; d++, v++) values[v] = 0.;
144       if (isCohesive && !cohesive) {
145         for (d = 0; d < spDim; d++, v++) values[v] = 0.;
146       }
147     }
148   }
149   PetscFunctionReturn(PETSC_SUCCESS);
150 }
151 
152 /*
153   DMProjectPoint_Field_Private - Interpolate a function of the given field, in the input basis, using the output basis on the given point
154 
155   Input Parameters:
156 + dm             - The output DM
157 . ds             - The output DS
158 . dmIn           - The input DM
159 . dsIn           - The input DS
160 . dmAux          - The auxiliary DM, which is always for the input space
161 . dsAux          - The auxiliary DS, which is always for the input space
162 . time           - The time for this evaluation
163 . localU         - The local solution
164 . localA         - The local auziliary fields
165 . cgeom          - The FE geometry for this point
166 . sp             - The output PetscDualSpace for each field
167 . p              - The point in the output DM
168 . T              - Input basis and derivatives for each field tabulated on the quadrature points
169 . TAux           - Auxiliary basis and derivatives for each aux field tabulated on the quadrature points
170 . funcs          - The evaluation function for each field
171 - ctxs           - The user context for each field
172 
173   Output Parameter:
174 . values         - The value for each dual basis vector in the output dual space
175 
176   Level: developer
177 
178   Note:
179   Not supported for FV
180 
181 .seealso: `DMProjectPoint_Field_Private()`
182 */
183 static PetscErrorCode DMProjectPoint_Field_Private(DM dm, PetscDS ds, DM dmIn, DMEnclosureType encIn, PetscDS dsIn, DM dmAux, DMEnclosureType encAux, PetscDS dsAux, PetscReal time, Vec localU, Vec localA, PetscFEGeom *cgeom, PetscDualSpace sp[], PetscInt p, PetscTabulation *T, PetscTabulation *TAux, 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[]), void **ctxs, PetscScalar values[])
184 {
185   PetscSection       section, sectionAux = NULL;
186   PetscScalar       *u, *u_t = NULL, *u_x, *a = NULL, *a_t = NULL, *a_x = NULL, *bc;
187   PetscScalar       *coefficients = NULL, *coefficientsAux = NULL;
188   PetscScalar       *coefficients_t = NULL, *coefficientsAux_t = NULL;
189   const PetscScalar *constants;
190   PetscReal         *x;
191   PetscInt          *uOff, *uOff_x, *aOff = NULL, *aOff_x = NULL, *Nc, face[2];
192   PetscFEGeom        fegeom;
193   const PetscInt     dE = cgeom->dimEmbed, *cone, *ornt;
194   PetscInt           numConstants, Nf, NfIn, NfAux = 0, f, spDim, d, v, inp, tp = 0;
195   PetscBool          isAffine, isCohesive, isCohesiveIn, transform;
196   DMPolytopeType     qct;
197 
198   PetscFunctionBeginHot;
199   PetscCall(PetscDSGetNumFields(ds, &Nf));
200   PetscCall(PetscDSGetComponents(ds, &Nc));
201   PetscCall(PetscDSIsCohesive(ds, &isCohesive));
202   PetscCall(PetscDSGetNumFields(dsIn, &NfIn));
203   PetscCall(PetscDSIsCohesive(dsIn, &isCohesiveIn));
204   PetscCall(PetscDSGetComponentOffsets(dsIn, &uOff));
205   PetscCall(PetscDSGetComponentDerivativeOffsets(dsIn, &uOff_x));
206   PetscCall(PetscDSGetEvaluationArrays(dsIn, &u, &bc /*&u_t*/, &u_x));
207   PetscCall(PetscDSGetWorkspace(dsIn, &x, NULL, NULL, NULL, NULL));
208   PetscCall(PetscDSGetConstants(dsIn, &numConstants, &constants));
209   PetscCall(DMHasBasisTransform(dmIn, &transform));
210   PetscCall(DMGetLocalSection(dmIn, &section));
211   PetscCall(DMGetEnclosurePoint(dmIn, dm, encIn, p, &inp));
212   // Get cohesive cell hanging off face
213   if (isCohesiveIn) {
214     PetscCall(DMPlexGetCellType(dmIn, inp, &qct));
215     if ((qct != DM_POLYTOPE_POINT_PRISM_TENSOR) && (qct != DM_POLYTOPE_SEG_PRISM_TENSOR) && (qct != DM_POLYTOPE_TRI_PRISM_TENSOR) && (qct != DM_POLYTOPE_QUAD_PRISM_TENSOR)) {
216       DMPolytopeType  ct;
217       const PetscInt *support;
218       PetscInt        Ns, s;
219 
220       PetscCall(DMPlexGetSupport(dmIn, inp, &support));
221       PetscCall(DMPlexGetSupportSize(dmIn, inp, &Ns));
222       for (s = 0; s < Ns; ++s) {
223         PetscCall(DMPlexGetCellType(dmIn, support[s], &ct));
224         if ((ct == DM_POLYTOPE_POINT_PRISM_TENSOR) || (ct == DM_POLYTOPE_SEG_PRISM_TENSOR) || (ct == DM_POLYTOPE_TRI_PRISM_TENSOR) || (ct == DM_POLYTOPE_QUAD_PRISM_TENSOR)) {
225           inp = support[s];
226           break;
227         }
228       }
229       PetscCheck(s < Ns, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cohesive cell not found from face %" PetscInt_FMT, inp);
230       PetscCall(PetscDSGetComponentOffsetsCohesive(dsIn, 2, &uOff));
231       PetscCall(DMPlexGetOrientedCone(dmIn, inp, &cone, &ornt));
232       face[0] = 0;
233       face[1] = 0;
234     }
235   }
236   if (localU) PetscCall(DMPlexVecGetClosure(dmIn, section, localU, inp, NULL, &coefficients));
237   if (dmAux) {
238     PetscInt subp;
239 
240     PetscCall(DMGetEnclosurePoint(dmAux, dm, encAux, p, &subp));
241     PetscCall(PetscDSGetNumFields(dsAux, &NfAux));
242     PetscCall(DMGetLocalSection(dmAux, &sectionAux));
243     PetscCall(PetscDSGetComponentOffsets(dsAux, &aOff));
244     PetscCall(PetscDSGetComponentDerivativeOffsets(dsAux, &aOff_x));
245     PetscCall(PetscDSGetEvaluationArrays(dsAux, &a, NULL /*&a_t*/, &a_x));
246     PetscCall(DMPlexVecGetClosure(dmAux, sectionAux, localA, subp, NULL, &coefficientsAux));
247   }
248   /* Get values for closure */
249   isAffine        = cgeom->isAffine;
250   fegeom.dim      = cgeom->dim;
251   fegeom.dimEmbed = cgeom->dimEmbed;
252   if (isAffine) {
253     fegeom.v    = x;
254     fegeom.xi   = cgeom->xi;
255     fegeom.J    = cgeom->J;
256     fegeom.invJ = cgeom->invJ;
257     fegeom.detJ = cgeom->detJ;
258   }
259   for (f = 0, v = 0; f < Nf; ++f) {
260     PetscQuadrature  allPoints;
261     PetscInt         q, dim, numPoints;
262     const PetscReal *points;
263     PetscScalar     *pointEval;
264     PetscBool        cohesive;
265     DM               dm;
266 
267     if (!sp[f]) continue;
268     PetscCall(PetscDSGetCohesive(ds, f, &cohesive));
269     PetscCall(PetscDualSpaceGetDimension(sp[f], &spDim));
270     if (!funcs[f]) {
271       for (d = 0; d < spDim; d++, v++) values[v] = 0.;
272       if (isCohesive && !cohesive) {
273         for (d = 0; d < spDim; d++, v++) values[v] = 0.;
274       }
275       continue;
276     }
277     PetscCall(PetscDualSpaceGetDM(sp[f], &dm));
278     PetscCall(PetscDualSpaceGetAllData(sp[f], &allPoints, NULL));
279     PetscCall(PetscQuadratureGetData(allPoints, &dim, NULL, &numPoints, &points, NULL));
280     PetscCall(DMGetWorkArray(dm, numPoints * Nc[f], MPIU_SCALAR, &pointEval));
281     for (q = 0; q < numPoints; ++q, ++tp) {
282       PetscInt qpt[2];
283 
284       if (isCohesiveIn) {
285         PetscCall(PetscDSPermuteQuadPoint(dsIn, ornt[0], f, q, &qpt[0]));
286         PetscCall(PetscDSPermuteQuadPoint(dsIn, DMPolytopeTypeComposeOrientationInv(qct, ornt[1], 0), f, q, &qpt[1]));
287       }
288       if (isAffine) {
289         CoordinatesRefToReal(dE, cgeom->dim, fegeom.xi, cgeom->v, fegeom.J, &points[q * dim], x);
290       } else {
291         fegeom.v    = &cgeom->v[tp * dE];
292         fegeom.J    = &cgeom->J[tp * dE * dE];
293         fegeom.invJ = &cgeom->invJ[tp * dE * dE];
294         fegeom.detJ = &cgeom->detJ[tp];
295       }
296       if (coefficients) {
297         if (isCohesiveIn) PetscCall(PetscFEEvaluateFieldJets_Hybrid_Internal(dsIn, NfIn, 0, tp, T, face, qpt, T, &fegeom, coefficients, coefficients_t, u, u_x, u_t));
298         else PetscCall(PetscFEEvaluateFieldJets_Internal(dsIn, NfIn, 0, tp, T, &fegeom, coefficients, coefficients_t, u, u_x, u_t));
299       }
300       if (dsAux) PetscCall(PetscFEEvaluateFieldJets_Internal(dsAux, NfAux, 0, tp, TAux, &fegeom, coefficientsAux, coefficientsAux_t, a, a_x, a_t));
301       if (transform) PetscCall(DMPlexBasisTransformApplyReal_Internal(dmIn, fegeom.v, PETSC_TRUE, dE, fegeom.v, fegeom.v, dm->transformCtx));
302       (*funcs[f])(dE, NfIn, NfAux, uOff, uOff_x, u, u_t, u_x, aOff, aOff_x, a, a_t, a_x, time, fegeom.v, numConstants, constants, &pointEval[Nc[f] * q]);
303     }
304     PetscCall(PetscDualSpaceApplyAll(sp[f], pointEval, &values[v]));
305     PetscCall(DMRestoreWorkArray(dm, numPoints * Nc[f], MPIU_SCALAR, &pointEval));
306     v += spDim;
307     /* TODO: For now, set both sides equal, but this should use info from other support cell */
308     if (isCohesive && !cohesive) {
309       for (d = 0; d < spDim; d++, v++) values[v] = values[v - spDim];
310     }
311   }
312   if (localU) PetscCall(DMPlexVecRestoreClosure(dmIn, section, localU, inp, NULL, &coefficients));
313   if (dmAux) PetscCall(DMPlexVecRestoreClosure(dmAux, sectionAux, localA, p, NULL, &coefficientsAux));
314   if (isCohesiveIn) PetscCall(DMPlexRestoreOrientedCone(dmIn, inp, &cone, &ornt));
315   PetscFunctionReturn(PETSC_SUCCESS);
316 }
317 
318 static PetscErrorCode DMProjectPoint_BdField_Private(DM dm, PetscDS ds, DM dmIn, PetscDS dsIn, DM dmAux, DMEnclosureType encAux, PetscDS dsAux, PetscReal time, Vec localU, Vec localA, PetscFEGeom *fgeom, PetscDualSpace sp[], PetscInt p, PetscTabulation *T, PetscTabulation *TAux, 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[]), void **ctxs, PetscScalar values[])
319 {
320   PetscSection       section, sectionAux = NULL;
321   PetscScalar       *u, *u_t = NULL, *u_x, *a = NULL, *a_t = NULL, *a_x = NULL, *bc;
322   PetscScalar       *coefficients = NULL, *coefficientsAux = NULL;
323   PetscScalar       *coefficients_t = NULL, *coefficientsAux_t = NULL;
324   const PetscScalar *constants;
325   PetscReal         *x;
326   PetscInt          *uOff, *uOff_x, *aOff = NULL, *aOff_x = NULL, *Nc;
327   PetscFEGeom        fegeom, cgeom;
328   const PetscInt     dE = fgeom->dimEmbed;
329   PetscInt           numConstants, Nf, NfAux = 0, f, spDim, d, v, tp = 0;
330   PetscBool          isAffine;
331 
332   PetscFunctionBeginHot;
333   PetscCheck(dm == dmIn, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Not yet upgraded to use different input DM");
334   PetscCall(PetscDSGetNumFields(ds, &Nf));
335   PetscCall(PetscDSGetComponents(ds, &Nc));
336   PetscCall(PetscDSGetComponentOffsets(ds, &uOff));
337   PetscCall(PetscDSGetComponentDerivativeOffsets(ds, &uOff_x));
338   PetscCall(PetscDSGetEvaluationArrays(ds, &u, &bc /*&u_t*/, &u_x));
339   PetscCall(PetscDSGetWorkspace(ds, &x, NULL, NULL, NULL, NULL));
340   PetscCall(PetscDSGetConstants(ds, &numConstants, &constants));
341   PetscCall(DMGetLocalSection(dm, &section));
342   PetscCall(DMPlexVecGetClosure(dmIn, section, localU, p, NULL, &coefficients));
343   if (dmAux) {
344     PetscInt subp;
345 
346     PetscCall(DMGetEnclosurePoint(dmAux, dm, encAux, p, &subp));
347     PetscCall(PetscDSGetNumFields(dsAux, &NfAux));
348     PetscCall(DMGetLocalSection(dmAux, &sectionAux));
349     PetscCall(PetscDSGetComponentOffsets(dsAux, &aOff));
350     PetscCall(PetscDSGetComponentDerivativeOffsets(dsAux, &aOff_x));
351     PetscCall(PetscDSGetEvaluationArrays(dsAux, &a, NULL /*&a_t*/, &a_x));
352     PetscCall(DMPlexVecGetClosure(dmAux, sectionAux, localA, subp, NULL, &coefficientsAux));
353   }
354   /* Get values for closure */
355   isAffine       = fgeom->isAffine;
356   fegeom.n       = NULL;
357   fegeom.J       = NULL;
358   fegeom.v       = NULL;
359   fegeom.xi      = NULL;
360   cgeom.dim      = fgeom->dim;
361   cgeom.dimEmbed = fgeom->dimEmbed;
362   if (isAffine) {
363     fegeom.v    = x;
364     fegeom.xi   = fgeom->xi;
365     fegeom.J    = fgeom->J;
366     fegeom.invJ = fgeom->invJ;
367     fegeom.detJ = fgeom->detJ;
368     fegeom.n    = fgeom->n;
369 
370     cgeom.J    = fgeom->suppJ[0];
371     cgeom.invJ = fgeom->suppInvJ[0];
372     cgeom.detJ = fgeom->suppDetJ[0];
373   }
374   for (f = 0, v = 0; f < Nf; ++f) {
375     PetscQuadrature  allPoints;
376     PetscInt         q, dim, numPoints;
377     const PetscReal *points;
378     PetscScalar     *pointEval;
379     DM               dm;
380 
381     if (!sp[f]) continue;
382     PetscCall(PetscDualSpaceGetDimension(sp[f], &spDim));
383     if (!funcs[f]) {
384       for (d = 0; d < spDim; d++, v++) values[v] = 0.;
385       continue;
386     }
387     PetscCall(PetscDualSpaceGetDM(sp[f], &dm));
388     PetscCall(PetscDualSpaceGetAllData(sp[f], &allPoints, NULL));
389     PetscCall(PetscQuadratureGetData(allPoints, &dim, NULL, &numPoints, &points, NULL));
390     PetscCall(DMGetWorkArray(dm, numPoints * Nc[f], MPIU_SCALAR, &pointEval));
391     for (q = 0; q < numPoints; ++q, ++tp) {
392       if (isAffine) {
393         CoordinatesRefToReal(dE, fgeom->dim, fegeom.xi, fgeom->v, fegeom.J, &points[q * dim], x);
394       } else {
395         fegeom.v    = &fgeom->v[tp * dE];
396         fegeom.J    = &fgeom->J[tp * dE * dE];
397         fegeom.invJ = &fgeom->invJ[tp * dE * dE];
398         fegeom.detJ = &fgeom->detJ[tp];
399         fegeom.n    = &fgeom->n[tp * dE];
400 
401         cgeom.J    = &fgeom->suppJ[0][tp * dE * dE];
402         cgeom.invJ = &fgeom->suppInvJ[0][tp * dE * dE];
403         cgeom.detJ = &fgeom->suppDetJ[0][tp];
404       }
405       /* TODO We should use cgeom here, instead of fegeom, however the geometry coming in through fgeom does not have the support cell geometry */
406       PetscCall(PetscFEEvaluateFieldJets_Internal(ds, Nf, 0, tp, T, &cgeom, coefficients, coefficients_t, u, u_x, u_t));
407       if (dsAux) PetscCall(PetscFEEvaluateFieldJets_Internal(dsAux, NfAux, 0, tp, TAux, &cgeom, coefficientsAux, coefficientsAux_t, a, a_x, a_t));
408       (*funcs[f])(dE, Nf, NfAux, uOff, uOff_x, u, u_t, u_x, aOff, aOff_x, a, a_t, a_x, time, fegeom.v, fegeom.n, numConstants, constants, &pointEval[Nc[f] * q]);
409     }
410     PetscCall(PetscDualSpaceApplyAll(sp[f], pointEval, &values[v]));
411     PetscCall(DMRestoreWorkArray(dm, numPoints * Nc[f], MPIU_SCALAR, &pointEval));
412     v += spDim;
413   }
414   PetscCall(DMPlexVecRestoreClosure(dmIn, section, localU, p, NULL, &coefficients));
415   if (dmAux) PetscCall(DMPlexVecRestoreClosure(dmAux, sectionAux, localA, p, NULL, &coefficientsAux));
416   PetscFunctionReturn(PETSC_SUCCESS);
417 }
418 
419 static PetscErrorCode DMProjectPoint_Private(DM dm, PetscDS ds, DM dmIn, DMEnclosureType encIn, PetscDS dsIn, DM dmAux, DMEnclosureType encAux, PetscDS dsAux, PetscFEGeom *fegeom, PetscInt effectiveHeight, PetscReal time, Vec localU, Vec localA, PetscBool hasFE, PetscBool hasFV, PetscBool isFE[], PetscDualSpace sp[], PetscInt p, PetscTabulation *T, PetscTabulation *TAux, DMBoundaryConditionType type, void (**funcs)(void), void **ctxs, PetscBool fieldActive[], PetscScalar values[])
420 {
421   PetscFVCellGeom fvgeom;
422   PetscInt        dim, dimEmbed;
423 
424   PetscFunctionBeginHot;
425   PetscCall(DMGetDimension(dm, &dim));
426   PetscCall(DMGetCoordinateDim(dm, &dimEmbed));
427   if (hasFV) PetscCall(DMPlexComputeCellGeometryFVM(dm, p, &fvgeom.volume, fvgeom.centroid, NULL));
428   switch (type) {
429   case DM_BC_ESSENTIAL:
430   case DM_BC_NATURAL:
431     PetscCall(DMProjectPoint_Func_Private(dm, ds, dmIn, dsIn, time, fegeom, &fvgeom, isFE, sp, (PetscErrorCode(**)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *))funcs, ctxs, values));
432     break;
433   case DM_BC_ESSENTIAL_FIELD:
434   case DM_BC_NATURAL_FIELD:
435     PetscCall(DMProjectPoint_Field_Private(dm, ds, dmIn, encIn, dsIn, dmAux, encAux, dsAux, time, localU, localA, fegeom, sp, p, T, TAux, (void (**)(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[]))funcs, ctxs, values));
436     break;
437   case DM_BC_ESSENTIAL_BD_FIELD:
438     PetscCall(DMProjectPoint_BdField_Private(dm, ds, dmIn, dsIn, dmAux, encAux, dsAux, time, localU, localA, fegeom, sp, p, T, TAux, (void (**)(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[]))funcs, ctxs, values));
439     break;
440   default:
441     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown boundary condition type: %d", (int)type);
442   }
443   PetscFunctionReturn(PETSC_SUCCESS);
444 }
445 
446 static PetscErrorCode PetscDualSpaceGetAllPointsUnion(PetscInt Nf, PetscDualSpace *sp, PetscInt dim, void (**funcs)(void), PetscQuadrature *allPoints)
447 {
448   PetscReal *points;
449   PetscInt   f, numPoints;
450 
451   PetscFunctionBegin;
452   if (!dim) {
453     PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, allPoints));
454     PetscFunctionReturn(PETSC_SUCCESS);
455   }
456   numPoints = 0;
457   for (f = 0; f < Nf; ++f) {
458     if (funcs[f]) {
459       PetscQuadrature fAllPoints;
460       PetscInt        fNumPoints;
461 
462       PetscCall(PetscDualSpaceGetAllData(sp[f], &fAllPoints, NULL));
463       PetscCall(PetscQuadratureGetData(fAllPoints, NULL, NULL, &fNumPoints, NULL, NULL));
464       numPoints += fNumPoints;
465     }
466   }
467   PetscCall(PetscMalloc1(dim * numPoints, &points));
468   numPoints = 0;
469   for (f = 0; f < Nf; ++f) {
470     if (funcs[f]) {
471       PetscQuadrature  fAllPoints;
472       PetscInt         qdim, fNumPoints, q;
473       const PetscReal *fPoints;
474 
475       PetscCall(PetscDualSpaceGetAllData(sp[f], &fAllPoints, NULL));
476       PetscCall(PetscQuadratureGetData(fAllPoints, &qdim, NULL, &fNumPoints, &fPoints, NULL));
477       PetscCheck(qdim == dim, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Spatial dimension %" PetscInt_FMT " for dual basis does not match input dimension %" PetscInt_FMT, qdim, dim);
478       for (q = 0; q < fNumPoints * dim; ++q) points[numPoints * dim + q] = fPoints[q];
479       numPoints += fNumPoints;
480     }
481   }
482   PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, allPoints));
483   PetscCall(PetscQuadratureSetData(*allPoints, dim, 0, numPoints, points, NULL));
484   PetscFunctionReturn(PETSC_SUCCESS);
485 }
486 
487 /*@C
488   DMGetFirstLabeledPoint - Find first labeled `point` in `odm` such that the corresponding point in `dm` has the specified `height`. Return `point` and the corresponding `ds`.
489 
490   Input Parameters:
491 + dm     - the `DM`
492 . odm    - the enclosing `DM`
493 . label  - label for `DM` domain, or `NULL` for whole domain
494 . numIds - the number of `ids`
495 . ids    - An array of the label ids in sequence for the domain
496 - height - Height of target cells in `DMPLEX` topology
497 
498   Output Parameters:
499 + point - the first labeled point
500 - ds    - the ds corresponding to the first labeled point
501 
502   Level: developer
503 
504 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetActivePoint()`, `DMLabel`, `PetscDS`
505 @*/
506 PetscErrorCode DMGetFirstLabeledPoint(DM dm, DM odm, DMLabel label, PetscInt numIds, const PetscInt ids[], PetscInt height, PetscInt *point, PetscDS *ds)
507 {
508   DM              plex;
509   DMEnclosureType enc;
510   PetscInt        ls = -1;
511 
512   PetscFunctionBegin;
513   if (point) *point = -1;
514   if (!label) PetscFunctionReturn(PETSC_SUCCESS);
515   PetscCall(DMGetEnclosureRelation(dm, odm, &enc));
516   PetscCall(DMConvert(dm, DMPLEX, &plex));
517   for (PetscInt i = 0; i < numIds; ++i) {
518     IS       labelIS;
519     PetscInt num_points, pStart, pEnd;
520     PetscCall(DMLabelGetStratumIS(label, ids[i], &labelIS));
521     if (!labelIS) continue; /* No points with that id on this process */
522     PetscCall(DMPlexGetHeightStratum(plex, height, &pStart, &pEnd));
523     PetscCall(ISGetSize(labelIS, &num_points));
524     if (num_points) {
525       const PetscInt *points;
526       PetscCall(ISGetIndices(labelIS, &points));
527       for (PetscInt i = 0; i < num_points; i++) {
528         PetscInt point;
529         PetscCall(DMGetEnclosurePoint(dm, odm, enc, points[i], &point));
530         if (pStart <= point && point < pEnd) {
531           ls = point;
532           if (ds) PetscCall(DMGetCellDS(dm, ls, ds, NULL));
533           if (ls >= 0) break;
534         }
535       }
536       PetscCall(ISRestoreIndices(labelIS, &points));
537     }
538     PetscCall(ISDestroy(&labelIS));
539     if (ls >= 0) break;
540   }
541   if (point) *point = ls;
542   PetscCall(DMDestroy(&plex));
543   PetscFunctionReturn(PETSC_SUCCESS);
544 }
545 
546 /*
547   This function iterates over a manifold, and interpolates the input function/field using the basis provided by the DS in our DM
548 
549   There are several different scenarios:
550 
551   1) Volumetric mesh with volumetric auxiliary data
552 
553      Here minHeight=0 since we loop over cells.
554 
555   2) Boundary mesh with boundary auxiliary data
556 
557      Here minHeight=1 since we loop over faces. This normally happens since we hang cells off of our boundary meshes to facilitate computation.
558 
559   3) Volumetric mesh with boundary auxiliary data
560 
561      Here minHeight=1 and auxbd=PETSC_TRUE since we loop over faces and use data only supported on those faces. This is common when imposing Dirichlet boundary conditions.
562 
563   4) Volumetric input mesh with boundary output mesh
564 
565      Here we must get a subspace for the input DS
566 
567   The maxHeight is used to support enforcement of constraints in DMForest.
568 
569   If localU is given and not equal to localX, we call DMPlexInsertBoundaryValues() to complete it.
570 
571   If we are using an input field (DM_BC_ESSENTIAL_FIELD or DM_BC_NATURAL_FIELD), we need to evaluate it at all the quadrature points of the dual basis functionals.
572     - We use effectiveHeight to mean the height above our incoming DS. For example, if the DS is for a submesh then the effective height is zero, whereas if the DS
573       is for the volumetric mesh, but we are iterating over a surface, then the effective height is nonzero. When the effective height is nonzero, we need to extract
574       dual spaces for the boundary from our input spaces.
575     - After extracting all quadrature points, we tabulate the input fields and auxiliary fields on them.
576 
577   We check that the #dof(closure(p)) == #dual basis functionals(p) for a representative p in the iteration
578 
579   If we have a label, we iterate over those points. This will probably break the maxHeight functionality since we do not check the height of those points.
580 */
581 static PetscErrorCode DMProjectLocal_Generic_Plex(DM dm, PetscReal time, Vec localU, PetscInt Ncc, const PetscInt comps[], DMLabel label, PetscInt numIds, const PetscInt ids[], DMBoundaryConditionType type, void (**funcs)(void), void **ctxs, InsertMode mode, Vec localX)
582 {
583   DM               plex, dmIn, plexIn, dmAux = NULL, plexAux = NULL, tdm;
584   DMEnclosureType  encIn, encAux;
585   PetscDS          ds = NULL, dsIn = NULL, dsAux = NULL;
586   Vec              localA = NULL, tv;
587   IS               fieldIS;
588   PetscSection     section;
589   PetscDualSpace  *sp, *cellsp, *spIn, *cellspIn;
590   PetscTabulation *T = NULL, *TAux = NULL;
591   PetscInt        *Nc;
592   PetscInt         dim, dimEmbed, depth, htInc = 0, htIncIn = 0, htIncAux = 0, minHeight, maxHeight, h, regionNum, Nf, NfIn, NfAux = 0, NfTot, f;
593   PetscBool       *isFE, hasFE = PETSC_FALSE, hasFV = PETSC_FALSE, isCohesive = PETSC_FALSE, isCohesiveIn = PETSC_FALSE, transform;
594   DMField          coordField;
595   DMLabel          depthLabel;
596   PetscQuadrature  allPoints = NULL;
597 
598   PetscFunctionBegin;
599   if (localU) PetscCall(VecGetDM(localU, &dmIn));
600   else dmIn = dm;
601   PetscCall(DMGetAuxiliaryVec(dm, label, numIds ? ids[0] : 0, 0, &localA));
602   if (localA) PetscCall(VecGetDM(localA, &dmAux));
603   else dmAux = NULL;
604   PetscCall(DMConvert(dm, DMPLEX, &plex));
605   PetscCall(DMConvert(dmIn, DMPLEX, &plexIn));
606   PetscCall(DMGetEnclosureRelation(dmIn, dm, &encIn));
607   PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
608   PetscCall(DMGetDimension(dm, &dim));
609   PetscCall(DMPlexGetVTKCellHeight(plex, &minHeight));
610   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
611   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
612   PetscCall(DMHasBasisTransform(dm, &transform));
613   /* Auxiliary information can only be used with interpolation of field functions */
614   if (dmAux) {
615     PetscCall(DMConvert(dmAux, DMPLEX, &plexAux));
616     if (type == DM_BC_ESSENTIAL_FIELD || type == DM_BC_ESSENTIAL_BD_FIELD || type == DM_BC_NATURAL_FIELD) PetscCheck(localA, PETSC_COMM_SELF, PETSC_ERR_USER, "Missing localA vector");
617   }
618   if (localU && localU != localX) PetscCall(DMPlexInsertBoundaryValues(plex, PETSC_TRUE, localU, time, NULL, NULL, NULL));
619   PetscCall(DMGetCoordinateField(dm, &coordField));
620   PetscCheck(coordField, PETSC_COMM_SELF, PETSC_ERR_USER, "DM must have a coordinate field");
621   /**** No collective calls below this point ****/
622   /* Determine height for iteration of all meshes */
623   {
624     DMPolytopeType ct, ctIn, ctAux;
625     PetscInt       minHeightIn, minHeightAux, lStart, pStart, pEnd, p, pStartIn, pStartAux, pEndAux;
626     PetscInt       dim = -1, dimIn = -1, dimAux = -1;
627 
628     PetscCall(DMPlexGetSimplexOrBoxCells(plex, minHeight, &pStart, &pEnd));
629     if (pEnd > pStart) {
630       PetscCall(DMGetFirstLabeledPoint(dm, dm, label, numIds, ids, minHeight, &lStart, NULL));
631       p = lStart < 0 ? pStart : lStart;
632       PetscCall(DMPlexGetCellType(plex, p, &ct));
633       dim = DMPolytopeTypeGetDim(ct);
634       PetscCall(DMPlexGetVTKCellHeight(plexIn, &minHeightIn));
635       PetscCall(DMPlexGetSimplexOrBoxCells(plexIn, minHeightIn, &pStartIn, NULL));
636       PetscCall(DMPlexGetCellType(plexIn, pStartIn, &ctIn));
637       dimIn = DMPolytopeTypeGetDim(ctIn);
638       if (dmAux) {
639         PetscCall(DMPlexGetVTKCellHeight(plexAux, &minHeightAux));
640         PetscCall(DMPlexGetSimplexOrBoxCells(plexAux, minHeightAux, &pStartAux, &pEndAux));
641         if (pStartAux < pEndAux) {
642           PetscCall(DMPlexGetCellType(plexAux, pStartAux, &ctAux));
643           dimAux = DMPolytopeTypeGetDim(ctAux);
644         }
645       } else dimAux = dim;
646     } else {
647       PetscCall(DMDestroy(&plex));
648       PetscCall(DMDestroy(&plexIn));
649       if (dmAux) PetscCall(DMDestroy(&plexAux));
650       PetscFunctionReturn(PETSC_SUCCESS);
651     }
652     if (dim < 0) {
653       DMLabel spmap = NULL, spmapIn = NULL, spmapAux = NULL;
654 
655       /* Fall back to determination based on being a submesh */
656       PetscCall(DMPlexGetSubpointMap(plex, &spmap));
657       PetscCall(DMPlexGetSubpointMap(plexIn, &spmapIn));
658       if (plexAux) PetscCall(DMPlexGetSubpointMap(plexAux, &spmapAux));
659       dim    = spmap ? 1 : 0;
660       dimIn  = spmapIn ? 1 : 0;
661       dimAux = spmapAux ? 1 : 0;
662     }
663     {
664       PetscInt dimProj   = PetscMin(PetscMin(dim, dimIn), (dimAux < 0 ? PETSC_MAX_INT : dimAux));
665       PetscInt dimAuxEff = dimAux < 0 ? dimProj : dimAux;
666 
667       PetscCheck(PetscAbsInt(dimProj - dim) <= 1 && PetscAbsInt(dimProj - dimIn) <= 1 && PetscAbsInt(dimProj - dimAuxEff) <= 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Do not currently support differences of more than 1 in dimension");
668       if (dimProj < dim) minHeight = 1;
669       htInc    = dim - dimProj;
670       htIncIn  = dimIn - dimProj;
671       htIncAux = dimAuxEff - dimProj;
672     }
673   }
674   PetscCall(DMPlexGetDepth(plex, &depth));
675   PetscCall(DMPlexGetDepthLabel(plex, &depthLabel));
676   PetscCall(DMPlexGetMaxProjectionHeight(plex, &maxHeight));
677   maxHeight = PetscMax(maxHeight, minHeight);
678   PetscCheck(maxHeight >= 0 && maxHeight <= dim, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Maximum projection height %" PetscInt_FMT " not in [0, %" PetscInt_FMT ")", maxHeight, dim);
679   PetscCall(DMGetFirstLabeledPoint(dm, dm, label, numIds, ids, 0, NULL, &ds));
680   if (!ds) PetscCall(DMGetDS(dm, &ds));
681   PetscCall(DMGetFirstLabeledPoint(dmIn, dm, label, numIds, ids, 0, NULL, &dsIn));
682   if (!dsIn) PetscCall(DMGetDS(dmIn, &dsIn));
683   PetscCall(PetscDSGetNumFields(ds, &Nf));
684   PetscCall(PetscDSGetNumFields(dsIn, &NfIn));
685   PetscCall(PetscDSIsCohesive(dsIn, &isCohesiveIn));
686   if (isCohesiveIn) --htIncIn; // Should be rearranged
687   PetscCall(DMGetNumFields(dm, &NfTot));
688   PetscCall(DMFindRegionNum(dm, ds, &regionNum));
689   PetscCall(DMGetRegionNumDS(dm, regionNum, NULL, &fieldIS, NULL, NULL));
690   PetscCall(PetscDSIsCohesive(ds, &isCohesive));
691   PetscCall(DMGetCoordinateDim(dm, &dimEmbed));
692   PetscCall(DMGetLocalSection(dm, &section));
693   if (dmAux) {
694     PetscCall(DMGetDS(dmAux, &dsAux));
695     PetscCall(PetscDSGetNumFields(dsAux, &NfAux));
696   }
697   PetscCall(PetscDSGetComponents(ds, &Nc));
698   PetscCall(PetscMalloc3(Nf, &isFE, Nf, &sp, NfIn, &spIn));
699   if (maxHeight > 0) PetscCall(PetscMalloc2(Nf, &cellsp, NfIn, &cellspIn));
700   else {
701     cellsp   = sp;
702     cellspIn = spIn;
703   }
704   /* Get cell dual spaces */
705   for (f = 0; f < Nf; ++f) {
706     PetscDiscType disctype;
707 
708     PetscCall(PetscDSGetDiscType_Internal(ds, f, &disctype));
709     if (disctype == PETSC_DISC_FE) {
710       PetscFE fe;
711 
712       isFE[f] = PETSC_TRUE;
713       hasFE   = PETSC_TRUE;
714       PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
715       PetscCall(PetscFEGetDualSpace(fe, &cellsp[f]));
716     } else if (disctype == PETSC_DISC_FV) {
717       PetscFV fv;
718 
719       isFE[f] = PETSC_FALSE;
720       hasFV   = PETSC_TRUE;
721       PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fv));
722       PetscCall(PetscFVGetDualSpace(fv, &cellsp[f]));
723     } else {
724       isFE[f]   = PETSC_FALSE;
725       cellsp[f] = NULL;
726     }
727   }
728   for (f = 0; f < NfIn; ++f) {
729     PetscDiscType disctype;
730 
731     PetscCall(PetscDSGetDiscType_Internal(dsIn, f, &disctype));
732     if (disctype == PETSC_DISC_FE) {
733       PetscFE fe;
734 
735       PetscCall(PetscDSGetDiscretization(dsIn, f, (PetscObject *)&fe));
736       PetscCall(PetscFEGetDualSpace(fe, &cellspIn[f]));
737     } else if (disctype == PETSC_DISC_FV) {
738       PetscFV fv;
739 
740       PetscCall(PetscDSGetDiscretization(dsIn, f, (PetscObject *)&fv));
741       PetscCall(PetscFVGetDualSpace(fv, &cellspIn[f]));
742     } else {
743       cellspIn[f] = NULL;
744     }
745   }
746   for (f = 0; f < Nf; ++f) {
747     if (!htInc) {
748       sp[f] = cellsp[f];
749     } else PetscCall(PetscDualSpaceGetHeightSubspace(cellsp[f], htInc, &sp[f]));
750   }
751   if (type == DM_BC_ESSENTIAL_FIELD || type == DM_BC_ESSENTIAL_BD_FIELD || type == DM_BC_NATURAL_FIELD) {
752     PetscFE          fem, subfem;
753     PetscDiscType    disctype;
754     const PetscReal *points;
755     PetscInt         numPoints;
756 
757     PetscCheck(maxHeight <= minHeight, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Field projection not supported for face interpolation");
758     PetscCall(PetscDualSpaceGetAllPointsUnion(Nf, sp, dim - htInc, funcs, &allPoints));
759     PetscCall(PetscQuadratureGetData(allPoints, NULL, NULL, &numPoints, &points, NULL));
760     PetscCall(PetscMalloc2(NfIn, &T, NfAux, &TAux));
761     for (f = 0; f < NfIn; ++f) {
762       if (!htIncIn) {
763         spIn[f] = cellspIn[f];
764       } else PetscCall(PetscDualSpaceGetHeightSubspace(cellspIn[f], htIncIn, &spIn[f]));
765 
766       PetscCall(PetscDSGetDiscType_Internal(dsIn, f, &disctype));
767       if (disctype != PETSC_DISC_FE) continue;
768       PetscCall(PetscDSGetDiscretization(dsIn, f, (PetscObject *)&fem));
769       if (!htIncIn) {
770         subfem = fem;
771       } else PetscCall(PetscFEGetHeightSubspace(fem, htIncIn, &subfem));
772       PetscCall(PetscFECreateTabulation(subfem, 1, numPoints, points, 1, &T[f]));
773     }
774     for (f = 0; f < NfAux; ++f) {
775       PetscCall(PetscDSGetDiscType_Internal(dsAux, f, &disctype));
776       if (disctype != PETSC_DISC_FE) continue;
777       PetscCall(PetscDSGetDiscretization(dsAux, f, (PetscObject *)&fem));
778       if (!htIncAux) {
779         subfem = fem;
780       } else PetscCall(PetscFEGetHeightSubspace(fem, htIncAux, &subfem));
781       PetscCall(PetscFECreateTabulation(subfem, 1, numPoints, points, 1, &TAux[f]));
782     }
783   }
784   /* Note: We make no attempt to optimize for height. Higher height things just overwrite the lower height results. */
785   for (h = minHeight; h <= maxHeight; h++) {
786     PetscInt     hEff     = h - minHeight + htInc;
787     PetscInt     hEffIn   = h - minHeight + htIncIn;
788     PetscInt     hEffAux  = h - minHeight + htIncAux;
789     PetscDS      dsEff    = ds;
790     PetscDS      dsEffIn  = dsIn;
791     PetscDS      dsEffAux = dsAux;
792     PetscScalar *values;
793     PetscBool   *fieldActive;
794     PetscInt     maxDegree;
795     PetscInt     pStart, pEnd, p, lStart, spDim, totDim, numValues;
796     IS           heightIS;
797 
798     if (h > minHeight) {
799       for (f = 0; f < Nf; ++f) PetscCall(PetscDualSpaceGetHeightSubspace(cellsp[f], hEff, &sp[f]));
800     }
801     PetscCall(DMPlexGetSimplexOrBoxCells(plex, h, &pStart, &pEnd));
802     PetscCall(DMGetFirstLabeledPoint(dm, dm, label, numIds, ids, h, &lStart, NULL));
803     PetscCall(DMLabelGetStratumIS(depthLabel, depth - h, &heightIS));
804     if (pEnd <= pStart) {
805       PetscCall(ISDestroy(&heightIS));
806       continue;
807     }
808     /* Compute totDim, the number of dofs in the closure of a point at this height */
809     totDim = 0;
810     for (f = 0; f < Nf; ++f) {
811       PetscBool cohesive;
812 
813       if (!sp[f]) continue;
814       PetscCall(PetscDSGetCohesive(ds, f, &cohesive));
815       PetscCall(PetscDualSpaceGetDimension(sp[f], &spDim));
816       totDim += spDim;
817       if (isCohesive && !cohesive) totDim += spDim;
818     }
819     p = lStart < 0 ? pStart : lStart;
820     PetscCall(DMPlexVecGetClosure(plex, section, localX, p, &numValues, NULL));
821     PetscCheck(numValues == totDim, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "The output section point (%" PetscInt_FMT ") closure size %" PetscInt_FMT " != dual space dimension %" PetscInt_FMT " at height %" PetscInt_FMT " in [%" PetscInt_FMT ", %" PetscInt_FMT "]", p, numValues, totDim, h, minHeight, maxHeight);
822     if (!totDim) {
823       PetscCall(ISDestroy(&heightIS));
824       continue;
825     }
826     if (htInc) PetscCall(PetscDSGetHeightSubspace(ds, hEff, &dsEff));
827     /* Compute totDimIn, the number of dofs in the closure of a point at this height */
828     if (localU) {
829       PetscInt totDimIn, pIn, numValuesIn;
830 
831       totDimIn = 0;
832       for (f = 0; f < NfIn; ++f) {
833         PetscBool cohesive;
834 
835         if (!spIn[f]) continue;
836         PetscCall(PetscDSGetCohesive(dsIn, f, &cohesive));
837         PetscCall(PetscDualSpaceGetDimension(spIn[f], &spDim));
838         totDimIn += spDim;
839         if (isCohesiveIn && !cohesive) totDimIn += spDim;
840       }
841       PetscCall(DMGetEnclosurePoint(dmIn, dm, encIn, lStart < 0 ? pStart : lStart, &pIn));
842       PetscCall(DMPlexVecGetClosure(plexIn, NULL, localU, pIn, &numValuesIn, NULL));
843       // TODO We could check that pIn is a cohesive cell for this check
844       PetscCheck(isCohesiveIn || (numValuesIn == totDimIn), PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "The input section point (%" PetscInt_FMT ") closure size %" PetscInt_FMT " != dual space dimension %" PetscInt_FMT " at height %" PetscInt_FMT, pIn, numValuesIn, totDimIn, htIncIn);
845       if (htIncIn) PetscCall(PetscDSGetHeightSubspace(dsIn, hEffIn, &dsEffIn));
846     }
847     if (htIncAux) PetscCall(PetscDSGetHeightSubspace(dsAux, hEffAux, &dsEffAux));
848     /* Loop over points at this height */
849     PetscCall(DMGetWorkArray(dm, numValues, MPIU_SCALAR, &values));
850     PetscCall(DMGetWorkArray(dm, NfTot, MPI_INT, &fieldActive));
851     {
852       const PetscInt *fields;
853 
854       PetscCall(ISGetIndices(fieldIS, &fields));
855       for (f = 0; f < NfTot; ++f) fieldActive[f] = PETSC_FALSE;
856       for (f = 0; f < Nf; ++f) fieldActive[fields[f]] = (funcs[f] && sp[f]) ? PETSC_TRUE : PETSC_FALSE;
857       PetscCall(ISRestoreIndices(fieldIS, &fields));
858     }
859     if (label) {
860       PetscInt i;
861 
862       for (i = 0; i < numIds; ++i) {
863         IS              pointIS, isectIS;
864         const PetscInt *points;
865         PetscInt        n;
866         PetscFEGeom    *fegeom = NULL, *chunkgeom = NULL;
867         PetscQuadrature quad = NULL;
868 
869         PetscCall(DMLabelGetStratumIS(label, ids[i], &pointIS));
870         if (!pointIS) continue; /* No points with that id on this process */
871         PetscCall(ISIntersect(pointIS, heightIS, &isectIS));
872         PetscCall(ISDestroy(&pointIS));
873         if (!isectIS) continue;
874         PetscCall(ISGetLocalSize(isectIS, &n));
875         PetscCall(ISGetIndices(isectIS, &points));
876         PetscCall(DMFieldGetDegree(coordField, isectIS, NULL, &maxDegree));
877         if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, isectIS, &quad));
878         if (!quad) {
879           if (!h && allPoints) {
880             quad      = allPoints;
881             allPoints = NULL;
882           } else {
883             PetscCall(PetscDualSpaceGetAllPointsUnion(Nf, sp, isCohesive ? dim - htInc - 1 : dim - htInc, funcs, &quad));
884           }
885         }
886         PetscCall(DMFieldCreateFEGeom(coordField, isectIS, quad, (htInc && h == minHeight) ? PETSC_TRUE : PETSC_FALSE, &fegeom));
887         for (p = 0; p < n; ++p) {
888           const PetscInt point = points[p];
889 
890           PetscCall(PetscArrayzero(values, numValues));
891           PetscCall(PetscFEGeomGetChunk(fegeom, p, p + 1, &chunkgeom));
892           PetscCall(DMPlexSetActivePoint(dm, point));
893           PetscCall(DMProjectPoint_Private(dm, dsEff, plexIn, encIn, dsEffIn, plexAux, encAux, dsEffAux, chunkgeom, htInc, time, localU, localA, hasFE, hasFV, isFE, sp, point, T, TAux, type, funcs, ctxs, fieldActive, values));
894           if (transform) PetscCall(DMPlexBasisTransformPoint_Internal(plex, tdm, tv, point, fieldActive, PETSC_FALSE, values));
895           PetscCall(DMPlexVecSetFieldClosure_Internal(plex, section, localX, fieldActive, point, Ncc, comps, label, ids[i], values, mode));
896         }
897         PetscCall(PetscFEGeomRestoreChunk(fegeom, p, p + 1, &chunkgeom));
898         PetscCall(PetscFEGeomDestroy(&fegeom));
899         PetscCall(PetscQuadratureDestroy(&quad));
900         PetscCall(ISRestoreIndices(isectIS, &points));
901         PetscCall(ISDestroy(&isectIS));
902       }
903     } else {
904       PetscFEGeom    *fegeom = NULL, *chunkgeom = NULL;
905       PetscQuadrature quad = NULL;
906       IS              pointIS;
907 
908       PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &pointIS));
909       PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
910       if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &quad));
911       if (!quad) {
912         if (!h && allPoints) {
913           quad      = allPoints;
914           allPoints = NULL;
915         } else {
916           PetscCall(PetscDualSpaceGetAllPointsUnion(Nf, sp, dim - htInc, funcs, &quad));
917         }
918       }
919       PetscCall(DMFieldCreateFEGeom(coordField, pointIS, quad, (htInc && h == minHeight) ? PETSC_TRUE : PETSC_FALSE, &fegeom));
920       for (p = pStart; p < pEnd; ++p) {
921         PetscCall(PetscArrayzero(values, numValues));
922         PetscCall(PetscFEGeomGetChunk(fegeom, p - pStart, p - pStart + 1, &chunkgeom));
923         PetscCall(DMPlexSetActivePoint(dm, p));
924         PetscCall(DMProjectPoint_Private(dm, dsEff, plexIn, encIn, dsEffIn, plexAux, encAux, dsEffAux, chunkgeom, htInc, time, localU, localA, hasFE, hasFV, isFE, sp, p, T, TAux, type, funcs, ctxs, fieldActive, values));
925         if (transform) PetscCall(DMPlexBasisTransformPoint_Internal(plex, tdm, tv, p, fieldActive, PETSC_FALSE, values));
926         PetscCall(DMPlexVecSetFieldClosure_Internal(plex, section, localX, fieldActive, p, Ncc, comps, NULL, -1, values, mode));
927       }
928       PetscCall(PetscFEGeomRestoreChunk(fegeom, p - pStart, pStart - p + 1, &chunkgeom));
929       PetscCall(PetscFEGeomDestroy(&fegeom));
930       PetscCall(PetscQuadratureDestroy(&quad));
931       PetscCall(ISDestroy(&pointIS));
932     }
933     PetscCall(ISDestroy(&heightIS));
934     PetscCall(DMRestoreWorkArray(dm, numValues, MPIU_SCALAR, &values));
935     PetscCall(DMRestoreWorkArray(dm, Nf, MPI_INT, &fieldActive));
936   }
937   /* Cleanup */
938   if (type == DM_BC_ESSENTIAL_FIELD || type == DM_BC_ESSENTIAL_BD_FIELD || type == DM_BC_NATURAL_FIELD) {
939     for (f = 0; f < NfIn; ++f) PetscCall(PetscTabulationDestroy(&T[f]));
940     for (f = 0; f < NfAux; ++f) PetscCall(PetscTabulationDestroy(&TAux[f]));
941     PetscCall(PetscFree2(T, TAux));
942   }
943   PetscCall(PetscQuadratureDestroy(&allPoints));
944   PetscCall(PetscFree3(isFE, sp, spIn));
945   if (maxHeight > 0) PetscCall(PetscFree2(cellsp, cellspIn));
946   PetscCall(DMDestroy(&plex));
947   PetscCall(DMDestroy(&plexIn));
948   if (dmAux) PetscCall(DMDestroy(&plexAux));
949   PetscFunctionReturn(PETSC_SUCCESS);
950 }
951 
952 PetscErrorCode DMProjectFunctionLocal_Plex(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, InsertMode mode, Vec localX)
953 {
954   PetscFunctionBegin;
955   PetscCall(DMProjectLocal_Generic_Plex(dm, time, NULL, 0, NULL, NULL, 0, NULL, DM_BC_ESSENTIAL, (void (**)(void))funcs, ctxs, mode, localX));
956   PetscFunctionReturn(PETSC_SUCCESS);
957 }
958 
959 PetscErrorCode DMProjectFunctionLabelLocal_Plex(DM dm, PetscReal time, DMLabel label, PetscInt numIds, const PetscInt ids[], PetscInt Ncc, const PetscInt comps[], PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, InsertMode mode, Vec localX)
960 {
961   PetscFunctionBegin;
962   PetscCall(DMProjectLocal_Generic_Plex(dm, time, NULL, Ncc, comps, label, numIds, ids, DM_BC_ESSENTIAL, (void (**)(void))funcs, ctxs, mode, localX));
963   PetscFunctionReturn(PETSC_SUCCESS);
964 }
965 
966 PetscErrorCode DMProjectFieldLocal_Plex(DM dm, PetscReal time, Vec localU, 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[]), InsertMode mode, Vec localX)
967 {
968   PetscFunctionBegin;
969   PetscCall(DMProjectLocal_Generic_Plex(dm, time, localU, 0, NULL, NULL, 0, NULL, DM_BC_ESSENTIAL_FIELD, (void (**)(void))funcs, NULL, mode, localX));
970   PetscFunctionReturn(PETSC_SUCCESS);
971 }
972 
973 PetscErrorCode DMProjectFieldLabelLocal_Plex(DM dm, PetscReal time, DMLabel label, PetscInt numIds, const PetscInt ids[], PetscInt Ncc, const PetscInt comps[], Vec localU, 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[]), InsertMode mode, Vec localX)
974 {
975   PetscFunctionBegin;
976   PetscCall(DMProjectLocal_Generic_Plex(dm, time, localU, Ncc, comps, label, numIds, ids, DM_BC_ESSENTIAL_FIELD, (void (**)(void))funcs, NULL, mode, localX));
977   PetscFunctionReturn(PETSC_SUCCESS);
978 }
979 
980 PetscErrorCode DMProjectBdFieldLabelLocal_Plex(DM dm, PetscReal time, DMLabel label, PetscInt numIds, const PetscInt ids[], PetscInt Ncc, const PetscInt comps[], Vec localU, 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[]), InsertMode mode, Vec localX)
981 {
982   PetscFunctionBegin;
983   PetscCall(DMProjectLocal_Generic_Plex(dm, time, localU, Ncc, comps, label, numIds, ids, DM_BC_ESSENTIAL_BD_FIELD, (void (**)(void))funcs, NULL, mode, localX));
984   PetscFunctionReturn(PETSC_SUCCESS);
985 }
986