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