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