xref: /petsc/src/dm/dt/dualspace/impls/lagrange/dspacelagrange.c (revision e5a36eccef3d6b83a2c625c30d0dfd5adb4001f2)
1 #include <petsc/private/petscfeimpl.h> /*I "petscfe.h" I*/
2 #include <petscdmplex.h>
3 
4 static PetscErrorCode PetscDualSpaceLagrangeGetTensor_Lagrange(PetscDualSpace sp, PetscBool *tensor)
5 {
6   PetscDualSpace_Lag *lag = (PetscDualSpace_Lag *)sp->data;
7 
8   PetscFunctionBegin;
9   *tensor = lag->tensorSpace;
10   PetscFunctionReturn(0);
11 }
12 
13 static PetscErrorCode PetscDualSpaceLagrangeSetTensor_Lagrange(PetscDualSpace sp, PetscBool tensor)
14 {
15   PetscDualSpace_Lag *lag = (PetscDualSpace_Lag *)sp->data;
16 
17   PetscFunctionBegin;
18   lag->tensorSpace = tensor;
19   PetscFunctionReturn(0);
20 }
21 
22 /*
23   LatticePointLexicographic_Internal - Returns all tuples of size 'len' with nonnegative integers that sum up to at most 'max'.
24                                        Ordering is lexicographic with lowest index as least significant in ordering.
25                                        e.g. for len == 2 and max == 2, this will return, in order, {0,0}, {1,0}, {2,0}, {0,1}, {1,1}, {2,0}.
26 
27   Input Parameters:
28 + len - The length of the tuple
29 . max - The maximum sum
30 - tup - A tuple of length len+1: tup[len] > 0 indicates a stopping condition
31 
32   Output Parameter:
33 . tup - A tuple of len integers whos sum is at most 'max'
34 */
35 static PetscErrorCode LatticePointLexicographic_Internal(PetscInt len, PetscInt max, PetscInt tup[])
36 {
37   PetscFunctionBegin;
38   while (len--) {
39     max -= tup[len];
40     if (!max) {
41       tup[len] = 0;
42       break;
43     }
44   }
45   tup[++len]++;
46   PetscFunctionReturn(0);
47 }
48 
49 /*
50   TensorPointLexicographic_Internal - Returns all tuples of size 'len' with nonnegative integers that are all less than or equal to 'max'.
51                                       Ordering is lexicographic with lowest index as least significant in ordering.
52                                       e.g. for len == 2 and max == 2, this will return, in order, {0,0}, {1,0}, {2,0}, {0,1}, {1,1}, {2,1}, {0,2}, {1,2}, {2,2}.
53 
54   Input Parameters:
55 + len - The length of the tuple
56 . max - The maximum value
57 - tup - A tuple of length len+1: tup[len] > 0 indicates a stopping condition
58 
59   Output Parameter:
60 . tup - A tuple of len integers whos sum is at most 'max'
61 */
62 static PetscErrorCode TensorPointLexicographic_Internal(PetscInt len, PetscInt max, PetscInt tup[])
63 {
64   PetscInt       i;
65 
66   PetscFunctionBegin;
67   for (i = 0; i < len; i++) {
68     if (tup[i] < max) {
69       break;
70     } else {
71       tup[i] = 0;
72     }
73   }
74   tup[i]++;
75   PetscFunctionReturn(0);
76 }
77 
78 
79 #define BaryIndex(perEdge,a,b,c) (((b)*(2*perEdge+1-(b)))/2)+(c)
80 
81 #define CartIndex(perEdge,a,b) (perEdge*(a)+b)
82 
83 static PetscErrorCode PetscDualSpaceGetSymmetries_Lagrange(PetscDualSpace sp, const PetscInt ****perms, const PetscScalar ****flips)
84 {
85 
86   PetscDualSpace_Lag *lag = (PetscDualSpace_Lag *) sp->data;
87   PetscInt           dim, order, p, Nc;
88   PetscErrorCode     ierr;
89 
90   PetscFunctionBegin;
91   ierr = PetscDualSpaceGetOrder(sp,&order);CHKERRQ(ierr);
92   ierr = PetscDualSpaceGetNumComponents(sp,&Nc);CHKERRQ(ierr);
93   ierr = DMGetDimension(sp->dm,&dim);CHKERRQ(ierr);
94   if (!dim || !lag->continuous || order < 3) PetscFunctionReturn(0);
95   if (dim > 3) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_SUP,"Lagrange symmetries not implemented for dim = %D > 3",dim);
96   if (!lag->symmetries) { /* store symmetries */
97     PetscDualSpace hsp;
98     DM             K;
99     PetscInt       numPoints = 1, d;
100     PetscInt       numFaces;
101     PetscInt       ***symmetries;
102     const PetscInt ***hsymmetries;
103 
104     if (lag->simplexCell) {
105       numFaces = 1 + dim;
106       for (d = 0; d < dim; d++) numPoints = numPoints * 2 + 1;
107     }
108     else {
109       numPoints = PetscPowInt(3,dim);
110       numFaces  = 2 * dim;
111     }
112     ierr = PetscCalloc1(numPoints,&symmetries);CHKERRQ(ierr);
113     if (0 < dim && dim < 3) { /* compute self symmetries */
114       PetscInt **cellSymmetries;
115 
116       lag->numSelfSym = 2 * numFaces;
117       lag->selfSymOff = numFaces;
118       ierr = PetscCalloc1(2*numFaces,&cellSymmetries);CHKERRQ(ierr);
119       /* we want to be able to index symmetries directly with the orientations, which range from [-numFaces,numFaces) */
120       symmetries[0] = &cellSymmetries[numFaces];
121       if (dim == 1) {
122         PetscInt dofPerEdge = order - 1;
123 
124         if (dofPerEdge > 1) {
125           PetscInt i, j, *reverse;
126 
127           ierr = PetscMalloc1(dofPerEdge*Nc,&reverse);CHKERRQ(ierr);
128           for (i = 0; i < dofPerEdge; i++) {
129             for (j = 0; j < Nc; j++) {
130               reverse[i*Nc + j] = Nc * (dofPerEdge - 1 - i) + j;
131             }
132           }
133           symmetries[0][-2] = reverse;
134 
135           /* yes, this is redundant, but it makes it easier to cleanup if I don't have to worry about what not to free */
136           ierr = PetscMalloc1(dofPerEdge*Nc,&reverse);CHKERRQ(ierr);
137           for (i = 0; i < dofPerEdge; i++) {
138             for (j = 0; j < Nc; j++) {
139               reverse[i*Nc + j] = Nc * (dofPerEdge - 1 - i) + j;
140             }
141           }
142           symmetries[0][1] = reverse;
143         }
144       } else {
145         PetscInt dofPerEdge = lag->simplexCell ? (order - 2) : (order - 1), s;
146         PetscInt dofPerFace;
147 
148         if (dofPerEdge > 1) {
149           for (s = -numFaces; s < numFaces; s++) {
150             PetscInt *sym, i, j, k, l;
151 
152             if (!s) continue;
153             if (lag->simplexCell) {
154               dofPerFace = (dofPerEdge * (dofPerEdge + 1))/2;
155               ierr = PetscMalloc1(Nc*dofPerFace,&sym);CHKERRQ(ierr);
156               for (j = 0, l = 0; j < dofPerEdge; j++) {
157                 for (k = 0; k < dofPerEdge - j; k++, l++) {
158                   i = dofPerEdge - 1 - j - k;
159                   switch (s) {
160                   case -3:
161                     sym[Nc*l] = BaryIndex(dofPerEdge,i,k,j);
162                     break;
163                   case -2:
164                     sym[Nc*l] = BaryIndex(dofPerEdge,j,i,k);
165                     break;
166                   case -1:
167                     sym[Nc*l] = BaryIndex(dofPerEdge,k,j,i);
168                     break;
169                   case 1:
170                     sym[Nc*l] = BaryIndex(dofPerEdge,k,i,j);
171                     break;
172                   case 2:
173                     sym[Nc*l] = BaryIndex(dofPerEdge,j,k,i);
174                     break;
175                   }
176                 }
177               }
178             } else {
179               dofPerFace = dofPerEdge * dofPerEdge;
180               ierr = PetscMalloc1(Nc*dofPerFace,&sym);CHKERRQ(ierr);
181               for (j = 0, l = 0; j < dofPerEdge; j++) {
182                 for (k = 0; k < dofPerEdge; k++, l++) {
183                   switch (s) {
184                   case -4:
185                     sym[Nc*l] = CartIndex(dofPerEdge,k,j);
186                     break;
187                   case -3:
188                     sym[Nc*l] = CartIndex(dofPerEdge,(dofPerEdge - 1 - j),k);
189                     break;
190                   case -2:
191                     sym[Nc*l] = CartIndex(dofPerEdge,(dofPerEdge - 1 - k),(dofPerEdge - 1 - j));
192                     break;
193                   case -1:
194                     sym[Nc*l] = CartIndex(dofPerEdge,j,(dofPerEdge - 1 - k));
195                     break;
196                   case 1:
197                     sym[Nc*l] = CartIndex(dofPerEdge,(dofPerEdge - 1 - k),j);
198                     break;
199                   case 2:
200                     sym[Nc*l] = CartIndex(dofPerEdge,(dofPerEdge - 1 - j),(dofPerEdge - 1 - k));
201                     break;
202                   case 3:
203                     sym[Nc*l] = CartIndex(dofPerEdge,k,(dofPerEdge - 1 - j));
204                     break;
205                   }
206                 }
207               }
208             }
209             for (i = 0; i < dofPerFace; i++) {
210               sym[Nc*i] *= Nc;
211               for (j = 1; j < Nc; j++) {
212                 sym[Nc*i+j] = sym[Nc*i] + j;
213               }
214             }
215             symmetries[0][s] = sym;
216           }
217         }
218       }
219     }
220     ierr = PetscDualSpaceGetHeightSubspace(sp,1,&hsp);CHKERRQ(ierr);
221     ierr = PetscDualSpaceGetSymmetries(hsp,&hsymmetries,NULL);CHKERRQ(ierr);
222     if (hsymmetries) {
223       PetscBool      *seen;
224       const PetscInt *cone;
225       PetscInt       KclosureSize, *Kclosure = NULL;
226 
227       ierr = PetscDualSpaceGetDM(sp,&K);CHKERRQ(ierr);
228       ierr = PetscCalloc1(numPoints,&seen);CHKERRQ(ierr);
229       ierr = DMPlexGetCone(K,0,&cone);CHKERRQ(ierr);
230       ierr = DMPlexGetTransitiveClosure(K,0,PETSC_TRUE,&KclosureSize,&Kclosure);CHKERRQ(ierr);
231       for (p = 0; p < numFaces; p++) {
232         PetscInt closureSize, *closure = NULL, q;
233 
234         ierr = DMPlexGetTransitiveClosure(K,cone[p],PETSC_TRUE,&closureSize,&closure);CHKERRQ(ierr);
235         for (q = 0; q < closureSize; q++) {
236           PetscInt point = closure[2*q], r;
237 
238           if(!seen[point]) {
239             for (r = 0; r < KclosureSize; r++) {
240               if (Kclosure[2 * r] == point) break;
241             }
242             seen[point] = PETSC_TRUE;
243             symmetries[r] = (PetscInt **) hsymmetries[q];
244           }
245         }
246         ierr = DMPlexRestoreTransitiveClosure(K,cone[p],PETSC_TRUE,&closureSize,&closure);CHKERRQ(ierr);
247       }
248       ierr = DMPlexRestoreTransitiveClosure(K,0,PETSC_TRUE,&KclosureSize,&Kclosure);CHKERRQ(ierr);
249       ierr = PetscFree(seen);CHKERRQ(ierr);
250     }
251     lag->symmetries = symmetries;
252   }
253   if (perms) *perms = (const PetscInt ***) lag->symmetries;
254   PetscFunctionReturn(0);
255 }
256 
257 /*@C
258   PetscDualSpaceGetSymmetries - Returns a description of the symmetries of this basis
259 
260   Not collective
261 
262   Input Parameter:
263 . sp - the PetscDualSpace object
264 
265   Output Parameters:
266 + perms - Permutations of the local degrees of freedom, parameterized by the point orientation
267 - flips - Sign reversal of the local degrees of freedom, parameterized by the point orientation
268 
269   Note: The permutation and flip arrays are organized in the following way
270 $ perms[p][ornt][dof # on point] = new local dof #
271 $ flips[p][ornt][dof # on point] = reversal or not
272 
273   Level: developer
274 
275 .seealso: PetscDualSpaceSetSymmetries()
276 @*/
277 PetscErrorCode PetscDualSpaceGetSymmetries(PetscDualSpace sp, const PetscInt ****perms, const PetscScalar ****flips)
278 {
279   PetscErrorCode ierr;
280 
281   PetscFunctionBegin;
282   PetscValidHeaderSpecific(sp,PETSCDUALSPACE_CLASSID,1);
283   if (perms) {
284     PetscValidPointer(perms,2);
285     *perms = NULL;
286   }
287   if (flips) {
288     PetscValidPointer(flips,3);
289     *flips = NULL;
290   }
291   if (sp->ops->getsymmetries) {
292     ierr = (sp->ops->getsymmetries)(sp,perms,flips);CHKERRQ(ierr);
293   }
294   PetscFunctionReturn(0);
295 }
296 
297 static PetscErrorCode PetscDualSpaceLagrangeView_Ascii(PetscDualSpace sp, PetscViewer viewer)
298 {
299   PetscDualSpace_Lag *lag = (PetscDualSpace_Lag *) sp->data;
300   PetscErrorCode      ierr;
301 
302   PetscFunctionBegin;
303   ierr = PetscViewerASCIIPrintf(viewer, "%s %sLagrange dual space\n", lag->continuous ? "Continuous" : "Discontinuous", lag->tensorSpace ? "tensor " : "");CHKERRQ(ierr);
304   PetscFunctionReturn(0);
305 }
306 
307 PetscErrorCode PetscDualSpaceView_Lagrange(PetscDualSpace sp, PetscViewer viewer)
308 {
309   PetscBool      iascii;
310   PetscErrorCode ierr;
311 
312   PetscFunctionBegin;
313   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 1);
314   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
315   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERASCII, &iascii);CHKERRQ(ierr);
316   if (iascii) {ierr = PetscDualSpaceLagrangeView_Ascii(sp, viewer);CHKERRQ(ierr);}
317   PetscFunctionReturn(0);
318 }
319 
320 static PetscErrorCode PetscDualSpaceGetDimension_SingleCell_Lagrange(PetscDualSpace sp, PetscInt order, PetscInt *dim)
321 {
322   PetscDualSpace_Lag *lag = (PetscDualSpace_Lag *) sp->data;
323   PetscReal           D   = 1.0;
324   PetscInt            n, i;
325   PetscErrorCode      ierr;
326 
327   PetscFunctionBegin;
328   *dim = -1;                    /* Ensure that the compiler knows *dim is set. */
329   ierr = DMGetDimension(sp->dm, &n);CHKERRQ(ierr);
330   if (!lag->tensorSpace) {
331     for (i = 1; i <= n; ++i) {
332       D *= ((PetscReal) (order+i))/i;
333     }
334     *dim = (PetscInt) (D + 0.5);
335   } else {
336     *dim = 1;
337     for (i = 0; i < n; ++i) *dim *= (order+1);
338   }
339   *dim *= sp->Nc;
340   PetscFunctionReturn(0);
341 }
342 
343 static PetscErrorCode PetscDualSpaceCreateHeightSubspace_Lagrange(PetscDualSpace sp, PetscInt height, PetscDualSpace *bdsp)
344 {
345   PetscDualSpace_Lag *lag = (PetscDualSpace_Lag *) sp->data;
346   PetscBool          continuous, tensor;
347   PetscInt           order;
348   PetscErrorCode     ierr;
349 
350   PetscFunctionBegin;
351   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 1);
352   PetscValidPointer(bdsp,2);
353   ierr = PetscDualSpaceLagrangeGetContinuity(sp,&continuous);CHKERRQ(ierr);
354   ierr = PetscDualSpaceGetOrder(sp,&order);CHKERRQ(ierr);
355   if (height == 0) {
356     ierr = PetscObjectReference((PetscObject)sp);CHKERRQ(ierr);
357     *bdsp = sp;
358   } else if (continuous == PETSC_FALSE || !order) {
359     *bdsp = NULL;
360   } else {
361     DM dm, K;
362     PetscInt dim;
363 
364     ierr = PetscDualSpaceGetDM(sp,&dm);CHKERRQ(ierr);
365     ierr = DMGetDimension(dm,&dim);CHKERRQ(ierr);
366     if (height > dim || height < 0) {SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Asked for dual space at height %d for dimension %d reference element\n",height,dim);}
367     ierr = PetscDualSpaceDuplicate(sp,bdsp);CHKERRQ(ierr);
368     ierr = PetscDualSpaceCreateReferenceCell(*bdsp, dim-height, lag->simplexCell, &K);CHKERRQ(ierr);
369     ierr = PetscDualSpaceSetDM(*bdsp, K);CHKERRQ(ierr);
370     ierr = DMDestroy(&K);CHKERRQ(ierr);
371     ierr = PetscDualSpaceLagrangeGetTensor(sp,&tensor);CHKERRQ(ierr);
372     ierr = PetscDualSpaceLagrangeSetTensor(*bdsp,tensor);CHKERRQ(ierr);
373     ierr = PetscDualSpaceSetUp(*bdsp);CHKERRQ(ierr);
374   }
375   PetscFunctionReturn(0);
376 }
377 
378 PetscErrorCode PetscDualSpaceSetUp_Lagrange(PetscDualSpace sp)
379 {
380   PetscDualSpace_Lag *lag = (PetscDualSpace_Lag *) sp->data;
381   DM                  dm    = sp->dm;
382   PetscInt            order = sp->order;
383   PetscInt            Nc    = sp->Nc;
384   PetscBool           continuous;
385   PetscSection        csection;
386   Vec                 coordinates;
387   PetscReal          *qpoints, *qweights;
388   PetscInt            depth, dim, pdimMax, pStart, pEnd, p, *pStratStart, *pStratEnd, coneSize, d, f = 0, c;
389   PetscBool           simplex, tensorSpace;
390   PetscErrorCode      ierr;
391 
392   PetscFunctionBegin;
393   /* Classify element type */
394   if (!order) lag->continuous = PETSC_FALSE;
395   continuous = lag->continuous;
396   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
397   ierr = DMPlexGetDepth(dm, &depth);CHKERRQ(ierr);
398   ierr = DMPlexGetChart(dm, &pStart, &pEnd);CHKERRQ(ierr);
399   ierr = PetscCalloc1(dim+1, &lag->numDof);CHKERRQ(ierr);
400   ierr = PetscMalloc2(depth+1,&pStratStart,depth+1,&pStratEnd);CHKERRQ(ierr);
401   for (d = 0; d <= depth; ++d) {ierr = DMPlexGetDepthStratum(dm, d, &pStratStart[d], &pStratEnd[d]);CHKERRQ(ierr);}
402   ierr = DMPlexGetConeSize(dm, pStratStart[depth], &coneSize);CHKERRQ(ierr);
403   ierr = DMGetCoordinateSection(dm, &csection);CHKERRQ(ierr);
404   ierr = DMGetCoordinatesLocal(dm, &coordinates);CHKERRQ(ierr);
405   if (depth == 1) {
406     if      (coneSize == dim+1)    simplex = PETSC_TRUE;
407     else if (coneSize == 1 << dim) simplex = PETSC_FALSE;
408     else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Only support simplices and tensor product cells");
409   } else if (depth == dim) {
410     if      (coneSize == dim+1)   simplex = PETSC_TRUE;
411     else if (coneSize == 2 * dim) simplex = PETSC_FALSE;
412     else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Only support simplices and tensor product cells");
413   } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Only support cell-vertex meshes or interpolated meshes");
414   lag->simplexCell = simplex;
415   if (dim > 1 && continuous && lag->simplexCell == lag->tensorSpace) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP, "Mismatching simplex/tensor cells and spaces only allowed for discontinuous elements");
416   tensorSpace    = lag->tensorSpace;
417   lag->height    = 0;
418   lag->subspaces = NULL;
419   if (continuous && sp->order > 0 && dim > 0) {
420     PetscInt i;
421 
422     lag->height = dim;
423     ierr = PetscMalloc1(dim,&lag->subspaces);CHKERRQ(ierr);
424     ierr = PetscDualSpaceCreateHeightSubspace_Lagrange(sp,1,&lag->subspaces[0]);CHKERRQ(ierr);
425     ierr = PetscDualSpaceSetUp(lag->subspaces[0]);CHKERRQ(ierr);
426     for (i = 1; i < dim; i++) {
427       ierr = PetscDualSpaceGetHeightSubspace(lag->subspaces[i-1],1,&lag->subspaces[i]);CHKERRQ(ierr);
428       ierr = PetscObjectReference((PetscObject)(lag->subspaces[i]));CHKERRQ(ierr);
429     }
430   }
431   ierr = PetscDualSpaceGetDimension_SingleCell_Lagrange(sp, sp->order, &pdimMax);CHKERRQ(ierr);
432   pdimMax *= (pStratEnd[depth] - pStratStart[depth]);
433   ierr = PetscMalloc1(pdimMax, &sp->functional);CHKERRQ(ierr);
434   if (!dim) {
435     for (c = 0; c < Nc; ++c) {
436       ierr = PetscQuadratureCreate(PETSC_COMM_SELF, &sp->functional[f]);CHKERRQ(ierr);
437       ierr = PetscCalloc1(Nc, &qweights);CHKERRQ(ierr);
438       ierr = PetscQuadratureSetOrder(sp->functional[f], 0);CHKERRQ(ierr);
439       ierr = PetscQuadratureSetData(sp->functional[f], 0, Nc, 1, NULL, qweights);CHKERRQ(ierr);
440       qweights[c] = 1.0;
441       ++f;
442       lag->numDof[0]++;
443     }
444   } else {
445     PetscInt     *tup;
446     PetscReal    *v0, *hv0, *J, *invJ, detJ, hdetJ;
447     PetscSection section;
448 
449     ierr = PetscSectionCreate(PETSC_COMM_SELF,&section);CHKERRQ(ierr);
450     ierr = PetscSectionSetChart(section,pStart,pEnd);CHKERRQ(ierr);
451     ierr = PetscCalloc5(dim+1,&tup,dim,&v0,dim,&hv0,dim*dim,&J,dim*dim,&invJ);CHKERRQ(ierr);
452     for (p = pStart; p < pEnd; p++) {
453       PetscInt       pointDim, d, nFunc = 0;
454       PetscDualSpace hsp;
455 
456       ierr = DMPlexComputeCellGeometryFEM(dm, p, NULL, v0, J, invJ, &detJ);CHKERRQ(ierr);
457       for (d = 0; d < depth; d++) {if (p >= pStratStart[d] && p < pStratEnd[d]) break;}
458       pointDim = (depth == 1 && d == 1) ? dim : d;
459       hsp = ((pointDim < dim) && lag->subspaces) ? lag->subspaces[dim - pointDim - 1] : NULL;
460       if (hsp) {
461         PetscDualSpace_Lag *hlag = (PetscDualSpace_Lag *) hsp->data;
462         DM                 hdm;
463 
464         ierr = PetscDualSpaceGetDM(hsp,&hdm);CHKERRQ(ierr);
465         ierr = DMPlexComputeCellGeometryFEM(hdm, 0, NULL, hv0, NULL, NULL, &hdetJ);CHKERRQ(ierr);
466         nFunc = lag->numDof[pointDim] = hlag->numDof[pointDim];
467       }
468       if (pointDim == dim) {
469         /* Cells, create for self */
470         PetscInt     orderEff = continuous ? (!tensorSpace ? order-1-dim : order-2) : order;
471         PetscReal    denom    = continuous ? order : (!tensorSpace ? order+1+dim : order+2);
472         PetscReal    numer    = (!simplex || !tensorSpace) ? 2. : (2./dim);
473         PetscReal    dx = numer/denom;
474         PetscInt     cdim, d, d2;
475 
476         if (orderEff < 0) continue;
477         ierr = PetscDualSpaceGetDimension_SingleCell_Lagrange(sp, orderEff, &cdim);CHKERRQ(ierr);
478         ierr = PetscMemzero(tup,(dim+1)*sizeof(PetscInt));CHKERRQ(ierr);
479         if (!tensorSpace) {
480           while (!tup[dim]) {
481             for (c = 0; c < Nc; ++c) {
482               ierr = PetscQuadratureCreate(PETSC_COMM_SELF, &sp->functional[f]);CHKERRQ(ierr);
483               ierr = PetscMalloc1(dim, &qpoints);CHKERRQ(ierr);
484               ierr = PetscCalloc1(Nc,  &qweights);CHKERRQ(ierr);
485               ierr = PetscQuadratureSetOrder(sp->functional[f], 0);CHKERRQ(ierr);
486               ierr = PetscQuadratureSetData(sp->functional[f], dim, Nc, 1, qpoints, qweights);CHKERRQ(ierr);
487               for (d = 0; d < dim; ++d) {
488                 qpoints[d] = v0[d];
489                 for (d2 = 0; d2 < dim; ++d2) qpoints[d] += J[d*dim+d2]*((tup[d2]+1)*dx);
490               }
491               qweights[c] = 1.0;
492               ++f;
493             }
494             ierr = LatticePointLexicographic_Internal(dim, orderEff, tup);CHKERRQ(ierr);
495           }
496         } else {
497           while (!tup[dim]) {
498             for (c = 0; c < Nc; ++c) {
499               ierr = PetscQuadratureCreate(PETSC_COMM_SELF, &sp->functional[f]);CHKERRQ(ierr);
500               ierr = PetscMalloc1(dim, &qpoints);CHKERRQ(ierr);
501               ierr = PetscCalloc1(Nc,  &qweights);CHKERRQ(ierr);
502               ierr = PetscQuadratureSetOrder(sp->functional[f], 0);CHKERRQ(ierr);
503               ierr = PetscQuadratureSetData(sp->functional[f], dim, Nc, 1, qpoints, qweights);CHKERRQ(ierr);
504               for (d = 0; d < dim; ++d) {
505                 qpoints[d] = v0[d];
506                 for (d2 = 0; d2 < dim; ++d2) qpoints[d] += J[d*dim+d2]*((tup[d2]+1)*dx);
507               }
508               qweights[c] = 1.0;
509               ++f;
510             }
511             ierr = TensorPointLexicographic_Internal(dim, orderEff, tup);CHKERRQ(ierr);
512           }
513         }
514         lag->numDof[dim] = cdim;
515       } else { /* transform functionals from subspaces */
516         PetscInt q;
517 
518         for (q = 0; q < nFunc; q++, f++) {
519           PetscQuadrature fn;
520           PetscInt        fdim, Nc, c, nPoints, i;
521           const PetscReal *points;
522           const PetscReal *weights;
523           PetscReal       *qpoints;
524           PetscReal       *qweights;
525 
526           ierr = PetscDualSpaceGetFunctional(hsp, q, &fn);CHKERRQ(ierr);
527           ierr = PetscQuadratureGetData(fn,&fdim,&Nc,&nPoints,&points,&weights);CHKERRQ(ierr);
528           if (fdim != pointDim) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_PLIB,"Expected height dual space dim %D, got %D",pointDim,fdim);
529           ierr = PetscMalloc1(nPoints * dim, &qpoints);CHKERRQ(ierr);
530           ierr = PetscCalloc1(nPoints * Nc,  &qweights);CHKERRQ(ierr);
531           for (i = 0; i < nPoints; i++) {
532             PetscInt  j, k;
533             PetscReal *qp = &qpoints[i * dim];
534 
535             for (c = 0; c < Nc; ++c) qweights[i*Nc+c] = weights[i*Nc+c];
536             for (j = 0; j < dim; ++j) qp[j] = v0[j];
537             for (j = 0; j < dim; ++j) {
538               for (k = 0; k < pointDim; k++) qp[j] += J[dim * j + k] * (points[pointDim * i + k] - hv0[k]);
539             }
540           }
541           ierr = PetscQuadratureCreate(PETSC_COMM_SELF, &sp->functional[f]);CHKERRQ(ierr);
542           ierr = PetscQuadratureSetOrder(sp->functional[f],0);CHKERRQ(ierr);
543           ierr = PetscQuadratureSetData(sp->functional[f],dim,Nc,nPoints,qpoints,qweights);CHKERRQ(ierr);
544         }
545       }
546       ierr = PetscSectionSetDof(section,p,lag->numDof[pointDim]);CHKERRQ(ierr);
547     }
548     ierr = PetscFree5(tup,v0,hv0,J,invJ);CHKERRQ(ierr);
549     ierr = PetscSectionSetUp(section);CHKERRQ(ierr);
550     { /* reorder to closure order */
551       PetscInt *key, count;
552       PetscQuadrature *reorder = NULL;
553 
554       ierr = PetscCalloc1(f,&key);CHKERRQ(ierr);
555       ierr = PetscMalloc1(f*sp->Nc,&reorder);CHKERRQ(ierr);
556 
557       for (p = pStratStart[depth], count = 0; p < pStratEnd[depth]; p++) {
558         PetscInt *closure = NULL, closureSize, c;
559 
560         ierr = DMPlexGetTransitiveClosure(dm,p,PETSC_TRUE,&closureSize,&closure);CHKERRQ(ierr);
561         for (c = 0; c < closureSize; c++) {
562           PetscInt point = closure[2 * c], dof, off, i;
563 
564           ierr = PetscSectionGetDof(section,point,&dof);CHKERRQ(ierr);
565           ierr = PetscSectionGetOffset(section,point,&off);CHKERRQ(ierr);
566           for (i = 0; i < dof; i++) {
567             PetscInt fi = i + off;
568             if (!key[fi]) {
569               key[fi] = 1;
570               reorder[count++] = sp->functional[fi];
571             }
572           }
573         }
574         ierr = DMPlexRestoreTransitiveClosure(dm,p,PETSC_TRUE,&closureSize,&closure);CHKERRQ(ierr);
575       }
576       ierr = PetscFree(sp->functional);CHKERRQ(ierr);
577       sp->functional = reorder;
578       ierr = PetscFree(key);CHKERRQ(ierr);
579     }
580     ierr = PetscSectionDestroy(&section);CHKERRQ(ierr);
581   }
582   if (pStratEnd[depth] == 1 && f != pdimMax) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of dual basis vectors %d not equal to dimension %d", f, pdimMax);
583   ierr = PetscFree2(pStratStart, pStratEnd);CHKERRQ(ierr);
584   if (f > pdimMax) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of dual basis vectors %d is greater than dimension %d", f, pdimMax);
585   PetscFunctionReturn(0);
586 }
587 
588 PetscErrorCode PetscDualSpaceDestroy_Lagrange(PetscDualSpace sp)
589 {
590   PetscDualSpace_Lag *lag = (PetscDualSpace_Lag *) sp->data;
591   PetscInt            i;
592   PetscErrorCode      ierr;
593 
594   PetscFunctionBegin;
595   if (lag->symmetries) {
596     PetscInt **selfSyms = lag->symmetries[0];
597 
598     if (selfSyms) {
599       PetscInt i, **allocated = &selfSyms[-lag->selfSymOff];
600 
601       for (i = 0; i < lag->numSelfSym; i++) {
602         ierr = PetscFree(allocated[i]);CHKERRQ(ierr);
603       }
604       ierr = PetscFree(allocated);CHKERRQ(ierr);
605     }
606     ierr = PetscFree(lag->symmetries);CHKERRQ(ierr);
607   }
608   for (i = 0; i < lag->height; i++) {
609     ierr = PetscDualSpaceDestroy(&lag->subspaces[i]);CHKERRQ(ierr);
610   }
611   ierr = PetscFree(lag->subspaces);CHKERRQ(ierr);
612   ierr = PetscFree(lag->numDof);CHKERRQ(ierr);
613   ierr = PetscFree(lag);CHKERRQ(ierr);
614   ierr = PetscObjectComposeFunction((PetscObject) sp, "PetscDualSpaceLagrangeGetContinuity_C", NULL);CHKERRQ(ierr);
615   ierr = PetscObjectComposeFunction((PetscObject) sp, "PetscDualSpaceLagrangeSetContinuity_C", NULL);CHKERRQ(ierr);
616   ierr = PetscObjectComposeFunction((PetscObject) sp, "PetscDualSpaceLagrangeGetTensor_C", NULL);CHKERRQ(ierr);
617   ierr = PetscObjectComposeFunction((PetscObject) sp, "PetscDualSpaceLagrangeSetTensor_C", NULL);CHKERRQ(ierr);
618   PetscFunctionReturn(0);
619 }
620 
621 PetscErrorCode PetscDualSpaceDuplicate_Lagrange(PetscDualSpace sp, PetscDualSpace *spNew)
622 {
623   PetscInt       order, Nc;
624   PetscBool      cont, tensor;
625   const char    *name;
626   PetscErrorCode ierr;
627 
628   PetscFunctionBegin;
629   ierr = PetscDualSpaceCreate(PetscObjectComm((PetscObject) sp), spNew);CHKERRQ(ierr);
630   ierr = PetscObjectGetName((PetscObject) sp,     &name);CHKERRQ(ierr);
631   ierr = PetscObjectSetName((PetscObject) *spNew,  name);CHKERRQ(ierr);
632   ierr = PetscDualSpaceSetType(*spNew, PETSCDUALSPACELAGRANGE);CHKERRQ(ierr);
633   ierr = PetscDualSpaceGetOrder(sp, &order);CHKERRQ(ierr);
634   ierr = PetscDualSpaceSetOrder(*spNew, order);CHKERRQ(ierr);
635   ierr = PetscDualSpaceGetNumComponents(sp, &Nc);CHKERRQ(ierr);
636   ierr = PetscDualSpaceSetNumComponents(*spNew, Nc);CHKERRQ(ierr);
637   ierr = PetscDualSpaceLagrangeGetContinuity(sp, &cont);CHKERRQ(ierr);
638   ierr = PetscDualSpaceLagrangeSetContinuity(*spNew, cont);CHKERRQ(ierr);
639   ierr = PetscDualSpaceLagrangeGetTensor(sp, &tensor);CHKERRQ(ierr);
640   ierr = PetscDualSpaceLagrangeSetTensor(*spNew, tensor);CHKERRQ(ierr);
641   PetscFunctionReturn(0);
642 }
643 
644 PetscErrorCode PetscDualSpaceSetFromOptions_Lagrange(PetscOptionItems *PetscOptionsObject,PetscDualSpace sp)
645 {
646   PetscBool      continuous, tensor, flg;
647   PetscErrorCode ierr;
648 
649   PetscFunctionBegin;
650   ierr = PetscDualSpaceLagrangeGetContinuity(sp, &continuous);CHKERRQ(ierr);
651   ierr = PetscDualSpaceLagrangeGetTensor(sp, &tensor);CHKERRQ(ierr);
652   ierr = PetscOptionsHead(PetscOptionsObject,"PetscDualSpace Lagrange Options");CHKERRQ(ierr);
653   ierr = PetscOptionsBool("-petscdualspace_lagrange_continuity", "Flag for continuous element", "PetscDualSpaceLagrangeSetContinuity", continuous, &continuous, &flg);CHKERRQ(ierr);
654   if (flg) {ierr = PetscDualSpaceLagrangeSetContinuity(sp, continuous);CHKERRQ(ierr);}
655   ierr = PetscOptionsBool("-petscdualspace_lagrange_tensor", "Flag for tensor dual space", "PetscDualSpaceLagrangeSetContinuity", tensor, &tensor, &flg);CHKERRQ(ierr);
656   if (flg) {ierr = PetscDualSpaceLagrangeSetTensor(sp, tensor);CHKERRQ(ierr);}
657   ierr = PetscOptionsTail();CHKERRQ(ierr);
658   PetscFunctionReturn(0);
659 }
660 
661 PetscErrorCode PetscDualSpaceGetDimension_Lagrange(PetscDualSpace sp, PetscInt *dim)
662 {
663   DM              K;
664   const PetscInt *numDof;
665   PetscInt        spatialDim, Nc, size = 0, d;
666   PetscErrorCode  ierr;
667 
668   PetscFunctionBegin;
669   ierr = PetscDualSpaceGetDM(sp, &K);CHKERRQ(ierr);
670   ierr = PetscDualSpaceGetNumDof(sp, &numDof);CHKERRQ(ierr);
671   ierr = DMGetDimension(K, &spatialDim);CHKERRQ(ierr);
672   ierr = DMPlexGetHeightStratum(K, 0, NULL, &Nc);CHKERRQ(ierr);
673   if (Nc == 1) {ierr = PetscDualSpaceGetDimension_SingleCell_Lagrange(sp, sp->order, dim);CHKERRQ(ierr); PetscFunctionReturn(0);}
674   for (d = 0; d <= spatialDim; ++d) {
675     PetscInt pStart, pEnd;
676 
677     ierr = DMPlexGetDepthStratum(K, d, &pStart, &pEnd);CHKERRQ(ierr);
678     size += (pEnd-pStart)*numDof[d];
679   }
680   *dim = size;
681   PetscFunctionReturn(0);
682 }
683 
684 PetscErrorCode PetscDualSpaceGetNumDof_Lagrange(PetscDualSpace sp, const PetscInt **numDof)
685 {
686   PetscDualSpace_Lag *lag = (PetscDualSpace_Lag *) sp->data;
687 
688   PetscFunctionBegin;
689   *numDof = lag->numDof;
690   PetscFunctionReturn(0);
691 }
692 
693 static PetscErrorCode PetscDualSpaceLagrangeGetContinuity_Lagrange(PetscDualSpace sp, PetscBool *continuous)
694 {
695   PetscDualSpace_Lag *lag = (PetscDualSpace_Lag *) sp->data;
696 
697   PetscFunctionBegin;
698   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 1);
699   PetscValidPointer(continuous, 2);
700   *continuous = lag->continuous;
701   PetscFunctionReturn(0);
702 }
703 
704 static PetscErrorCode PetscDualSpaceLagrangeSetContinuity_Lagrange(PetscDualSpace sp, PetscBool continuous)
705 {
706   PetscDualSpace_Lag *lag = (PetscDualSpace_Lag *) sp->data;
707 
708   PetscFunctionBegin;
709   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 1);
710   lag->continuous = continuous;
711   PetscFunctionReturn(0);
712 }
713 
714 /*@
715   PetscDualSpaceLagrangeGetContinuity - Retrieves the flag for element continuity
716 
717   Not Collective
718 
719   Input Parameter:
720 . sp         - the PetscDualSpace
721 
722   Output Parameter:
723 . continuous - flag for element continuity
724 
725   Level: intermediate
726 
727 .keywords: PetscDualSpace, Lagrange, continuous, discontinuous
728 .seealso: PetscDualSpaceLagrangeSetContinuity()
729 @*/
730 PetscErrorCode PetscDualSpaceLagrangeGetContinuity(PetscDualSpace sp, PetscBool *continuous)
731 {
732   PetscErrorCode ierr;
733 
734   PetscFunctionBegin;
735   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 1);
736   PetscValidPointer(continuous, 2);
737   ierr = PetscTryMethod(sp, "PetscDualSpaceLagrangeGetContinuity_C", (PetscDualSpace,PetscBool*),(sp,continuous));CHKERRQ(ierr);
738   PetscFunctionReturn(0);
739 }
740 
741 /*@
742   PetscDualSpaceLagrangeSetContinuity - Indicate whether the element is continuous
743 
744   Logically Collective on PetscDualSpace
745 
746   Input Parameters:
747 + sp         - the PetscDualSpace
748 - continuous - flag for element continuity
749 
750   Options Database:
751 . -petscdualspace_lagrange_continuity <bool>
752 
753   Level: intermediate
754 
755 .keywords: PetscDualSpace, Lagrange, continuous, discontinuous
756 .seealso: PetscDualSpaceLagrangeGetContinuity()
757 @*/
758 PetscErrorCode PetscDualSpaceLagrangeSetContinuity(PetscDualSpace sp, PetscBool continuous)
759 {
760   PetscErrorCode ierr;
761 
762   PetscFunctionBegin;
763   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 1);
764   PetscValidLogicalCollectiveBool(sp, continuous, 2);
765   ierr = PetscTryMethod(sp, "PetscDualSpaceLagrangeSetContinuity_C", (PetscDualSpace,PetscBool),(sp,continuous));CHKERRQ(ierr);
766   PetscFunctionReturn(0);
767 }
768 
769 PetscErrorCode PetscDualSpaceGetHeightSubspace_Lagrange(PetscDualSpace sp, PetscInt height, PetscDualSpace *bdsp)
770 {
771   PetscDualSpace_Lag *lag = (PetscDualSpace_Lag *) sp->data;
772   PetscErrorCode     ierr;
773 
774   PetscFunctionBegin;
775   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 1);
776   PetscValidPointer(bdsp,2);
777   if (height == 0) {
778     *bdsp = sp;
779   }
780   else {
781     DM dm;
782     PetscInt dim;
783 
784     ierr = PetscDualSpaceGetDM(sp,&dm);CHKERRQ(ierr);
785     ierr = DMGetDimension(dm,&dim);CHKERRQ(ierr);
786     if (height > dim || height < 0) {SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Asked for dual space at height %d for dimension %d reference element\n",height,dim);}
787     if (height <= lag->height) {
788       *bdsp = lag->subspaces[height-1];
789     }
790     else {
791       *bdsp = NULL;
792     }
793   }
794   PetscFunctionReturn(0);
795 }
796 
797 PetscErrorCode PetscDualSpaceInitialize_Lagrange(PetscDualSpace sp)
798 {
799   PetscFunctionBegin;
800   sp->ops->setfromoptions    = PetscDualSpaceSetFromOptions_Lagrange;
801   sp->ops->setup             = PetscDualSpaceSetUp_Lagrange;
802   sp->ops->view              = PetscDualSpaceView_Lagrange;
803   sp->ops->destroy           = PetscDualSpaceDestroy_Lagrange;
804   sp->ops->duplicate         = PetscDualSpaceDuplicate_Lagrange;
805   sp->ops->getdimension      = PetscDualSpaceGetDimension_Lagrange;
806   sp->ops->getnumdof         = PetscDualSpaceGetNumDof_Lagrange;
807   sp->ops->getheightsubspace = PetscDualSpaceGetHeightSubspace_Lagrange;
808   sp->ops->getsymmetries     = PetscDualSpaceGetSymmetries_Lagrange;
809   sp->ops->apply             = PetscDualSpaceApplyDefault;
810   sp->ops->applyall          = PetscDualSpaceApplyAllDefault;
811   sp->ops->createallpoints   = PetscDualSpaceCreateAllPointsDefault;
812   PetscFunctionReturn(0);
813 }
814 
815 /*MC
816   PETSCDUALSPACELAGRANGE = "lagrange" - A PetscDualSpace object that encapsulates a dual space of pointwise evaluation functionals
817 
818   Level: intermediate
819 
820 .seealso: PetscDualSpaceType, PetscDualSpaceCreate(), PetscDualSpaceSetType()
821 M*/
822 
823 PETSC_EXTERN PetscErrorCode PetscDualSpaceCreate_Lagrange(PetscDualSpace sp)
824 {
825   PetscDualSpace_Lag *lag;
826   PetscErrorCode      ierr;
827 
828   PetscFunctionBegin;
829   PetscValidHeaderSpecific(sp, PETSCDUALSPACE_CLASSID, 1);
830   ierr     = PetscNewLog(sp,&lag);CHKERRQ(ierr);
831   sp->data = lag;
832 
833   lag->numDof      = NULL;
834   lag->simplexCell = PETSC_TRUE;
835   lag->tensorSpace = PETSC_FALSE;
836   lag->continuous  = PETSC_TRUE;
837 
838   ierr = PetscDualSpaceInitialize_Lagrange(sp);CHKERRQ(ierr);
839   ierr = PetscObjectComposeFunction((PetscObject) sp, "PetscDualSpaceLagrangeGetContinuity_C", PetscDualSpaceLagrangeGetContinuity_Lagrange);CHKERRQ(ierr);
840   ierr = PetscObjectComposeFunction((PetscObject) sp, "PetscDualSpaceLagrangeSetContinuity_C", PetscDualSpaceLagrangeSetContinuity_Lagrange);CHKERRQ(ierr);
841   ierr = PetscObjectComposeFunction((PetscObject) sp, "PetscDualSpaceLagrangeGetTensor_C", PetscDualSpaceLagrangeGetTensor_Lagrange);CHKERRQ(ierr);
842   ierr = PetscObjectComposeFunction((PetscObject) sp, "PetscDualSpaceLagrangeSetTensor_C", PetscDualSpaceLagrangeSetTensor_Lagrange);CHKERRQ(ierr);
843   PetscFunctionReturn(0);
844 }
845 
846