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