xref: /petsc/src/dm/impls/plex/transform/impls/extrude/plextrextrude.c (revision c8b0c2b58db46088bcb0f3a2497e96cb9efa794b)
1 #include <petsc/private/dmplextransformimpl.h> /*I "petscdmplextransform.h" I*/
2 
3 const char *const PlexNormalAlgs[] = {"default", "input", "compute", "compute_bd"};
4 
5 static PetscErrorCode DMPlexTransformView_Extrude(DMPlexTransform tr, PetscViewer viewer)
6 {
7   DMPlexTransform_Extrude *ex = (DMPlexTransform_Extrude *)tr->data;
8   PetscBool                isascii;
9 
10   PetscFunctionBegin;
11   PetscValidHeaderSpecific(tr, DMPLEXTRANSFORM_CLASSID, 1);
12   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
13   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &isascii));
14   if (isascii) {
15     DM          dm;
16     DMLabel     active;
17     PetscInt    dim;
18     const char *name;
19 
20     PetscCall(PetscObjectGetName((PetscObject)tr, &name));
21     PetscCall(DMPlexTransformGetDM(tr, &dm));
22     PetscCall(DMGetDimension(dm, &dim));
23     PetscCall(DMPlexTransformGetActive(tr, &active));
24 
25     PetscCall(PetscViewerASCIIPrintf(viewer, "Extrusion transformation %s\n", name ? name : ""));
26     PetscCall(PetscViewerASCIIPrintf(viewer, "  number of layers: %" PetscInt_FMT "\n", ex->layers));
27     PetscCall(PetscViewerASCIIPrintf(viewer, "  create tensor cells: %s\n", ex->useTensor ? "YES" : "NO"));
28     if (ex->periodic) PetscCall(PetscViewerASCIIPrintf(viewer, "  periodic\n"));
29     PetscCall(PetscViewerASCIIPrintf(viewer, "  normal algorithm: %s\n", PlexNormalAlgs[ex->normalAlg]));
30     if (ex->normalFunc) PetscCall(PetscViewerASCIIPrintf(viewer, "  normal modified by user function\n"));
31   } else {
32     SETERRQ(PetscObjectComm((PetscObject)tr), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlexTransform writing", ((PetscObject)viewer)->type_name);
33   }
34   PetscFunctionReturn(PETSC_SUCCESS);
35 }
36 
37 static PetscErrorCode DMPlexTransformSetFromOptions_Extrude(DMPlexTransform tr, PetscOptionItems PetscOptionsObject)
38 {
39   DMPlexTransform_Extrude *ex = (DMPlexTransform_Extrude *)tr->data;
40   PetscReal                th, normal[3], *thicknesses;
41   PetscInt                 nl, Nc;
42   PetscBool                tensor, sym, per, flg;
43   char                     funcname[PETSC_MAX_PATH_LEN];
44 
45   PetscFunctionBegin;
46   PetscOptionsHeadBegin(PetscOptionsObject, "DMPlexTransform Extrusion Options");
47   PetscCall(PetscOptionsBoundedInt("-dm_plex_transform_extrude_layers", "Number of layers to extrude", "", ex->layers, &nl, &flg, 1));
48   if (flg) PetscCall(DMPlexTransformExtrudeSetLayers(tr, nl));
49   PetscCall(PetscOptionsReal("-dm_plex_transform_extrude_thickness", "Total thickness of extruded layers", "", ex->thickness, &th, &flg));
50   if (flg) PetscCall(DMPlexTransformExtrudeSetThickness(tr, th));
51   PetscCall(PetscOptionsBool("-dm_plex_transform_extrude_use_tensor", "Create tensor cells", "", ex->useTensor, &tensor, &flg));
52   if (flg) PetscCall(DMPlexTransformExtrudeSetTensor(tr, tensor));
53   PetscCall(PetscOptionsBool("-dm_plex_transform_extrude_symmetric", "Extrude layers symmetrically about the surface", "", ex->symmetric, &sym, &flg));
54   if (flg) PetscCall(DMPlexTransformExtrudeSetSymmetric(tr, sym));
55   PetscCall(PetscOptionsBool("-dm_plex_transform_extrude_periodic", "Extrude layers periodically about the surface", "", ex->periodic, &per, &flg));
56   if (flg) PetscCall(DMPlexTransformExtrudeSetPeriodic(tr, per));
57   Nc = 3;
58   PetscCall(PetscOptionsRealArray("-dm_plex_transform_extrude_normal", "Input normal vector for extrusion", "DMPlexTransformExtrudeSetNormal", normal, &Nc, &flg));
59   if (flg) {
60     // Extrusion dimension might not yet be determined
61     PetscCheck(!ex->cdimEx || Nc == ex->cdimEx, PetscObjectComm((PetscObject)tr), PETSC_ERR_ARG_SIZ, "Input normal has size %" PetscInt_FMT " != %" PetscInt_FMT " extruded coordinate dimension", Nc, ex->cdimEx);
62     PetscCall(DMPlexTransformExtrudeSetNormal(tr, normal));
63   }
64   PetscCall(PetscOptionsString("-dm_plex_transform_extrude_normal_function", "Function to determine normal vector", "DMPlexTransformExtrudeSetNormalFunction", funcname, funcname, sizeof(funcname), &flg));
65   if (flg) {
66     PetscSimplePointFn *normalFunc;
67 
68     PetscCall(PetscDLSym(NULL, funcname, (void **)&normalFunc));
69     PetscCall(DMPlexTransformExtrudeSetNormalFunction(tr, normalFunc));
70   }
71   nl = ex->layers;
72   PetscCall(PetscMalloc1(nl, &thicknesses));
73   PetscCall(PetscOptionsRealArray("-dm_plex_transform_extrude_thicknesses", "Thickness of each individual extruded layer", "", thicknesses, &nl, &flg));
74   if (flg) {
75     PetscCheck(nl, PetscObjectComm((PetscObject)tr), PETSC_ERR_ARG_OUTOFRANGE, "Must give at least one thickness for -dm_plex_transform_extrude_thicknesses");
76     PetscCall(DMPlexTransformExtrudeSetThicknesses(tr, nl, thicknesses));
77   }
78   PetscCall(PetscFree(thicknesses));
79   PetscOptionsHeadEnd();
80   PetscFunctionReturn(PETSC_SUCCESS);
81 }
82 
83 /* Determine the implicit dimension pre-extrusion (either the implicit dimension of the DM or of a point in the active set for the transform).
84    If that dimension is the same as the current coordinate dimension (ex->dim), the extruded mesh will have a coordinate dimension one greater;
85    Otherwise the coordinate dimension will be kept. */
86 static PetscErrorCode DMPlexTransformExtrudeComputeExtrusionDim(DMPlexTransform tr)
87 {
88   DMPlexTransform_Extrude *ex = (DMPlexTransform_Extrude *)tr->data;
89   DM                       dm;
90   DMLabel                  active;
91   PetscInt                 dim, dimExtPoint, dimExtPointG;
92 
93   PetscFunctionBegin;
94   PetscCall(DMPlexTransformGetDM(tr, &dm));
95   PetscCall(DMGetDimension(dm, &dim));
96   PetscCall(DMPlexTransformGetActive(tr, &active));
97   if (active) {
98     IS              valueIS, pointIS;
99     const PetscInt *values, *points;
100     DMPolytopeType  ct;
101     PetscInt        Nv, Np;
102 
103     dimExtPoint = 0;
104     PetscCall(DMLabelGetValueIS(active, &valueIS));
105     PetscCall(ISGetLocalSize(valueIS, &Nv));
106     PetscCall(ISGetIndices(valueIS, &values));
107     for (PetscInt v = 0; v < Nv; ++v) {
108       PetscCall(DMLabelGetStratumIS(active, values[v], &pointIS));
109       PetscCall(ISGetLocalSize(pointIS, &Np));
110       PetscCall(ISGetIndices(pointIS, &points));
111       for (PetscInt p = 0; p < Np; ++p) {
112         PetscCall(DMPlexGetCellType(dm, points[p], &ct));
113         dimExtPoint = PetscMax(dimExtPoint, DMPolytopeTypeGetDim(ct));
114       }
115       PetscCall(ISRestoreIndices(pointIS, &points));
116       PetscCall(ISDestroy(&pointIS));
117     }
118     PetscCall(ISRestoreIndices(valueIS, &values));
119     PetscCall(ISDestroy(&valueIS));
120   } else dimExtPoint = dim;
121   PetscCallMPI(MPIU_Allreduce(&dimExtPoint, &dimExtPointG, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)tr)));
122   ex->dimEx  = PetscMax(dim, dimExtPointG + 1);
123   ex->cdimEx = ex->cdim == dimExtPointG ? ex->cdim + 1 : ex->cdim;
124   PetscCheck(ex->dimEx <= 3, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Topological dimension for extruded mesh %" PetscInt_FMT " must not exceed 3", ex->dimEx);
125   PetscCheck(ex->cdimEx <= 3, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate dimension for extruded mesh %" PetscInt_FMT " must not exceed 3", ex->cdimEx);
126   PetscFunctionReturn(PETSC_SUCCESS);
127 }
128 
129 static PetscErrorCode DMPlexTransformSetDimensions_Extrude(DMPlexTransform tr, DM dm, DM tdm)
130 {
131   DMPlexTransform_Extrude *ex = (DMPlexTransform_Extrude *)tr->data;
132   PetscInt                 dim;
133 
134   PetscFunctionBegin;
135   PetscCall(DMGetDimension(dm, &dim));
136   PetscCall(DMSetDimension(tdm, ex->dimEx));
137   PetscCall(DMSetCoordinateDim(tdm, ex->cdimEx));
138   PetscFunctionReturn(PETSC_SUCCESS);
139 }
140 
141 /* DM_POLYTOPE_POINT produces
142      Nl+1 points, or Nl points periodically and
143      Nl segments, or tensor segments
144 */
145 static PetscErrorCode DMPlexTransformExtrudeSetUp_Point(DMPlexTransform_Extrude *ex, PetscInt Nl)
146 {
147   const DMPolytopeType ct = DM_POLYTOPE_POINT;
148   const PetscInt       Np = ex->periodic ? Nl : Nl + 1;
149   PetscInt             Nc, No;
150 
151   PetscFunctionBegin;
152   ex->Nt[ct] = 2;
153   Nc         = 6 * Nl;
154   No         = 2 * Nl;
155   PetscCall(PetscMalloc4(ex->Nt[ct], &ex->target[ct], ex->Nt[ct], &ex->size[ct], Nc, &ex->cone[ct], No, &ex->ornt[ct]));
156   ex->target[ct][0] = DM_POLYTOPE_POINT;
157   ex->target[ct][1] = ex->useTensor ? DM_POLYTOPE_POINT_PRISM_TENSOR : DM_POLYTOPE_SEGMENT;
158   ex->size[ct][0]   = Np;
159   ex->size[ct][1]   = Nl;
160   /*   cones for segments/tensor segments */
161   for (PetscInt i = 0; i < Nl; ++i) {
162     ex->cone[ct][6 * i + 0] = DM_POLYTOPE_POINT;
163     ex->cone[ct][6 * i + 1] = 0;
164     ex->cone[ct][6 * i + 2] = i;
165     ex->cone[ct][6 * i + 3] = DM_POLYTOPE_POINT;
166     ex->cone[ct][6 * i + 4] = 0;
167     ex->cone[ct][6 * i + 5] = (i + 1) % Np;
168   }
169   for (PetscInt i = 0; i < No; ++i) ex->ornt[ct][i] = 0;
170   PetscFunctionReturn(PETSC_SUCCESS);
171 }
172 
173 /* DM_POLYTOPE_SEGMENT produces
174      Nl+1 segments, or Nl segments periodically and
175      Nl quads, or tensor quads
176 */
177 static PetscErrorCode DMPlexTransformExtrudeSetUp_Segment(DMPlexTransform_Extrude *ex, PetscInt Nl)
178 {
179   const DMPolytopeType ct = DM_POLYTOPE_SEGMENT;
180   const PetscInt       Np = ex->periodic ? Nl : Nl + 1;
181   PetscInt             Nc, No, coff, ooff;
182 
183   PetscFunctionBegin;
184   ex->Nt[ct] = 2;
185   Nc         = 8 * Np + 14 * Nl;
186   No         = 2 * Np + 4 * Nl;
187   PetscCall(PetscMalloc4(ex->Nt[ct], &ex->target[ct], ex->Nt[ct], &ex->size[ct], Nc, &ex->cone[ct], No, &ex->ornt[ct]));
188   ex->target[ct][0] = DM_POLYTOPE_SEGMENT;
189   ex->target[ct][1] = ex->useTensor ? DM_POLYTOPE_SEG_PRISM_TENSOR : DM_POLYTOPE_QUADRILATERAL;
190   ex->size[ct][0]   = Np;
191   ex->size[ct][1]   = Nl;
192   /*   cones for segments */
193   for (PetscInt i = 0; i < Np; ++i) {
194     ex->cone[ct][8 * i + 0] = DM_POLYTOPE_POINT;
195     ex->cone[ct][8 * i + 1] = 1;
196     ex->cone[ct][8 * i + 2] = 0;
197     ex->cone[ct][8 * i + 3] = i;
198     ex->cone[ct][8 * i + 4] = DM_POLYTOPE_POINT;
199     ex->cone[ct][8 * i + 5] = 1;
200     ex->cone[ct][8 * i + 6] = 1;
201     ex->cone[ct][8 * i + 7] = i;
202   }
203   for (PetscInt i = 0; i < 2 * Np; ++i) ex->ornt[ct][i] = 0;
204   /*   cones for quads/tensor quads */
205   coff = 8 * Np;
206   ooff = 2 * Np;
207   for (PetscInt i = 0; i < Nl; ++i) {
208     if (ex->useTensor) {
209       ex->cone[ct][coff + 14 * i + 0]  = DM_POLYTOPE_SEGMENT;
210       ex->cone[ct][coff + 14 * i + 1]  = 0;
211       ex->cone[ct][coff + 14 * i + 2]  = i;
212       ex->cone[ct][coff + 14 * i + 3]  = DM_POLYTOPE_SEGMENT;
213       ex->cone[ct][coff + 14 * i + 4]  = 0;
214       ex->cone[ct][coff + 14 * i + 5]  = (i + 1) % Np;
215       ex->cone[ct][coff + 14 * i + 6]  = DM_POLYTOPE_POINT_PRISM_TENSOR;
216       ex->cone[ct][coff + 14 * i + 7]  = 1;
217       ex->cone[ct][coff + 14 * i + 8]  = 0;
218       ex->cone[ct][coff + 14 * i + 9]  = i;
219       ex->cone[ct][coff + 14 * i + 10] = DM_POLYTOPE_POINT_PRISM_TENSOR;
220       ex->cone[ct][coff + 14 * i + 11] = 1;
221       ex->cone[ct][coff + 14 * i + 12] = 1;
222       ex->cone[ct][coff + 14 * i + 13] = i;
223       ex->ornt[ct][ooff + 4 * i + 0]   = 0;
224       ex->ornt[ct][ooff + 4 * i + 1]   = 0;
225       ex->ornt[ct][ooff + 4 * i + 2]   = 0;
226       ex->ornt[ct][ooff + 4 * i + 3]   = 0;
227     } else {
228       ex->cone[ct][coff + 14 * i + 0]  = DM_POLYTOPE_SEGMENT;
229       ex->cone[ct][coff + 14 * i + 1]  = 0;
230       ex->cone[ct][coff + 14 * i + 2]  = i;
231       ex->cone[ct][coff + 14 * i + 3]  = DM_POLYTOPE_SEGMENT;
232       ex->cone[ct][coff + 14 * i + 4]  = 1;
233       ex->cone[ct][coff + 14 * i + 5]  = 1;
234       ex->cone[ct][coff + 14 * i + 6]  = i;
235       ex->cone[ct][coff + 14 * i + 7]  = DM_POLYTOPE_SEGMENT;
236       ex->cone[ct][coff + 14 * i + 8]  = 0;
237       ex->cone[ct][coff + 14 * i + 9]  = (i + 1) % Np;
238       ex->cone[ct][coff + 14 * i + 10] = DM_POLYTOPE_SEGMENT;
239       ex->cone[ct][coff + 14 * i + 11] = 1;
240       ex->cone[ct][coff + 14 * i + 12] = 0;
241       ex->cone[ct][coff + 14 * i + 13] = i;
242       ex->ornt[ct][ooff + 4 * i + 0]   = 0;
243       ex->ornt[ct][ooff + 4 * i + 1]   = 0;
244       ex->ornt[ct][ooff + 4 * i + 2]   = -1;
245       ex->ornt[ct][ooff + 4 * i + 3]   = -1;
246     }
247   }
248   PetscFunctionReturn(PETSC_SUCCESS);
249 }
250 
251 /* DM_POLYTOPE_TRIANGLE produces
252      Nl+1 triangles, or Nl triangles periodically and
253      Nl triangular prisms/tensor triangular prisms
254 */
255 static PetscErrorCode DMPlexTransformExtrudeSetUp_Triangle(DMPlexTransform_Extrude *ex, PetscInt Nl)
256 {
257   const DMPolytopeType ct = DM_POLYTOPE_TRIANGLE;
258   const PetscInt       Np = ex->periodic ? Nl : Nl + 1;
259   PetscInt             Nc, No, coff, ooff;
260 
261   PetscFunctionBegin;
262   ex->Nt[ct] = 2;
263   Nc         = 12 * Np + 18 * Nl;
264   No         = 3 * Np + 5 * Nl;
265   PetscCall(PetscMalloc4(ex->Nt[ct], &ex->target[ct], ex->Nt[ct], &ex->size[ct], Nc, &ex->cone[ct], No, &ex->ornt[ct]));
266   ex->target[ct][0] = DM_POLYTOPE_TRIANGLE;
267   ex->target[ct][1] = ex->useTensor ? DM_POLYTOPE_TRI_PRISM_TENSOR : DM_POLYTOPE_TRI_PRISM;
268   ex->size[ct][0]   = Np;
269   ex->size[ct][1]   = Nl;
270   /*   cones for triangles */
271   for (PetscInt i = 0; i < Np; ++i) {
272     ex->cone[ct][12 * i + 0]  = DM_POLYTOPE_SEGMENT;
273     ex->cone[ct][12 * i + 1]  = 1;
274     ex->cone[ct][12 * i + 2]  = 0;
275     ex->cone[ct][12 * i + 3]  = i;
276     ex->cone[ct][12 * i + 4]  = DM_POLYTOPE_SEGMENT;
277     ex->cone[ct][12 * i + 5]  = 1;
278     ex->cone[ct][12 * i + 6]  = 1;
279     ex->cone[ct][12 * i + 7]  = i;
280     ex->cone[ct][12 * i + 8]  = DM_POLYTOPE_SEGMENT;
281     ex->cone[ct][12 * i + 9]  = 1;
282     ex->cone[ct][12 * i + 10] = 2;
283     ex->cone[ct][12 * i + 11] = i;
284   }
285   for (PetscInt i = 0; i < 3 * Np; ++i) ex->ornt[ct][i] = 0;
286   /*   cones for triangular prisms/tensor triangular prisms */
287   coff = 12 * Np;
288   ooff = 3 * Np;
289   for (PetscInt i = 0; i < Nl; ++i) {
290     if (ex->useTensor) {
291       ex->cone[ct][coff + 18 * i + 0]  = DM_POLYTOPE_TRIANGLE;
292       ex->cone[ct][coff + 18 * i + 1]  = 0;
293       ex->cone[ct][coff + 18 * i + 2]  = i;
294       ex->cone[ct][coff + 18 * i + 3]  = DM_POLYTOPE_TRIANGLE;
295       ex->cone[ct][coff + 18 * i + 4]  = 0;
296       ex->cone[ct][coff + 18 * i + 5]  = (i + 1) % Np;
297       ex->cone[ct][coff + 18 * i + 6]  = DM_POLYTOPE_SEG_PRISM_TENSOR;
298       ex->cone[ct][coff + 18 * i + 7]  = 1;
299       ex->cone[ct][coff + 18 * i + 8]  = 0;
300       ex->cone[ct][coff + 18 * i + 9]  = i;
301       ex->cone[ct][coff + 18 * i + 10] = DM_POLYTOPE_SEG_PRISM_TENSOR;
302       ex->cone[ct][coff + 18 * i + 11] = 1;
303       ex->cone[ct][coff + 18 * i + 12] = 1;
304       ex->cone[ct][coff + 18 * i + 13] = i;
305       ex->cone[ct][coff + 18 * i + 14] = DM_POLYTOPE_SEG_PRISM_TENSOR;
306       ex->cone[ct][coff + 18 * i + 15] = 1;
307       ex->cone[ct][coff + 18 * i + 16] = 2;
308       ex->cone[ct][coff + 18 * i + 17] = i;
309       ex->ornt[ct][ooff + 5 * i + 0]   = 0;
310       ex->ornt[ct][ooff + 5 * i + 1]   = 0;
311       ex->ornt[ct][ooff + 5 * i + 2]   = 0;
312       ex->ornt[ct][ooff + 5 * i + 3]   = 0;
313       ex->ornt[ct][ooff + 5 * i + 4]   = 0;
314     } else {
315       ex->cone[ct][coff + 18 * i + 0]  = DM_POLYTOPE_TRIANGLE;
316       ex->cone[ct][coff + 18 * i + 1]  = 0;
317       ex->cone[ct][coff + 18 * i + 2]  = i;
318       ex->cone[ct][coff + 18 * i + 3]  = DM_POLYTOPE_TRIANGLE;
319       ex->cone[ct][coff + 18 * i + 4]  = 0;
320       ex->cone[ct][coff + 18 * i + 5]  = (i + 1) % Np;
321       ex->cone[ct][coff + 18 * i + 6]  = DM_POLYTOPE_QUADRILATERAL;
322       ex->cone[ct][coff + 18 * i + 7]  = 1;
323       ex->cone[ct][coff + 18 * i + 8]  = 0;
324       ex->cone[ct][coff + 18 * i + 9]  = i;
325       ex->cone[ct][coff + 18 * i + 10] = DM_POLYTOPE_QUADRILATERAL;
326       ex->cone[ct][coff + 18 * i + 11] = 1;
327       ex->cone[ct][coff + 18 * i + 12] = 1;
328       ex->cone[ct][coff + 18 * i + 13] = i;
329       ex->cone[ct][coff + 18 * i + 14] = DM_POLYTOPE_QUADRILATERAL;
330       ex->cone[ct][coff + 18 * i + 15] = 1;
331       ex->cone[ct][coff + 18 * i + 16] = 2;
332       ex->cone[ct][coff + 18 * i + 17] = i;
333       ex->ornt[ct][ooff + 5 * i + 0]   = -2;
334       ex->ornt[ct][ooff + 5 * i + 1]   = 0;
335       ex->ornt[ct][ooff + 5 * i + 2]   = 0;
336       ex->ornt[ct][ooff + 5 * i + 3]   = 0;
337       ex->ornt[ct][ooff + 5 * i + 4]   = 0;
338     }
339   }
340   PetscFunctionReturn(PETSC_SUCCESS);
341 }
342 
343 /* DM_POLYTOPE_QUADRILATERAL produces
344      Nl+1 quads, or Nl quads periodically and
345      Nl hexes/tensor hexes
346 */
347 static PetscErrorCode DMPlexTransformExtrudeSetUp_Quadrilateral(DMPlexTransform_Extrude *ex, PetscInt Nl)
348 {
349   const DMPolytopeType ct = DM_POLYTOPE_QUADRILATERAL;
350   const PetscInt       Np = ex->periodic ? Nl : Nl + 1;
351   PetscInt             Nc, No, coff, ooff;
352 
353   PetscFunctionBegin;
354   ex->Nt[ct] = 2;
355   Nc         = 16 * Np + 22 * Nl;
356   No         = 4 * Np + 6 * Nl;
357   PetscCall(PetscMalloc4(ex->Nt[ct], &ex->target[ct], ex->Nt[ct], &ex->size[ct], Nc, &ex->cone[ct], No, &ex->ornt[ct]));
358   ex->target[ct][0] = DM_POLYTOPE_QUADRILATERAL;
359   ex->target[ct][1] = ex->useTensor ? DM_POLYTOPE_QUAD_PRISM_TENSOR : DM_POLYTOPE_HEXAHEDRON;
360   ex->size[ct][0]   = Np;
361   ex->size[ct][1]   = Nl;
362   /*   cones for quads */
363   for (PetscInt i = 0; i < Np; ++i) {
364     ex->cone[ct][16 * i + 0]  = DM_POLYTOPE_SEGMENT;
365     ex->cone[ct][16 * i + 1]  = 1;
366     ex->cone[ct][16 * i + 2]  = 0;
367     ex->cone[ct][16 * i + 3]  = i;
368     ex->cone[ct][16 * i + 4]  = DM_POLYTOPE_SEGMENT;
369     ex->cone[ct][16 * i + 5]  = 1;
370     ex->cone[ct][16 * i + 6]  = 1;
371     ex->cone[ct][16 * i + 7]  = i;
372     ex->cone[ct][16 * i + 8]  = DM_POLYTOPE_SEGMENT;
373     ex->cone[ct][16 * i + 9]  = 1;
374     ex->cone[ct][16 * i + 10] = 2;
375     ex->cone[ct][16 * i + 11] = i;
376     ex->cone[ct][16 * i + 12] = DM_POLYTOPE_SEGMENT;
377     ex->cone[ct][16 * i + 13] = 1;
378     ex->cone[ct][16 * i + 14] = 3;
379     ex->cone[ct][16 * i + 15] = i;
380   }
381   for (PetscInt i = 0; i < 4 * Np; ++i) ex->ornt[ct][i] = 0;
382   /*   cones for hexes/tensor hexes */
383   coff = 16 * Np;
384   ooff = 4 * Np;
385   for (PetscInt i = 0; i < Nl; ++i) {
386     if (ex->useTensor) {
387       ex->cone[ct][coff + 22 * i + 0]  = DM_POLYTOPE_QUADRILATERAL;
388       ex->cone[ct][coff + 22 * i + 1]  = 0;
389       ex->cone[ct][coff + 22 * i + 2]  = i;
390       ex->cone[ct][coff + 22 * i + 3]  = DM_POLYTOPE_QUADRILATERAL;
391       ex->cone[ct][coff + 22 * i + 4]  = 0;
392       ex->cone[ct][coff + 22 * i + 5]  = (i + 1) % Np;
393       ex->cone[ct][coff + 22 * i + 6]  = DM_POLYTOPE_SEG_PRISM_TENSOR;
394       ex->cone[ct][coff + 22 * i + 7]  = 1;
395       ex->cone[ct][coff + 22 * i + 8]  = 0;
396       ex->cone[ct][coff + 22 * i + 9]  = i;
397       ex->cone[ct][coff + 22 * i + 10] = DM_POLYTOPE_SEG_PRISM_TENSOR;
398       ex->cone[ct][coff + 22 * i + 11] = 1;
399       ex->cone[ct][coff + 22 * i + 12] = 1;
400       ex->cone[ct][coff + 22 * i + 13] = i;
401       ex->cone[ct][coff + 22 * i + 14] = DM_POLYTOPE_SEG_PRISM_TENSOR;
402       ex->cone[ct][coff + 22 * i + 15] = 1;
403       ex->cone[ct][coff + 22 * i + 16] = 2;
404       ex->cone[ct][coff + 22 * i + 17] = i;
405       ex->cone[ct][coff + 22 * i + 18] = DM_POLYTOPE_SEG_PRISM_TENSOR;
406       ex->cone[ct][coff + 22 * i + 19] = 1;
407       ex->cone[ct][coff + 22 * i + 20] = 3;
408       ex->cone[ct][coff + 22 * i + 21] = i;
409       ex->ornt[ct][ooff + 6 * i + 0]   = 0;
410       ex->ornt[ct][ooff + 6 * i + 1]   = 0;
411       ex->ornt[ct][ooff + 6 * i + 2]   = 0;
412       ex->ornt[ct][ooff + 6 * i + 3]   = 0;
413       ex->ornt[ct][ooff + 6 * i + 4]   = 0;
414       ex->ornt[ct][ooff + 6 * i + 5]   = 0;
415     } else {
416       ex->cone[ct][coff + 22 * i + 0]  = DM_POLYTOPE_QUADRILATERAL;
417       ex->cone[ct][coff + 22 * i + 1]  = 0;
418       ex->cone[ct][coff + 22 * i + 2]  = i;
419       ex->cone[ct][coff + 22 * i + 3]  = DM_POLYTOPE_QUADRILATERAL;
420       ex->cone[ct][coff + 22 * i + 4]  = 0;
421       ex->cone[ct][coff + 22 * i + 5]  = (i + 1) % Np;
422       ex->cone[ct][coff + 22 * i + 6]  = DM_POLYTOPE_QUADRILATERAL;
423       ex->cone[ct][coff + 22 * i + 7]  = 1;
424       ex->cone[ct][coff + 22 * i + 8]  = 0;
425       ex->cone[ct][coff + 22 * i + 9]  = i;
426       ex->cone[ct][coff + 22 * i + 10] = DM_POLYTOPE_QUADRILATERAL;
427       ex->cone[ct][coff + 22 * i + 11] = 1;
428       ex->cone[ct][coff + 22 * i + 12] = 2;
429       ex->cone[ct][coff + 22 * i + 13] = i;
430       ex->cone[ct][coff + 22 * i + 14] = DM_POLYTOPE_QUADRILATERAL;
431       ex->cone[ct][coff + 22 * i + 15] = 1;
432       ex->cone[ct][coff + 22 * i + 16] = 1;
433       ex->cone[ct][coff + 22 * i + 17] = i;
434       ex->cone[ct][coff + 22 * i + 18] = DM_POLYTOPE_QUADRILATERAL;
435       ex->cone[ct][coff + 22 * i + 19] = 1;
436       ex->cone[ct][coff + 22 * i + 20] = 3;
437       ex->cone[ct][coff + 22 * i + 21] = i;
438       ex->ornt[ct][ooff + 6 * i + 0]   = -2;
439       ex->ornt[ct][ooff + 6 * i + 1]   = 0;
440       ex->ornt[ct][ooff + 6 * i + 2]   = 0;
441       ex->ornt[ct][ooff + 6 * i + 3]   = 0;
442       ex->ornt[ct][ooff + 6 * i + 4]   = 0;
443       ex->ornt[ct][ooff + 6 * i + 5]   = 1;
444     }
445   }
446   PetscFunctionReturn(PETSC_SUCCESS);
447 }
448 
449 /*
450   The refine types for extrusion are:
451 
452   ct:       For any normally extruded point
453   ct + 100: For any point which should just return itself
454 */
455 static PetscErrorCode DMPlexTransformSetUp_Extrude(DMPlexTransform tr)
456 {
457   DMPlexTransform_Extrude *ex = (DMPlexTransform_Extrude *)tr->data;
458   DM                       dm;
459   DMLabel                  active;
460   PetscInt                 Nl = ex->layers, l, ict, dim;
461 
462   PetscFunctionBegin;
463   PetscCall(DMPlexTransformExtrudeComputeExtrusionDim(tr));
464   PetscCall(DMPlexTransformGetDM(tr, &dm));
465   PetscCall(DMGetDimension(dm, &dim));
466   PetscCall(DMPlexTransformGetActive(tr, &active));
467   if (active) {
468     DMLabel  celltype;
469     PetscInt pStart, pEnd, p;
470 
471     PetscCall(DMPlexGetCellTypeLabel(dm, &celltype));
472     PetscCall(DMLabelCreate(PETSC_COMM_SELF, "Refine Type", &tr->trType));
473     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
474     for (p = pStart; p < pEnd; ++p) {
475       PetscInt ct, val;
476 
477       PetscCall(DMLabelGetValue(celltype, p, &ct));
478       PetscCall(DMLabelGetValue(active, p, &val));
479       if (val < 0) {
480         PetscCall(DMLabelSetValue(tr->trType, p, ct + 100));
481       } else {
482         PetscCall(DMLabelSetValue(tr->trType, p, ct));
483       }
484     }
485   }
486   if (ex->normalAlg != NORMAL_INPUT) {
487     if (dim != ex->cdim) ex->normalAlg = NORMAL_COMPUTE;
488     else if (active) ex->normalAlg = NORMAL_COMPUTE_BD;
489   }
490   // Need this to determine face sharing
491   PetscMPIInt size;
492 
493   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
494   if (size > 1) {
495     PetscSF  sf;
496     PetscInt Nr;
497 
498     PetscCall(DMGetPointSF(dm, &sf));
499     PetscCall(PetscSFGetGraph(sf, &Nr, NULL, NULL, NULL));
500     if (Nr >= 0) {
501       PetscCall(PetscSFComputeDegreeBegin(sf, &ex->degree));
502       PetscCall(PetscSFComputeDegreeEnd(sf, &ex->degree));
503     }
504   }
505   // Create normal field
506   if (ex->normalAlg == NORMAL_COMPUTE_BD) {
507     PetscSection s;
508     PetscInt     vStart, vEnd;
509 
510     PetscMPIInt rank;
511     PetscCallMPI(MPI_Comm_rank(PETSC_COMM_WORLD, &rank));
512 
513     PetscCall(DMClone(dm, &ex->dmNormal));
514     PetscCall(DMGetLocalSection(ex->dmNormal, &s));
515     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
516     PetscCall(PetscSectionSetNumFields(s, 1));
517     PetscCall(PetscSectionSetChart(s, vStart, vEnd));
518     for (PetscInt v = vStart; v < vEnd; ++v) PetscCall(PetscSectionSetDof(s, v, ex->cdimEx));
519     PetscCall(PetscSectionSetUp(s));
520     PetscCall(DMCreateLocalVector(ex->dmNormal, &ex->vecNormal));
521     PetscCall(PetscObjectSetName((PetscObject)ex->vecNormal, "Normal Field"));
522 
523     // find an active point in the closure of v and use its coordinate normal as the extrusion direction
524     PetscSF         sf;
525     const PetscInt *leaves;
526     PetscScalar    *a, *normal;
527     PetscInt        Nl;
528 
529     PetscCall(DMGetPointSF(ex->dmNormal, &sf));
530     PetscCall(PetscSFGetGraph(sf, NULL, &Nl, &leaves, NULL));
531     PetscCall(VecGetArrayWrite(ex->vecNormal, &a));
532     for (PetscInt v = vStart; v < vEnd; ++v) {
533       PetscInt *star = NULL;
534       PetscInt  starSize, pStart, pEnd;
535 
536       PetscCall(DMPlexGetDepthStratum(ex->dmNormal, ex->cdimEx - 1, &pStart, &pEnd));
537       PetscCall(DMPlexGetTransitiveClosure(ex->dmNormal, v, PETSC_FALSE, &starSize, &star));
538       PetscCall(DMPlexPointLocalRef(ex->dmNormal, v, a, &normal));
539       for (PetscInt st = 0; st < starSize * 2; st += 2) {
540         const PetscInt face = star[st];
541         if ((face >= pStart) && (face < pEnd)) {
542           PetscReal       cnormal[3] = {0, 0, 0};
543           const PetscInt *supp;
544           PetscInt        suppSize, floc = -1;
545           PetscBool       shared;
546 
547           PetscCall(DMPlexComputeCellGeometryFVM(ex->dmNormal, face, NULL, NULL, cnormal));
548           PetscCall(DMPlexGetSupportSize(ex->dmNormal, face, &suppSize));
549           PetscCall(DMPlexGetSupport(ex->dmNormal, face, &supp));
550           // Only use external faces, so I can get the orientation from any cell
551           if (leaves) PetscCall(PetscFindInt(face, Nl, leaves, &floc));
552           shared = floc >= 0 || (ex->degree && ex->degree[face]) ? PETSC_TRUE : PETSC_FALSE;
553           if (suppSize == 1 && !shared) {
554             const PetscInt *cone, *ornt;
555             PetscInt        coneSize, c;
556 
557             PetscCall(DMPlexGetConeSize(ex->dmNormal, supp[0], &coneSize));
558             PetscCall(DMPlexGetCone(ex->dmNormal, supp[0], &cone));
559             PetscCall(DMPlexGetConeOrientation(ex->dmNormal, supp[0], &ornt));
560             for (c = 0; c < coneSize; ++c)
561               if (cone[c] == face) break;
562             PetscCheck(c < coneSize, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Asymmetry in cone/support");
563             if (ornt[c] < 0)
564               for (PetscInt d = 0; d < ex->cdimEx; ++d) cnormal[d] *= -1.;
565             for (PetscInt d = 0; d < ex->cdimEx; ++d) normal[d] += cnormal[d];
566           }
567         }
568       }
569       PetscCall(DMPlexRestoreTransitiveClosure(ex->dmNormal, v, PETSC_FALSE, &starSize, &star));
570     }
571     PetscCall(VecRestoreArrayWrite(ex->vecNormal, &a));
572 
573     Vec g;
574 
575     PetscCall(DMGetGlobalVector(ex->dmNormal, &g));
576     PetscCall(VecSet(g, 0.));
577     PetscCall(DMLocalToGlobal(ex->dmNormal, ex->vecNormal, ADD_VALUES, g));
578     PetscCall(DMGlobalToLocal(ex->dmNormal, g, INSERT_VALUES, ex->vecNormal));
579     PetscCall(DMRestoreGlobalVector(ex->dmNormal, &g));
580   }
581   PetscCall(PetscMalloc5(DM_NUM_POLYTOPES, &ex->Nt, DM_NUM_POLYTOPES, &ex->target, DM_NUM_POLYTOPES, &ex->size, DM_NUM_POLYTOPES, &ex->cone, DM_NUM_POLYTOPES, &ex->ornt));
582   for (ict = 0; ict < DM_NUM_POLYTOPES; ++ict) {
583     ex->Nt[ict]     = -1;
584     ex->target[ict] = NULL;
585     ex->size[ict]   = NULL;
586     ex->cone[ict]   = NULL;
587     ex->ornt[ict]   = NULL;
588   }
589   PetscCall(DMPlexTransformExtrudeSetUp_Point(ex, Nl));
590   PetscCall(DMPlexTransformExtrudeSetUp_Segment(ex, Nl));
591   PetscCall(DMPlexTransformExtrudeSetUp_Triangle(ex, Nl));
592   PetscCall(DMPlexTransformExtrudeSetUp_Quadrilateral(ex, Nl));
593   /* Layers positions */
594   if (!ex->Nth) {
595     if (ex->symmetric)
596       for (l = 0; l <= ex->layers; ++l) ex->layerPos[l] = (ex->thickness * l) / ex->layers - ex->thickness / 2;
597     else
598       for (l = 0; l <= ex->layers; ++l) ex->layerPos[l] = (ex->thickness * l) / ex->layers;
599   } else {
600     ex->thickness   = 0.;
601     ex->layerPos[0] = 0.;
602     for (l = 0; l < ex->layers; ++l) {
603       const PetscReal t   = ex->thicknesses[PetscMin(l, ex->Nth - 1)];
604       ex->layerPos[l + 1] = ex->layerPos[l] + t;
605       ex->thickness += t;
606     }
607     if (ex->symmetric)
608       for (l = 0; l <= ex->layers; ++l) ex->layerPos[l] -= ex->thickness / 2.;
609   }
610   PetscFunctionReturn(PETSC_SUCCESS);
611 }
612 
613 static PetscErrorCode DMPlexTransformDestroy_Extrude(DMPlexTransform tr)
614 {
615   DMPlexTransform_Extrude *ex = (DMPlexTransform_Extrude *)tr->data;
616   PetscInt                 ct;
617 
618   PetscFunctionBegin;
619   if (ex->target) {
620     for (ct = 0; ct < DM_NUM_POLYTOPES; ++ct) PetscCall(PetscFree4(ex->target[ct], ex->size[ct], ex->cone[ct], ex->ornt[ct]));
621   }
622   PetscCall(PetscFree5(ex->Nt, ex->target, ex->size, ex->cone, ex->ornt));
623   PetscCall(PetscFree(ex->layerPos));
624   PetscCall(DMDestroy(&ex->dmNormal));
625   PetscCall(VecDestroy(&ex->vecNormal));
626   PetscCall(PetscFree(ex));
627   PetscFunctionReturn(PETSC_SUCCESS);
628 }
629 
630 static PetscErrorCode DMPlexTransformGetSubcellOrientation_Extrude(DMPlexTransform tr, DMPolytopeType sct, PetscInt sp, PetscInt so, DMPolytopeType tct, PetscInt r, PetscInt o, PetscInt *rnew, PetscInt *onew)
631 {
632   DMPlexTransform_Extrude *ex     = (DMPlexTransform_Extrude *)tr->data;
633   DMLabel                  trType = tr->trType;
634   PetscInt                 rt;
635 
636   PetscFunctionBeginHot;
637   *rnew = r;
638   *onew = DMPolytopeTypeComposeOrientation(tct, o, so);
639   if (!so) PetscFunctionReturn(PETSC_SUCCESS);
640   if (trType) {
641     PetscCall(DMLabelGetValue(tr->trType, sp, &rt));
642     if (rt >= 100) PetscFunctionReturn(PETSC_SUCCESS);
643   }
644   if (ex->useTensor) {
645     switch (sct) {
646     case DM_POLYTOPE_POINT:
647       break;
648     case DM_POLYTOPE_SEGMENT:
649       switch (tct) {
650       case DM_POLYTOPE_SEGMENT:
651         break;
652       case DM_POLYTOPE_SEG_PRISM_TENSOR:
653         *onew = DMPolytopeTypeComposeOrientation(tct, o, so ? -1 : 0);
654         break;
655       default:
656         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell type %s is not produced by %s", DMPolytopeTypes[tct], DMPolytopeTypes[sct]);
657       }
658       break;
659     // We need to handle identity extrusions from volumes (TET, HEX, etc) when boundary faces are being extruded
660     case DM_POLYTOPE_TRIANGLE:
661       break;
662     case DM_POLYTOPE_QUADRILATERAL:
663       break;
664     default:
665       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Unsupported cell type %s", DMPolytopeTypes[sct]);
666     }
667   } else {
668     switch (sct) {
669     case DM_POLYTOPE_POINT:
670       break;
671     case DM_POLYTOPE_SEGMENT:
672       switch (tct) {
673       case DM_POLYTOPE_SEGMENT:
674         break;
675       case DM_POLYTOPE_QUADRILATERAL:
676         *onew = DMPolytopeTypeComposeOrientation(tct, o, so ? -3 : 0);
677         break;
678       default:
679         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell type %s is not produced by %s", DMPolytopeTypes[tct], DMPolytopeTypes[sct]);
680       }
681       break;
682     // We need to handle identity extrusions from volumes (TET, HEX, etc) when boundary faces are being extruded
683     case DM_POLYTOPE_TRIANGLE:
684       break;
685     case DM_POLYTOPE_QUADRILATERAL:
686       break;
687     default:
688       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Unsupported cell type %s", DMPolytopeTypes[sct]);
689     }
690   }
691   PetscFunctionReturn(PETSC_SUCCESS);
692 }
693 
694 static PetscErrorCode DMPlexTransformCellTransform_Extrude(DMPlexTransform tr, DMPolytopeType source, PetscInt p, PetscInt *rt, PetscInt *Nt, DMPolytopeType *target[], PetscInt *size[], PetscInt *cone[], PetscInt *ornt[])
695 {
696   DMPlexTransform_Extrude *ex     = (DMPlexTransform_Extrude *)tr->data;
697   DMLabel                  trType = tr->trType;
698   PetscBool                ignore = PETSC_FALSE, identity = PETSC_FALSE;
699   PetscInt                 val = 0;
700 
701   PetscFunctionBegin;
702   if (trType) {
703     PetscCall(DMLabelGetValue(trType, p, &val));
704     identity = val >= 100 ? PETSC_TRUE : PETSC_FALSE;
705   } else {
706     ignore = ex->Nt[source] < 0 ? PETSC_TRUE : PETSC_FALSE;
707   }
708   if (rt) *rt = val;
709   if (ignore) {
710     /* Ignore cells that cannot be extruded */
711     *Nt     = 0;
712     *target = NULL;
713     *size   = NULL;
714     *cone   = NULL;
715     *ornt   = NULL;
716   } else if (identity) {
717     PetscCall(DMPlexTransformCellTransformIdentity(tr, source, p, NULL, Nt, target, size, cone, ornt));
718   } else {
719     *Nt     = ex->Nt[source];
720     *target = ex->target[source];
721     *size   = ex->size[source];
722     *cone   = ex->cone[source];
723     *ornt   = ex->ornt[source];
724   }
725   PetscFunctionReturn(PETSC_SUCCESS);
726 }
727 
728 /* Computes new vertex along normal */
729 static PetscErrorCode DMPlexTransformMapCoordinates_Extrude(DMPlexTransform tr, DMPolytopeType pct, DMPolytopeType ct, PetscInt p, PetscInt r, PetscInt Nv, PetscInt dE, const PetscScalar in[], PetscScalar out[])
730 {
731   DMPlexTransform_Extrude *ex = (DMPlexTransform_Extrude *)tr->data;
732   DM                       dm;
733   DMLabel                  active;
734   PetscReal                ones2[2]  = {0., 1.};
735   PetscReal                ones3[3]  = {0., 0., 1.};
736   PetscReal                normal[3] = {0., 0., 0.};
737   PetscReal                norm      = 0.;
738   PetscInt                 dEx       = ex->cdimEx;
739   PetscInt                 dim, cStart, cEnd;
740 
741   PetscFunctionBeginHot;
742   PetscCheck(pct == DM_POLYTOPE_POINT, PETSC_COMM_SELF, PETSC_ERR_SUP, "Not for parent point type %s", DMPolytopeTypes[pct]);
743   PetscCheck(ct == DM_POLYTOPE_POINT, PETSC_COMM_SELF, PETSC_ERR_SUP, "Not for refined point type %s", DMPolytopeTypes[ct]);
744   PetscCheck(Nv == 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Vertices should be produced from a single vertex, not %" PetscInt_FMT, Nv);
745   PetscCheck(dE == ex->cdim, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Coordinate dim %" PetscInt_FMT " != %" PetscInt_FMT " original dimension", dE, ex->cdim);
746   PetscCheck(dEx <= 3, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate dimension for extruded mesh %" PetscInt_FMT " must not exceed 3", dEx);
747 
748   PetscCall(DMPlexTransformGetDM(tr, &dm));
749   PetscCall(DMGetDimension(dm, &dim));
750   PetscCall(DMPlexTransformGetActive(tr, &active));
751   switch (ex->normalAlg) {
752   case NORMAL_DEFAULT:
753     switch (ex->cdimEx) {
754     case 2:
755       for (PetscInt d = 0; d < dEx; ++d) normal[d] = ones2[d];
756       break;
757     case 3:
758       for (PetscInt d = 0; d < dEx; ++d) normal[d] = ones3[d];
759       break;
760     default:
761       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "No default normal for dimension %" PetscInt_FMT, ex->cdimEx);
762     }
763     break;
764   case NORMAL_INPUT:
765     for (PetscInt d = 0; d < dEx; ++d) normal[d] = ex->normal[d];
766     break;
767   case NORMAL_COMPUTE: {
768     PetscInt *star = NULL;
769     PetscInt  starSize;
770 
771     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
772     PetscCall(DMPlexGetTransitiveClosure(dm, p, PETSC_FALSE, &starSize, &star));
773     for (PetscInt st = 0; st < starSize * 2; st += 2) {
774       if ((star[st] >= cStart) && (star[st] < cEnd)) {
775         PetscReal cnormal[3] = {0, 0, 0};
776 
777         PetscCall(DMPlexComputeCellGeometryFVM(dm, star[st], NULL, NULL, cnormal));
778         for (PetscInt d = 0; d < dEx; ++d) normal[d] += cnormal[d];
779       }
780     }
781     PetscCall(DMPlexRestoreTransitiveClosure(dm, p, PETSC_FALSE, &starSize, &star));
782   } break;
783   case NORMAL_COMPUTE_BD: {
784     const PetscScalar *a;
785     PetscScalar       *vnormal;
786 
787     PetscCall(VecGetArrayRead(ex->vecNormal, &a));
788     PetscCall(DMPlexPointLocalRead(ex->dmNormal, p, a, (void *)&vnormal));
789     for (PetscInt d = 0; d < dEx; ++d) normal[d] = PetscRealPart(vnormal[d]);
790     PetscCall(VecRestoreArrayRead(ex->vecNormal, &a));
791   } break;
792   default:
793     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unable to determine normal for extrusion");
794   }
795   if (ex->normalFunc) {
796     PetscScalar n[3];
797     PetscReal   x[3], dot = 0.;
798 
799     for (PetscInt d = 0; d < ex->cdim; ++d) x[d] = PetscRealPart(in[d]);
800     PetscCall((*ex->normalFunc)(ex->cdim, 0., x, r, n, NULL));
801     for (PetscInt d = 0; d < dEx; ++d) dot += PetscRealPart(n[d]) * normal[d];
802     for (PetscInt d = 0; d < dEx; ++d) normal[d] = PetscSign(dot) * PetscRealPart(n[d]);
803   }
804   for (PetscInt d = 0; d < dEx; ++d) norm += PetscSqr(normal[d]);
805   for (PetscInt d = 0; d < dEx; ++d) normal[d] *= norm == 0.0 ? 1.0 : 1. / PetscSqrtReal(norm);
806   for (PetscInt d = 0; d < dEx; ++d) out[d] = normal[d] * ex->layerPos[r];
807   for (PetscInt d = 0; d < ex->cdim; ++d) out[d] += in[d];
808   PetscFunctionReturn(PETSC_SUCCESS);
809 }
810 
811 static PetscErrorCode DMPlexTransformInitialize_Extrude(DMPlexTransform tr)
812 {
813   PetscFunctionBegin;
814   tr->ops->view                  = DMPlexTransformView_Extrude;
815   tr->ops->setfromoptions        = DMPlexTransformSetFromOptions_Extrude;
816   tr->ops->setup                 = DMPlexTransformSetUp_Extrude;
817   tr->ops->destroy               = DMPlexTransformDestroy_Extrude;
818   tr->ops->setdimensions         = DMPlexTransformSetDimensions_Extrude;
819   tr->ops->celltransform         = DMPlexTransformCellTransform_Extrude;
820   tr->ops->getsubcellorientation = DMPlexTransformGetSubcellOrientation_Extrude;
821   tr->ops->mapcoordinates        = DMPlexTransformMapCoordinates_Extrude;
822   PetscFunctionReturn(PETSC_SUCCESS);
823 }
824 
825 PETSC_EXTERN PetscErrorCode DMPlexTransformCreate_Extrude(DMPlexTransform tr)
826 {
827   DMPlexTransform_Extrude *ex;
828   DM                       dm;
829   PetscInt                 dim;
830 
831   PetscFunctionBegin;
832   PetscValidHeaderSpecific(tr, DMPLEXTRANSFORM_CLASSID, 1);
833   PetscCall(PetscNew(&ex));
834   tr->data      = ex;
835   ex->thickness = 1.;
836   ex->useTensor = PETSC_TRUE;
837   ex->symmetric = PETSC_FALSE;
838   ex->periodic  = PETSC_FALSE;
839   ex->normalAlg = NORMAL_DEFAULT;
840   ex->layerPos  = NULL;
841   PetscCall(DMPlexTransformGetDM(tr, &dm));
842   PetscCall(DMGetDimension(dm, &dim));
843   PetscCall(DMGetCoordinateDim(dm, &ex->cdim));
844   PetscCall(DMPlexTransformExtrudeSetLayers(tr, 1));
845   PetscCall(DMPlexTransformInitialize_Extrude(tr));
846   PetscFunctionReturn(PETSC_SUCCESS);
847 }
848 
849 /*@
850   DMPlexTransformExtrudeGetLayers - Get the number of extruded layers.
851 
852   Not Collective
853 
854   Input Parameter:
855 . tr - The `DMPlexTransform`
856 
857   Output Parameter:
858 . layers - The number of layers
859 
860   Level: intermediate
861 
862 .seealso: `DMPlexTransform`, `DMPlexTransformExtrudeSetLayers()`
863 @*/
864 PetscErrorCode DMPlexTransformExtrudeGetLayers(DMPlexTransform tr, PetscInt *layers)
865 {
866   DMPlexTransform_Extrude *ex = (DMPlexTransform_Extrude *)tr->data;
867 
868   PetscFunctionBegin;
869   PetscValidHeaderSpecific(tr, DMPLEXTRANSFORM_CLASSID, 1);
870   PetscAssertPointer(layers, 2);
871   *layers = ex->layers;
872   PetscFunctionReturn(PETSC_SUCCESS);
873 }
874 
875 /*@
876   DMPlexTransformExtrudeSetLayers - Set the number of extruded layers.
877 
878   Not Collective
879 
880   Input Parameters:
881 + tr     - The `DMPlexTransform`
882 - layers - The number of layers
883 
884   Level: intermediate
885 
886 .seealso: `DMPlexTransform`, `DMPlexTransformExtrudeGetLayers()`
887 @*/
888 PetscErrorCode DMPlexTransformExtrudeSetLayers(DMPlexTransform tr, PetscInt layers)
889 {
890   DMPlexTransform_Extrude *ex = (DMPlexTransform_Extrude *)tr->data;
891 
892   PetscFunctionBegin;
893   PetscValidHeaderSpecific(tr, DMPLEXTRANSFORM_CLASSID, 1);
894   ex->layers = layers;
895   PetscCall(PetscFree(ex->layerPos));
896   PetscCall(PetscCalloc1(ex->layers + 1, &ex->layerPos));
897   PetscFunctionReturn(PETSC_SUCCESS);
898 }
899 
900 /*@
901   DMPlexTransformExtrudeGetThickness - Get the total thickness of the layers
902 
903   Not Collective
904 
905   Input Parameter:
906 . tr - The `DMPlexTransform`
907 
908   Output Parameter:
909 . thickness - The total thickness of the layers
910 
911   Level: intermediate
912 
913 .seealso: `DMPlexTransform`, `DMPlexTransformExtrudeSetThickness()`
914 @*/
915 PetscErrorCode DMPlexTransformExtrudeGetThickness(DMPlexTransform tr, PetscReal *thickness)
916 {
917   DMPlexTransform_Extrude *ex = (DMPlexTransform_Extrude *)tr->data;
918 
919   PetscFunctionBegin;
920   PetscValidHeaderSpecific(tr, DMPLEXTRANSFORM_CLASSID, 1);
921   PetscAssertPointer(thickness, 2);
922   *thickness = ex->thickness;
923   PetscFunctionReturn(PETSC_SUCCESS);
924 }
925 
926 /*@
927   DMPlexTransformExtrudeSetThickness - Set the total thickness of the layers
928 
929   Not Collective
930 
931   Input Parameters:
932 + tr        - The `DMPlexTransform`
933 - thickness - The total thickness of the layers
934 
935   Level: intermediate
936 
937 .seealso: `DMPlexTransform`, `DMPlexTransformExtrudeGetThickness()`
938 @*/
939 PetscErrorCode DMPlexTransformExtrudeSetThickness(DMPlexTransform tr, PetscReal thickness)
940 {
941   DMPlexTransform_Extrude *ex = (DMPlexTransform_Extrude *)tr->data;
942 
943   PetscFunctionBegin;
944   PetscValidHeaderSpecific(tr, DMPLEXTRANSFORM_CLASSID, 1);
945   PetscCheck(thickness > 0., PetscObjectComm((PetscObject)tr), PETSC_ERR_ARG_OUTOFRANGE, "Height of layers %g must be positive", (double)thickness);
946   ex->thickness = thickness;
947   PetscFunctionReturn(PETSC_SUCCESS);
948 }
949 
950 /*@
951   DMPlexTransformExtrudeGetTensor - Get the flag to use tensor cells
952 
953   Not Collective
954 
955   Input Parameter:
956 . tr - The `DMPlexTransform`
957 
958   Output Parameter:
959 . useTensor - The flag to use tensor cells
960 
961   Note:
962   This flag determines the orientation behavior of the created points.
963 
964   For example, if tensor is `PETSC_TRUE`, then
965 .vb
966   DM_POLYTOPE_POINT_PRISM_TENSOR is made instead of DM_POLYTOPE_SEGMENT,
967   DM_POLYTOPE_SEG_PRISM_TENSOR instead of DM_POLYTOPE_QUADRILATERAL,
968   DM_POLYTOPE_TRI_PRISM_TENSOR instead of DM_POLYTOPE_TRI_PRISM, and
969   DM_POLYTOPE_QUAD_PRISM_TENSOR instead of DM_POLYTOPE_HEXAHEDRON.
970 .ve
971 
972   Level: intermediate
973 
974 .seealso: `DMPlexTransform`, `DMPlexTransformExtrudeSetTensor()`
975 @*/
976 PetscErrorCode DMPlexTransformExtrudeGetTensor(DMPlexTransform tr, PetscBool *useTensor)
977 {
978   DMPlexTransform_Extrude *ex = (DMPlexTransform_Extrude *)tr->data;
979 
980   PetscFunctionBegin;
981   PetscValidHeaderSpecific(tr, DMPLEXTRANSFORM_CLASSID, 1);
982   PetscAssertPointer(useTensor, 2);
983   *useTensor = ex->useTensor;
984   PetscFunctionReturn(PETSC_SUCCESS);
985 }
986 
987 /*@
988   DMPlexTransformExtrudeSetTensor - Set the flag to use tensor cells
989 
990   Not Collective
991 
992   Input Parameters:
993 + tr        - The `DMPlexTransform`
994 - useTensor - The flag for tensor cells
995 
996   Note:
997   This flag determines the orientation behavior of the created points
998   For example, if tensor is `PETSC_TRUE`, then
999 .vb
1000   DM_POLYTOPE_POINT_PRISM_TENSOR is made instead of DM_POLYTOPE_SEGMENT,
1001   DM_POLYTOPE_SEG_PRISM_TENSOR instead of DM_POLYTOPE_QUADRILATERAL,
1002   DM_POLYTOPE_TRI_PRISM_TENSOR instead of DM_POLYTOPE_TRI_PRISM, and
1003   DM_POLYTOPE_QUAD_PRISM_TENSOR instead of DM_POLYTOPE_HEXAHEDRON.
1004 .ve
1005 
1006   Level: intermediate
1007 
1008 .seealso: `DMPlexTransform`, `DMPlexTransformExtrudeGetTensor()`
1009 @*/
1010 PetscErrorCode DMPlexTransformExtrudeSetTensor(DMPlexTransform tr, PetscBool useTensor)
1011 {
1012   DMPlexTransform_Extrude *ex = (DMPlexTransform_Extrude *)tr->data;
1013 
1014   PetscFunctionBegin;
1015   PetscValidHeaderSpecific(tr, DMPLEXTRANSFORM_CLASSID, 1);
1016   ex->useTensor = useTensor;
1017   PetscFunctionReturn(PETSC_SUCCESS);
1018 }
1019 
1020 /*@
1021   DMPlexTransformExtrudeGetSymmetric - Get the flag to extrude symmetrically from the initial surface
1022 
1023   Not Collective
1024 
1025   Input Parameter:
1026 . tr - The `DMPlexTransform`
1027 
1028   Output Parameter:
1029 . symmetric - The flag to extrude symmetrically
1030 
1031   Level: intermediate
1032 
1033 .seealso: `DMPlexTransform`, `DMPlexTransformExtrudeSetSymmetric()`, `DMPlexTransformExtrudeGetPeriodic()`
1034 @*/
1035 PetscErrorCode DMPlexTransformExtrudeGetSymmetric(DMPlexTransform tr, PetscBool *symmetric)
1036 {
1037   DMPlexTransform_Extrude *ex = (DMPlexTransform_Extrude *)tr->data;
1038 
1039   PetscFunctionBegin;
1040   PetscValidHeaderSpecific(tr, DMPLEXTRANSFORM_CLASSID, 1);
1041   PetscAssertPointer(symmetric, 2);
1042   *symmetric = ex->symmetric;
1043   PetscFunctionReturn(PETSC_SUCCESS);
1044 }
1045 
1046 /*@
1047   DMPlexTransformExtrudeSetSymmetric - Set the flag to extrude symmetrically from the initial surface
1048 
1049   Not Collective
1050 
1051   Input Parameters:
1052 + tr        - The `DMPlexTransform`
1053 - symmetric - The flag to extrude symmetrically
1054 
1055   Level: intermediate
1056 
1057 .seealso: `DMPlexTransform`, `DMPlexTransformExtrudeGetSymmetric()`, `DMPlexTransformExtrudeSetPeriodic()`
1058 @*/
1059 PetscErrorCode DMPlexTransformExtrudeSetSymmetric(DMPlexTransform tr, PetscBool symmetric)
1060 {
1061   DMPlexTransform_Extrude *ex = (DMPlexTransform_Extrude *)tr->data;
1062 
1063   PetscFunctionBegin;
1064   PetscValidHeaderSpecific(tr, DMPLEXTRANSFORM_CLASSID, 1);
1065   ex->symmetric = symmetric;
1066   PetscFunctionReturn(PETSC_SUCCESS);
1067 }
1068 
1069 /*@
1070   DMPlexTransformExtrudeGetPeriodic - Get the flag to extrude periodically from the initial surface
1071 
1072   Not Collective
1073 
1074   Input Parameter:
1075 . tr - The `DMPlexTransform`
1076 
1077   Output Parameter:
1078 . periodic - The flag to extrude periodically
1079 
1080   Level: intermediate
1081 
1082 .seealso: `DMPlexTransform`, `DMPlexTransformExtrudeSetPeriodic()`, `DMPlexTransformExtrudeGetSymmetric()`
1083 @*/
1084 PetscErrorCode DMPlexTransformExtrudeGetPeriodic(DMPlexTransform tr, PetscBool *periodic)
1085 {
1086   DMPlexTransform_Extrude *ex = (DMPlexTransform_Extrude *)tr->data;
1087 
1088   PetscFunctionBegin;
1089   PetscValidHeaderSpecific(tr, DMPLEXTRANSFORM_CLASSID, 1);
1090   PetscAssertPointer(periodic, 2);
1091   *periodic = ex->periodic;
1092   PetscFunctionReturn(PETSC_SUCCESS);
1093 }
1094 
1095 /*@
1096   DMPlexTransformExtrudeSetPeriodic - Set the flag to extrude periodically from the initial surface
1097 
1098   Not Collective
1099 
1100   Input Parameters:
1101 + tr       - The `DMPlexTransform`
1102 - periodic - The flag to extrude periodically
1103 
1104   Level: intermediate
1105 
1106 .seealso: `DMPlexTransform`, `DMPlexTransformExtrudeGetPeriodic()`, `DMPlexTransformExtrudeSetSymmetric()`
1107 @*/
1108 PetscErrorCode DMPlexTransformExtrudeSetPeriodic(DMPlexTransform tr, PetscBool periodic)
1109 {
1110   DMPlexTransform_Extrude *ex = (DMPlexTransform_Extrude *)tr->data;
1111 
1112   PetscFunctionBegin;
1113   PetscValidHeaderSpecific(tr, DMPLEXTRANSFORM_CLASSID, 1);
1114   ex->periodic = periodic;
1115   PetscFunctionReturn(PETSC_SUCCESS);
1116 }
1117 
1118 /*@
1119   DMPlexTransformExtrudeGetNormal - Get the extrusion normal vector
1120 
1121   Not Collective
1122 
1123   Input Parameter:
1124 . tr - The `DMPlexTransform`
1125 
1126   Output Parameter:
1127 . normal - The extrusion direction
1128 
1129   Note:
1130   The user passes in an array, which is filled by the library.
1131 
1132   Level: intermediate
1133 
1134 .seealso: `DMPlexTransform`, `DMPlexTransformExtrudeSetNormal()`
1135 @*/
1136 PetscErrorCode DMPlexTransformExtrudeGetNormal(DMPlexTransform tr, PetscReal normal[])
1137 {
1138   DMPlexTransform_Extrude *ex = (DMPlexTransform_Extrude *)tr->data;
1139   PetscInt                 d;
1140 
1141   PetscFunctionBegin;
1142   PetscValidHeaderSpecific(tr, DMPLEXTRANSFORM_CLASSID, 1);
1143   if (ex->normalAlg == NORMAL_INPUT) {
1144     for (d = 0; d < ex->cdimEx; ++d) normal[d] = ex->normal[d];
1145   } else {
1146     for (d = 0; d < ex->cdimEx; ++d) normal[d] = 0.;
1147   }
1148   PetscFunctionReturn(PETSC_SUCCESS);
1149 }
1150 
1151 /*@
1152   DMPlexTransformExtrudeSetNormal - Set the extrusion normal
1153 
1154   Not Collective
1155 
1156   Input Parameters:
1157 + tr     - The `DMPlexTransform`
1158 - normal - The extrusion direction
1159 
1160   Level: intermediate
1161 
1162 .seealso: `DMPlexTransform`, `DMPlexTransformExtrudeGetNormal()`
1163 @*/
1164 PetscErrorCode DMPlexTransformExtrudeSetNormal(DMPlexTransform tr, const PetscReal normal[])
1165 {
1166   DMPlexTransform_Extrude *ex = (DMPlexTransform_Extrude *)tr->data;
1167   PetscInt                 d;
1168 
1169   PetscFunctionBegin;
1170   PetscValidHeaderSpecific(tr, DMPLEXTRANSFORM_CLASSID, 1);
1171   ex->normalAlg = NORMAL_INPUT;
1172   for (d = 0; d < ex->cdimEx; ++d) ex->normal[d] = normal[d];
1173   PetscFunctionReturn(PETSC_SUCCESS);
1174 }
1175 
1176 /*@C
1177   DMPlexTransformExtrudeSetNormalFunction - Set a function to determine the extrusion normal
1178 
1179   Not Collective
1180 
1181   Input Parameters:
1182 + tr         - The `DMPlexTransform`
1183 - normalFunc - A function determining the extrusion direction, see `PetscSimplePointFn` for the calling sequence
1184 
1185   Level: intermediate
1186 
1187 .seealso: `DMPlexTransform`, `DMPlexTransformExtrudeGetNormal()`, `PetscSimplePointFn`
1188 @*/
1189 PetscErrorCode DMPlexTransformExtrudeSetNormalFunction(DMPlexTransform tr, PetscSimplePointFn *normalFunc)
1190 {
1191   DMPlexTransform_Extrude *ex = (DMPlexTransform_Extrude *)tr->data;
1192 
1193   PetscFunctionBegin;
1194   PetscValidHeaderSpecific(tr, DMPLEXTRANSFORM_CLASSID, 1);
1195   ex->normalFunc = normalFunc;
1196   PetscFunctionReturn(PETSC_SUCCESS);
1197 }
1198 
1199 /*@
1200   DMPlexTransformExtrudeSetThicknesses - Set the thickness of each layer
1201 
1202   Not Collective
1203 
1204   Input Parameters:
1205 + tr          - The `DMPlexTransform`
1206 . Nth         - The number of thicknesses
1207 - thicknesses - The array of thicknesses
1208 
1209   Level: intermediate
1210 
1211 .seealso: `DMPlexTransform`, `DMPlexTransformExtrudeSetThickness()`, `DMPlexTransformExtrudeGetThickness()`
1212 @*/
1213 PetscErrorCode DMPlexTransformExtrudeSetThicknesses(DMPlexTransform tr, PetscInt Nth, const PetscReal thicknesses[])
1214 {
1215   DMPlexTransform_Extrude *ex = (DMPlexTransform_Extrude *)tr->data;
1216   PetscInt                 t;
1217 
1218   PetscFunctionBegin;
1219   PetscValidHeaderSpecific(tr, DMPLEXTRANSFORM_CLASSID, 1);
1220   PetscCheck(Nth > 0, PetscObjectComm((PetscObject)tr), PETSC_ERR_ARG_OUTOFRANGE, "Number of thicknesses %" PetscInt_FMT " must be positive", Nth);
1221   ex->Nth = PetscMin(Nth, ex->layers);
1222   PetscCall(PetscFree(ex->thicknesses));
1223   PetscCall(PetscMalloc1(ex->Nth, &ex->thicknesses));
1224   for (t = 0; t < ex->Nth; ++t) {
1225     PetscCheck(thicknesses[t] > 0., PetscObjectComm((PetscObject)tr), PETSC_ERR_ARG_OUTOFRANGE, "Thickness %g of layer %" PetscInt_FMT " must be positive", (double)thicknesses[t], t);
1226     ex->thicknesses[t] = thicknesses[t];
1227   }
1228   PetscFunctionReturn(PETSC_SUCCESS);
1229 }
1230