xref: /libCEED/examples/petsc/src/petscutils.c (revision 2c58efb6e21741e7a04026bb6e2331c4d2d8bd66)
1e83e87a5Sjeremylt #include "../include/petscutils.h"
2e83e87a5Sjeremylt 
3e83e87a5Sjeremylt // -----------------------------------------------------------------------------
4e83e87a5Sjeremylt // Convert PETSc MemType to libCEED MemType
5e83e87a5Sjeremylt // -----------------------------------------------------------------------------
6e83e87a5Sjeremylt CeedMemType MemTypeP2C(PetscMemType mtype) {
7e83e87a5Sjeremylt   return PetscMemTypeDevice(mtype) ? CEED_MEM_DEVICE : CEED_MEM_HOST;
8e83e87a5Sjeremylt }
9e83e87a5Sjeremylt 
10e83e87a5Sjeremylt // -----------------------------------------------------------------------------
11e83e87a5Sjeremylt // Utility function taken from petsc/src/dm/impls/plex/examples/tutorials/ex7.c
12e83e87a5Sjeremylt // -----------------------------------------------------------------------------
13e83e87a5Sjeremylt PetscErrorCode ProjectToUnitSphere(DM dm) {
14e83e87a5Sjeremylt   Vec            coordinates;
15e83e87a5Sjeremylt   PetscScalar   *coords;
16e83e87a5Sjeremylt   PetscInt       Nv, v, dim, d;
17e83e87a5Sjeremylt   PetscErrorCode ierr;
18e83e87a5Sjeremylt 
19e83e87a5Sjeremylt   PetscFunctionBeginUser;
20e83e87a5Sjeremylt   ierr = DMGetCoordinatesLocal(dm, &coordinates); CHKERRQ(ierr);
21e83e87a5Sjeremylt   ierr = VecGetLocalSize(coordinates, &Nv); CHKERRQ(ierr);
22e83e87a5Sjeremylt   ierr = VecGetBlockSize(coordinates, &dim); CHKERRQ(ierr);
23e83e87a5Sjeremylt   Nv  /= dim;
24e83e87a5Sjeremylt   ierr = VecGetArray(coordinates, &coords); CHKERRQ(ierr);
25e83e87a5Sjeremylt   for (v = 0; v < Nv; ++v) {
26e83e87a5Sjeremylt     PetscReal r = 0.0;
27e83e87a5Sjeremylt 
28e83e87a5Sjeremylt     for (d = 0; d < dim; ++d) r += PetscSqr(PetscRealPart(coords[v*dim+d]));
29e83e87a5Sjeremylt     r = PetscSqrtReal(r);
30e83e87a5Sjeremylt     for (d = 0; d < dim; ++d) coords[v*dim+d] /= r;
31e83e87a5Sjeremylt   }
32e83e87a5Sjeremylt   ierr = VecRestoreArray(coordinates, &coords); CHKERRQ(ierr);
33e83e87a5Sjeremylt   PetscFunctionReturn(0);
34e83e87a5Sjeremylt };
35e83e87a5Sjeremylt 
36e83e87a5Sjeremylt // -----------------------------------------------------------------------------
37*2c58efb6Sjeremylt // Apply 3D Kershaw mesh transformation
38*2c58efb6Sjeremylt // -----------------------------------------------------------------------------
39*2c58efb6Sjeremylt // Transition from a value of "a" for x=0, to a value of "b" for x=1.  Optionally
40*2c58efb6Sjeremylt // smooth -- see the commented versions at the end.
41*2c58efb6Sjeremylt static double step(const double a, const double b, double x) {
42*2c58efb6Sjeremylt   if (x <= 0) return a;
43*2c58efb6Sjeremylt   if (x >= 1) return b;
44*2c58efb6Sjeremylt   return a + (b-a) * (x);
45*2c58efb6Sjeremylt }
46*2c58efb6Sjeremylt 
47*2c58efb6Sjeremylt // 1D transformation at the right boundary
48*2c58efb6Sjeremylt static double right(const double eps, const double x) {
49*2c58efb6Sjeremylt   return (x <= 0.5) ? (2-eps) * x : 1 + eps*(x-1);
50*2c58efb6Sjeremylt }
51*2c58efb6Sjeremylt 
52*2c58efb6Sjeremylt // 1D transformation at the left boundary
53*2c58efb6Sjeremylt static double left(const double eps, const double x) {
54*2c58efb6Sjeremylt   return 1-right(eps,1-x);
55*2c58efb6Sjeremylt }
56*2c58efb6Sjeremylt 
57*2c58efb6Sjeremylt // Apply 3D Kershaw mesh transformation
58*2c58efb6Sjeremylt // The eps parameters are in (0, 1]
59*2c58efb6Sjeremylt // Uniform mesh is recovered for eps=1
60*2c58efb6Sjeremylt PetscErrorCode kershaw(DM dmorig, PetscScalar eps) {
61*2c58efb6Sjeremylt   PetscErrorCode ierr;
62*2c58efb6Sjeremylt   Vec coord;
63*2c58efb6Sjeremylt   PetscInt ncoord;
64*2c58efb6Sjeremylt   PetscScalar *c;
65*2c58efb6Sjeremylt 
66*2c58efb6Sjeremylt   PetscFunctionBeginUser;
67*2c58efb6Sjeremylt   ierr = DMGetCoordinatesLocal(dmorig, &coord); CHKERRQ(ierr);
68*2c58efb6Sjeremylt   ierr = VecGetLocalSize(coord, &ncoord); CHKERRQ(ierr);
69*2c58efb6Sjeremylt   ierr = VecGetArray(coord, &c); CHKERRQ(ierr);
70*2c58efb6Sjeremylt 
71*2c58efb6Sjeremylt   for (PetscInt i = 0; i < ncoord; i += 3) {
72*2c58efb6Sjeremylt     PetscScalar x = c[i], y = c[i+1], z = c[i+2];
73*2c58efb6Sjeremylt     PetscInt layer = x*6;
74*2c58efb6Sjeremylt     PetscScalar lambda = (x-layer/6.0)*6;
75*2c58efb6Sjeremylt     c[i] = x;
76*2c58efb6Sjeremylt 
77*2c58efb6Sjeremylt     switch (layer) {
78*2c58efb6Sjeremylt     case 0:
79*2c58efb6Sjeremylt       c[i+1] = left(eps, y);
80*2c58efb6Sjeremylt       c[i+2] = left(eps, z);
81*2c58efb6Sjeremylt       break;
82*2c58efb6Sjeremylt     case 1:
83*2c58efb6Sjeremylt     case 4:
84*2c58efb6Sjeremylt       c[i+1] = step(left(eps, y), right(eps, y), lambda);
85*2c58efb6Sjeremylt       c[i+2] = step(left(eps, z), right(eps, z), lambda);
86*2c58efb6Sjeremylt       break;
87*2c58efb6Sjeremylt     case 2:
88*2c58efb6Sjeremylt       c[i+1] = step(right(eps, y), left(eps, y), lambda/2);
89*2c58efb6Sjeremylt       c[i+2] = step(right(eps, z), left(eps, z), lambda/2);
90*2c58efb6Sjeremylt       break;
91*2c58efb6Sjeremylt     case 3:
92*2c58efb6Sjeremylt       c[i+1] = step(right(eps, y), left(eps, y), (1+lambda)/2);
93*2c58efb6Sjeremylt       c[i+2] = step(right(eps, z), left(eps, z), (1+lambda)/2);
94*2c58efb6Sjeremylt       break;
95*2c58efb6Sjeremylt     default:
96*2c58efb6Sjeremylt       c[i+1] = right(eps, y);
97*2c58efb6Sjeremylt       c[i+2] = right(eps, z);
98*2c58efb6Sjeremylt     }
99*2c58efb6Sjeremylt   }
100*2c58efb6Sjeremylt   ierr = VecRestoreArray(coord, &c); CHKERRQ(ierr);
101*2c58efb6Sjeremylt   PetscFunctionReturn(0);
102*2c58efb6Sjeremylt }
103*2c58efb6Sjeremylt 
104*2c58efb6Sjeremylt // -----------------------------------------------------------------------------
105e83e87a5Sjeremylt // PETSc FE Boilerplate
106e83e87a5Sjeremylt // -----------------------------------------------------------------------------
107e83e87a5Sjeremylt PetscErrorCode PetscFECreateByDegree(DM dm, PetscInt dim, PetscInt Nc,
108e83e87a5Sjeremylt                                      PetscBool isSimplex, const char prefix[],
109e83e87a5Sjeremylt                                      PetscInt order, PetscFE *fem) {
110e83e87a5Sjeremylt   PetscQuadrature q, fq;
111e83e87a5Sjeremylt   DM              K;
112e83e87a5Sjeremylt   PetscSpace      P;
113e83e87a5Sjeremylt   PetscDualSpace  Q;
114e83e87a5Sjeremylt   PetscInt        quadPointsPerEdge;
115e83e87a5Sjeremylt   PetscBool       tensor = isSimplex ? PETSC_FALSE : PETSC_TRUE;
116e83e87a5Sjeremylt   PetscErrorCode  ierr;
117e83e87a5Sjeremylt 
118e83e87a5Sjeremylt   PetscFunctionBeginUser;
119e83e87a5Sjeremylt   /* Create space */
120e83e87a5Sjeremylt   ierr = PetscSpaceCreate(PetscObjectComm((PetscObject) dm), &P); CHKERRQ(ierr);
121e83e87a5Sjeremylt   ierr = PetscObjectSetOptionsPrefix((PetscObject) P, prefix); CHKERRQ(ierr);
122e83e87a5Sjeremylt   ierr = PetscSpacePolynomialSetTensor(P, tensor); CHKERRQ(ierr);
123e83e87a5Sjeremylt   ierr = PetscSpaceSetFromOptions(P); CHKERRQ(ierr);
124e83e87a5Sjeremylt   ierr = PetscSpaceSetNumComponents(P, Nc); CHKERRQ(ierr);
125e83e87a5Sjeremylt   ierr = PetscSpaceSetNumVariables(P, dim); CHKERRQ(ierr);
126e83e87a5Sjeremylt   ierr = PetscSpaceSetDegree(P, order, order); CHKERRQ(ierr);
127e83e87a5Sjeremylt   ierr = PetscSpaceSetUp(P); CHKERRQ(ierr);
128e83e87a5Sjeremylt   ierr = PetscSpacePolynomialGetTensor(P, &tensor); CHKERRQ(ierr);
129e83e87a5Sjeremylt   /* Create dual space */
130e83e87a5Sjeremylt   ierr = PetscDualSpaceCreate(PetscObjectComm((PetscObject) dm), &Q);
131e83e87a5Sjeremylt   CHKERRQ(ierr);
132e83e87a5Sjeremylt   ierr = PetscDualSpaceSetType(Q,PETSCDUALSPACELAGRANGE); CHKERRQ(ierr);
133e83e87a5Sjeremylt   ierr = PetscObjectSetOptionsPrefix((PetscObject) Q, prefix); CHKERRQ(ierr);
134e83e87a5Sjeremylt   ierr = PetscDualSpaceCreateReferenceCell(Q, dim, isSimplex, &K); CHKERRQ(ierr);
135e83e87a5Sjeremylt   ierr = PetscDualSpaceSetDM(Q, K); CHKERRQ(ierr);
136e83e87a5Sjeremylt   ierr = DMDestroy(&K); CHKERRQ(ierr);
137e83e87a5Sjeremylt   ierr = PetscDualSpaceSetNumComponents(Q, Nc); CHKERRQ(ierr);
138e83e87a5Sjeremylt   ierr = PetscDualSpaceSetOrder(Q, order); CHKERRQ(ierr);
139e83e87a5Sjeremylt   ierr = PetscDualSpaceLagrangeSetTensor(Q, tensor); CHKERRQ(ierr);
140e83e87a5Sjeremylt   ierr = PetscDualSpaceSetFromOptions(Q); CHKERRQ(ierr);
141e83e87a5Sjeremylt   ierr = PetscDualSpaceSetUp(Q); CHKERRQ(ierr);
142e83e87a5Sjeremylt   /* Create element */
143e83e87a5Sjeremylt   ierr = PetscFECreate(PetscObjectComm((PetscObject) dm), fem); CHKERRQ(ierr);
144e83e87a5Sjeremylt   ierr = PetscObjectSetOptionsPrefix((PetscObject) *fem, prefix); CHKERRQ(ierr);
145e83e87a5Sjeremylt   ierr = PetscFESetFromOptions(*fem); CHKERRQ(ierr);
146e83e87a5Sjeremylt   ierr = PetscFESetBasisSpace(*fem, P); CHKERRQ(ierr);
147e83e87a5Sjeremylt   ierr = PetscFESetDualSpace(*fem, Q); CHKERRQ(ierr);
148e83e87a5Sjeremylt   ierr = PetscFESetNumComponents(*fem, Nc); CHKERRQ(ierr);
149e83e87a5Sjeremylt   ierr = PetscFESetUp(*fem); CHKERRQ(ierr);
150e83e87a5Sjeremylt   ierr = PetscSpaceDestroy(&P); CHKERRQ(ierr);
151e83e87a5Sjeremylt   ierr = PetscDualSpaceDestroy(&Q); CHKERRQ(ierr);
152e83e87a5Sjeremylt   /* Create quadrature */
153e83e87a5Sjeremylt   quadPointsPerEdge = PetscMax(order + 1,1);
154e83e87a5Sjeremylt   if (isSimplex) {
155e83e87a5Sjeremylt     ierr = PetscDTStroudConicalQuadrature(dim,   1, quadPointsPerEdge, -1.0, 1.0,
156e83e87a5Sjeremylt                                           &q); CHKERRQ(ierr);
157e83e87a5Sjeremylt     ierr = PetscDTStroudConicalQuadrature(dim-1, 1, quadPointsPerEdge, -1.0, 1.0,
158e83e87a5Sjeremylt                                           &fq); CHKERRQ(ierr);
159e83e87a5Sjeremylt   } else {
160e83e87a5Sjeremylt     ierr = PetscDTGaussTensorQuadrature(dim,   1, quadPointsPerEdge, -1.0, 1.0,
161e83e87a5Sjeremylt                                         &q); CHKERRQ(ierr);
162e83e87a5Sjeremylt     ierr = PetscDTGaussTensorQuadrature(dim-1, 1, quadPointsPerEdge, -1.0, 1.0,
163e83e87a5Sjeremylt                                         &fq); CHKERRQ(ierr);
164e83e87a5Sjeremylt   }
165e83e87a5Sjeremylt   ierr = PetscFESetQuadrature(*fem, q); CHKERRQ(ierr);
166e83e87a5Sjeremylt   ierr = PetscFESetFaceQuadrature(*fem, fq); CHKERRQ(ierr);
167e83e87a5Sjeremylt   ierr = PetscQuadratureDestroy(&q); CHKERRQ(ierr);
168e83e87a5Sjeremylt   ierr = PetscQuadratureDestroy(&fq); CHKERRQ(ierr);
169e83e87a5Sjeremylt 
170e83e87a5Sjeremylt   PetscFunctionReturn(0);
171e83e87a5Sjeremylt };
172e83e87a5Sjeremylt 
173e83e87a5Sjeremylt // -----------------------------------------------------------------------------
174e83e87a5Sjeremylt // Create BC label
175e83e87a5Sjeremylt // -----------------------------------------------------------------------------
176e83e87a5Sjeremylt static PetscErrorCode CreateBCLabel(DM dm, const char name[]) {
177e83e87a5Sjeremylt   int ierr;
178e83e87a5Sjeremylt   DMLabel label;
179e83e87a5Sjeremylt 
180e83e87a5Sjeremylt   PetscFunctionBeginUser;
181e83e87a5Sjeremylt 
182e83e87a5Sjeremylt   ierr = DMCreateLabel(dm, name); CHKERRQ(ierr);
183e83e87a5Sjeremylt   ierr = DMGetLabel(dm, name, &label); CHKERRQ(ierr);
184e83e87a5Sjeremylt   ierr = DMPlexMarkBoundaryFaces(dm, 1, label); CHKERRQ(ierr);
185e83e87a5Sjeremylt   ierr = DMPlexLabelComplete(dm, label); CHKERRQ(ierr);
186e83e87a5Sjeremylt 
187e83e87a5Sjeremylt   PetscFunctionReturn(0);
188e83e87a5Sjeremylt };
189e83e87a5Sjeremylt 
190e83e87a5Sjeremylt // -----------------------------------------------------------------------------
191e83e87a5Sjeremylt // This function sets up a DM for a given degree
192e83e87a5Sjeremylt // -----------------------------------------------------------------------------
193e83e87a5Sjeremylt PetscErrorCode SetupDMByDegree(DM dm, PetscInt degree, PetscInt ncompu,
194e83e87a5Sjeremylt                                PetscInt dim, bool enforcebc, BCFunction bcsfunc) {
195e83e87a5Sjeremylt   PetscInt ierr, marker_ids[1] = {1};
196e83e87a5Sjeremylt   PetscFE fe;
197e83e87a5Sjeremylt 
198e83e87a5Sjeremylt   PetscFunctionBeginUser;
199e83e87a5Sjeremylt 
200e83e87a5Sjeremylt   // Setup FE
201e83e87a5Sjeremylt   ierr = PetscFECreateByDegree(dm, dim, ncompu, PETSC_FALSE, NULL, degree, &fe);
202e83e87a5Sjeremylt   CHKERRQ(ierr);
203e83e87a5Sjeremylt   ierr = DMSetFromOptions(dm); CHKERRQ(ierr);
204e83e87a5Sjeremylt   ierr = DMAddField(dm, NULL, (PetscObject)fe); CHKERRQ(ierr);
205e83e87a5Sjeremylt 
206e83e87a5Sjeremylt   // Setup DM
207e83e87a5Sjeremylt   ierr = DMCreateDS(dm); CHKERRQ(ierr);
208e83e87a5Sjeremylt   if (enforcebc) {
209e83e87a5Sjeremylt     PetscBool hasLabel;
210e83e87a5Sjeremylt     DMHasLabel(dm, "marker", &hasLabel);
211e83e87a5Sjeremylt     if (!hasLabel) {CreateBCLabel(dm, "marker");}
212e83e87a5Sjeremylt     ierr = DMAddBoundary(dm, DM_BC_ESSENTIAL, "wall", "marker", 0, 0, NULL,
213e83e87a5Sjeremylt                          (void(*)(void))bcsfunc, NULL, 1, marker_ids, NULL);
214e83e87a5Sjeremylt     CHKERRQ(ierr);
215e83e87a5Sjeremylt   }
216e83e87a5Sjeremylt   ierr = DMPlexSetClosurePermutationTensor(dm, PETSC_DETERMINE, NULL);
217e83e87a5Sjeremylt   CHKERRQ(ierr);
218e83e87a5Sjeremylt   ierr = PetscFEDestroy(&fe); CHKERRQ(ierr);
219e83e87a5Sjeremylt 
220e83e87a5Sjeremylt   PetscFunctionReturn(0);
221e83e87a5Sjeremylt };
222e83e87a5Sjeremylt 
223e83e87a5Sjeremylt // -----------------------------------------------------------------------------
224e83e87a5Sjeremylt // Utility function - essential BC dofs are encoded in closure indices as -(i+1)
225e83e87a5Sjeremylt // -----------------------------------------------------------------------------
226e83e87a5Sjeremylt PetscInt Involute(PetscInt i) {
227e83e87a5Sjeremylt   return i >= 0 ? i : -(i + 1);
228e83e87a5Sjeremylt };
229e83e87a5Sjeremylt 
230e83e87a5Sjeremylt // -----------------------------------------------------------------------------
231e83e87a5Sjeremylt // Get CEED restriction data from DMPlex
232e83e87a5Sjeremylt // -----------------------------------------------------------------------------
233e83e87a5Sjeremylt PetscErrorCode CreateRestrictionFromPlex(Ceed ceed, DM dm, CeedInt P,
234e83e87a5Sjeremylt     CeedInt topodim, CeedInt height, DMLabel domainLabel, CeedInt value,
235e83e87a5Sjeremylt     CeedElemRestriction *Erestrict) {
236e83e87a5Sjeremylt   PetscSection section;
237e83e87a5Sjeremylt   PetscInt p, Nelem, Ndof, *erestrict, eoffset, nfields, dim, depth;
238e83e87a5Sjeremylt   DMLabel depthLabel;
239e83e87a5Sjeremylt   IS depthIS, iterIS;
240e83e87a5Sjeremylt   Vec Uloc;
241e83e87a5Sjeremylt   const PetscInt *iterIndices;
242e83e87a5Sjeremylt   PetscErrorCode ierr;
243e83e87a5Sjeremylt 
244e83e87a5Sjeremylt   PetscFunctionBeginUser;
245e83e87a5Sjeremylt 
246e83e87a5Sjeremylt   ierr = DMGetDimension(dm, &dim); CHKERRQ(ierr);
247e83e87a5Sjeremylt   dim -= height;
248e83e87a5Sjeremylt   ierr = DMGetLocalSection(dm, &section); CHKERRQ(ierr);
249e83e87a5Sjeremylt   ierr = PetscSectionGetNumFields(section, &nfields); CHKERRQ(ierr);
250e83e87a5Sjeremylt   PetscInt ncomp[nfields], fieldoff[nfields+1];
251e83e87a5Sjeremylt   fieldoff[0] = 0;
252e83e87a5Sjeremylt   for (PetscInt f = 0; f < nfields; f++) {
253e83e87a5Sjeremylt     ierr = PetscSectionGetFieldComponents(section, f, &ncomp[f]); CHKERRQ(ierr);
254e83e87a5Sjeremylt     fieldoff[f+1] = fieldoff[f] + ncomp[f];
255e83e87a5Sjeremylt   }
256e83e87a5Sjeremylt 
257e83e87a5Sjeremylt   ierr = DMPlexGetDepth(dm, &depth); CHKERRQ(ierr);
258e83e87a5Sjeremylt   ierr = DMPlexGetDepthLabel(dm, &depthLabel); CHKERRQ(ierr);
259e83e87a5Sjeremylt   ierr = DMLabelGetStratumIS(depthLabel, depth - height, &depthIS); CHKERRQ(ierr);
260e83e87a5Sjeremylt   if (domainLabel) {
261e83e87a5Sjeremylt     IS domainIS;
262e83e87a5Sjeremylt     ierr = DMLabelGetStratumIS(domainLabel, value, &domainIS); CHKERRQ(ierr);
263e83e87a5Sjeremylt     if (domainIS) { // domainIS is non-empty
264e83e87a5Sjeremylt       ierr = ISIntersect(depthIS, domainIS, &iterIS); CHKERRQ(ierr);
265e83e87a5Sjeremylt       ierr = ISDestroy(&domainIS); CHKERRQ(ierr);
266e83e87a5Sjeremylt     } else { // domainIS is NULL (empty)
267e83e87a5Sjeremylt       iterIS = NULL;
268e83e87a5Sjeremylt     }
269e83e87a5Sjeremylt     ierr = ISDestroy(&depthIS); CHKERRQ(ierr);
270e83e87a5Sjeremylt   } else {
271e83e87a5Sjeremylt     iterIS = depthIS;
272e83e87a5Sjeremylt   }
273e83e87a5Sjeremylt   if (iterIS) {
274e83e87a5Sjeremylt     ierr = ISGetLocalSize(iterIS, &Nelem); CHKERRQ(ierr);
275e83e87a5Sjeremylt     ierr = ISGetIndices(iterIS, &iterIndices); CHKERRQ(ierr);
276e83e87a5Sjeremylt   } else {
277e83e87a5Sjeremylt     Nelem = 0;
278e83e87a5Sjeremylt     iterIndices = NULL;
279e83e87a5Sjeremylt   }
280e83e87a5Sjeremylt   ierr = PetscMalloc1(Nelem*PetscPowInt(P, topodim), &erestrict); CHKERRQ(ierr);
281e83e87a5Sjeremylt   for (p = 0, eoffset = 0; p < Nelem; p++) {
282e83e87a5Sjeremylt     PetscInt c = iterIndices[p];
283e83e87a5Sjeremylt     PetscInt numindices, *indices, nnodes;
284e83e87a5Sjeremylt     ierr = DMPlexGetClosureIndices(dm, section, section, c, PETSC_TRUE,
285e83e87a5Sjeremylt                                    &numindices, &indices, NULL, NULL);
286e83e87a5Sjeremylt     CHKERRQ(ierr);
287e83e87a5Sjeremylt     bool flip = false;
288e83e87a5Sjeremylt     if (height > 0) {
289e83e87a5Sjeremylt       PetscInt numCells, numFaces, start = -1;
290e83e87a5Sjeremylt       const PetscInt *orients, *faces, *cells;
291e83e87a5Sjeremylt       ierr = DMPlexGetSupport(dm, c, &cells); CHKERRQ(ierr);
292e83e87a5Sjeremylt       ierr = DMPlexGetSupportSize(dm, c, &numCells); CHKERRQ(ierr);
293e83e87a5Sjeremylt       if (numCells != 1) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP,
294e83e87a5Sjeremylt                                     "Expected one cell in support of exterior face, but got %D cells",
295e83e87a5Sjeremylt                                     numCells);
296e83e87a5Sjeremylt       ierr = DMPlexGetCone(dm, cells[0], &faces); CHKERRQ(ierr);
297e83e87a5Sjeremylt       ierr = DMPlexGetConeSize(dm, cells[0], &numFaces); CHKERRQ(ierr);
298e83e87a5Sjeremylt       for (PetscInt i=0; i<numFaces; i++) {if (faces[i] == c) start = i;}
299e83e87a5Sjeremylt       if (start < 0) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_CORRUPT,
300e83e87a5Sjeremylt                                 "Could not find face %D in cone of its support",
301e83e87a5Sjeremylt                                 c);
302e83e87a5Sjeremylt       ierr = DMPlexGetConeOrientation(dm, cells[0], &orients); CHKERRQ(ierr);
303e83e87a5Sjeremylt       if (orients[start] < 0) flip = true;
304e83e87a5Sjeremylt     }
305e83e87a5Sjeremylt     if (numindices % fieldoff[nfields]) SETERRQ1(PETSC_COMM_SELF,
306e83e87a5Sjeremylt           PETSC_ERR_ARG_INCOMP, "Number of closure indices not compatible with Cell %D",
307e83e87a5Sjeremylt           c);
308e83e87a5Sjeremylt     nnodes = numindices / fieldoff[nfields];
309e83e87a5Sjeremylt     for (PetscInt i = 0; i < nnodes; i++) {
310e83e87a5Sjeremylt       PetscInt ii = i;
311e83e87a5Sjeremylt       if (flip) {
312e83e87a5Sjeremylt         if (P == nnodes) ii = nnodes - 1 - i;
313e83e87a5Sjeremylt         else if (P*P == nnodes) {
314e83e87a5Sjeremylt           PetscInt row = i / P, col = i % P;
315e83e87a5Sjeremylt           ii = row + col * P;
316e83e87a5Sjeremylt         } else SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_SUP,
317e83e87a5Sjeremylt                           "No support for flipping point with %D nodes != P (%D) or P^2",
318e83e87a5Sjeremylt                           nnodes, P);
319e83e87a5Sjeremylt       }
320e83e87a5Sjeremylt       // Check that indices are blocked by node and thus can be coalesced as a single field with
321e83e87a5Sjeremylt       // fieldoff[nfields] = sum(ncomp) components.
322e83e87a5Sjeremylt       for (PetscInt f = 0; f < nfields; f++) {
323e83e87a5Sjeremylt         for (PetscInt j = 0; j < ncomp[f]; j++) {
324e83e87a5Sjeremylt           if (Involute(indices[fieldoff[f]*nnodes + ii*ncomp[f] + j])
325e83e87a5Sjeremylt               != Involute(indices[ii*ncomp[0]]) + fieldoff[f] + j)
326e83e87a5Sjeremylt             SETERRQ4(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP,
327e83e87a5Sjeremylt                      "Cell %D closure indices not interlaced for node %D field %D component %D",
328e83e87a5Sjeremylt                      c, ii, f, j);
329e83e87a5Sjeremylt         }
330e83e87a5Sjeremylt       }
331e83e87a5Sjeremylt       // Essential boundary conditions are encoded as -(loc+1), but we don't care so we decode.
332e83e87a5Sjeremylt       PetscInt loc = Involute(indices[ii*ncomp[0]]);
333e83e87a5Sjeremylt       erestrict[eoffset++] = loc;
334e83e87a5Sjeremylt     }
335e83e87a5Sjeremylt     ierr = DMPlexRestoreClosureIndices(dm, section, section, c, PETSC_TRUE,
336e83e87a5Sjeremylt                                        &numindices, &indices, NULL, NULL);
337e83e87a5Sjeremylt     CHKERRQ(ierr);
338e83e87a5Sjeremylt   }
339e83e87a5Sjeremylt   if (eoffset != Nelem*PetscPowInt(P, topodim))
340e83e87a5Sjeremylt     SETERRQ3(PETSC_COMM_SELF, PETSC_ERR_LIB,
341e83e87a5Sjeremylt              "ElemRestriction of size (%D,%D) initialized %D nodes", Nelem,
342e83e87a5Sjeremylt              PetscPowInt(P, topodim),eoffset);
343e83e87a5Sjeremylt   if (iterIS) {
344e83e87a5Sjeremylt     ierr = ISRestoreIndices(iterIS, &iterIndices); CHKERRQ(ierr);
345e83e87a5Sjeremylt   }
346e83e87a5Sjeremylt   ierr = ISDestroy(&iterIS); CHKERRQ(ierr);
347e83e87a5Sjeremylt 
348e83e87a5Sjeremylt   ierr = DMGetLocalVector(dm, &Uloc); CHKERRQ(ierr);
349e83e87a5Sjeremylt   ierr = VecGetLocalSize(Uloc, &Ndof); CHKERRQ(ierr);
350e83e87a5Sjeremylt   ierr = DMRestoreLocalVector(dm, &Uloc); CHKERRQ(ierr);
351e83e87a5Sjeremylt   CeedElemRestrictionCreate(ceed, Nelem, PetscPowInt(P, topodim),
352e83e87a5Sjeremylt                             fieldoff[nfields],
353e83e87a5Sjeremylt                             1, Ndof, CEED_MEM_HOST, CEED_COPY_VALUES, erestrict,
354e83e87a5Sjeremylt                             Erestrict);
355e83e87a5Sjeremylt   ierr = PetscFree(erestrict); CHKERRQ(ierr);
356e83e87a5Sjeremylt   PetscFunctionReturn(0);
357e83e87a5Sjeremylt };
358e83e87a5Sjeremylt 
359e83e87a5Sjeremylt // -----------------------------------------------------------------------------
360