xref: /petsc/src/dm/impls/plex/plexfluent.c (revision 87b0d978282847a17837259c7d61552776b73e0e)
1 #define PETSC_DESIRE_FEATURE_TEST_MACROS /* for fileno() */
2 #define PETSCDM_DLL
3 #include <petsc/private/dmpleximpl.h> /*I   "petscdmplex.h"   I*/
4 
5 /* Utility struct to store the contents of a Fluent file in memory */
6 typedef struct {
7   int          index; /* Type of section */
8   unsigned int zoneID;
9   unsigned int first;
10   unsigned int last;
11   int          type;
12   int          nd; /* Either ND or element-type */
13   void        *data;
14 } FluentSection;
15 
16 /*@
17   DMPlexCreateFluentFromFile - Create a `DMPLEX` mesh from a Fluent mesh file
18 
19   Collective
20 
21   Input Parameters:
22 + comm        - The MPI communicator
23 . filename    - Name of the Fluent mesh file
24 - interpolate - Create faces and edges in the mesh
25 
26   Output Parameter:
27 . dm - The `DM` object representing the mesh
28 
29   Level: beginner
30 
31 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateFromFile()`, `DMPlexCreateFluent()`, `DMPlexCreate()`
32 @*/
33 PetscErrorCode DMPlexCreateFluentFromFile(MPI_Comm comm, const char filename[], PetscBool interpolate, DM *dm)
34 {
35   PetscViewer viewer;
36 
37   PetscFunctionBegin;
38   /* Create file viewer and build plex */
39   PetscCall(PetscViewerCreate(comm, &viewer));
40   PetscCall(PetscViewerSetType(viewer, PETSCVIEWERASCII));
41   PetscCall(PetscViewerFileSetMode(viewer, FILE_MODE_READ));
42   PetscCall(PetscViewerFileSetName(viewer, filename));
43   PetscCall(DMPlexCreateFluent(comm, viewer, interpolate, dm));
44   PetscCall(PetscViewerDestroy(&viewer));
45   PetscFunctionReturn(PETSC_SUCCESS);
46 }
47 
48 static PetscErrorCode DMPlexCreateFluent_ReadString(PetscViewer viewer, char *buffer, char delim)
49 {
50   PetscInt ret, i = 0;
51 
52   PetscFunctionBegin;
53   do PetscCall(PetscViewerRead(viewer, &buffer[i++], 1, &ret, PETSC_CHAR));
54   while (ret > 0 && buffer[i - 1] != '\0' && buffer[i - 1] != delim && i < PETSC_MAX_PATH_LEN - 1);
55   if (!ret) buffer[i - 1] = '\0';
56   else buffer[i] = '\0';
57   PetscCheck(i < PETSC_MAX_PATH_LEN - 1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Buffer overflow! This is not a valid Fluent file.");
58   PetscFunctionReturn(PETSC_SUCCESS);
59 }
60 
61 static PetscErrorCode DMPlexCreateFluent_ReadValues(PetscViewer viewer, void *data, PetscInt count, PetscDataType dtype, PetscBool binary, PetscInt *numClosingParens)
62 {
63   int      fdes = 0;
64   FILE    *file;
65   PetscInt i;
66 
67   PetscFunctionBegin;
68   *numClosingParens = 0;
69   if (binary) {
70     /* Extract raw file descriptor to read binary block */
71     PetscCall(PetscViewerASCIIGetPointer(viewer, &file));
72     PetscCall(PetscFFlush(file));
73     fdes = fileno(file);
74   }
75 
76   if (!binary && dtype == PETSC_INT) {
77     char         cbuf[256];
78     unsigned int ibuf;
79     int          snum;
80     /* Parse hexadecimal ascii integers */
81     for (i = 0; i < count; i++) {
82       size_t len;
83 
84       PetscCall(PetscViewerRead(viewer, cbuf, 1, NULL, PETSC_STRING));
85       snum = sscanf(cbuf, "%x", &ibuf);
86       PetscCheck(snum == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "File is not a valid Fluent file");
87       ((PetscInt *)data)[i] = (PetscInt)ibuf;
88       // Check for trailing parentheses
89       PetscCall(PetscStrlen(cbuf, &len));
90       while (cbuf[len - 1] == ')' && len > 0) {
91         ++(*numClosingParens);
92         --len;
93       }
94     }
95   } else if (binary && dtype == PETSC_INT) {
96     /* Always read 32-bit ints and cast to PetscInt */
97     int *ibuf;
98     PetscCall(PetscMalloc1(count, &ibuf));
99     PetscCall(PetscBinaryRead(fdes, ibuf, count, NULL, PETSC_ENUM));
100     PetscCall(PetscByteSwap(ibuf, PETSC_ENUM, count));
101     for (i = 0; i < count; i++) ((PetscInt *)data)[i] = ibuf[i];
102     PetscCall(PetscFree(ibuf));
103 
104   } else if (binary && dtype == PETSC_SCALAR) {
105     float *fbuf;
106     /* Always read 32-bit floats and cast to PetscScalar */
107     PetscCall(PetscMalloc1(count, &fbuf));
108     PetscCall(PetscBinaryRead(fdes, fbuf, count, NULL, PETSC_FLOAT));
109     PetscCall(PetscByteSwap(fbuf, PETSC_FLOAT, count));
110     for (i = 0; i < count; i++) ((PetscScalar *)data)[i] = fbuf[i];
111     PetscCall(PetscFree(fbuf));
112   } else {
113     PetscCall(PetscViewerASCIIRead(viewer, data, count, NULL, dtype));
114   }
115   PetscFunctionReturn(PETSC_SUCCESS);
116 }
117 
118 static PetscErrorCode DMPlexCreateFluent_ReadSection(PetscViewer viewer, FluentSection *s)
119 {
120   char buffer[PETSC_MAX_PATH_LEN];
121   int  snum;
122 
123   PetscFunctionBegin;
124   /* Fast-forward to next section and derive its index */
125   PetscCall(DMPlexCreateFluent_ReadString(viewer, buffer, '('));
126   PetscCall(DMPlexCreateFluent_ReadString(viewer, buffer, ' '));
127   snum = sscanf(buffer, "%d", &s->index);
128   /* If we can't match an index return -1 to signal end-of-file */
129   if (snum < 1) {
130     s->index = -1;
131     PetscFunctionReturn(PETSC_SUCCESS);
132   }
133 
134   if (s->index == 0) { /* Comment */
135     PetscCall(DMPlexCreateFluent_ReadString(viewer, buffer, ')'));
136 
137   } else if (s->index == 2) { /* Dimension */
138     PetscCall(DMPlexCreateFluent_ReadString(viewer, buffer, ')'));
139     snum = sscanf(buffer, "%d", &s->nd);
140     PetscCheck(snum == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "File is not a valid Fluent file");
141 
142   } else if (s->index == 10 || s->index == 2010) { /* Vertices */
143     PetscInt numClosingParens = 0;
144 
145     PetscCall(DMPlexCreateFluent_ReadString(viewer, buffer, ')'));
146     snum = sscanf(buffer, "(%x %x %x %d %d)", &s->zoneID, &s->first, &s->last, &s->type, &s->nd);
147     PetscCheck(snum == 5, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "File is not a valid Fluent file");
148     if (s->zoneID > 0) {
149       PetscInt numCoords = s->last - s->first + 1;
150       PetscCall(DMPlexCreateFluent_ReadString(viewer, buffer, '('));
151       PetscCall(PetscMalloc1(s->nd * numCoords, (PetscScalar **)&s->data));
152       PetscCall(DMPlexCreateFluent_ReadValues(viewer, s->data, s->nd * numCoords, PETSC_SCALAR, s->index == 2010 ? PETSC_TRUE : PETSC_FALSE, &numClosingParens));
153       if (!numClosingParens) PetscCall(DMPlexCreateFluent_ReadString(viewer, buffer, ')'));
154       else --numClosingParens;
155     }
156     if (!numClosingParens) PetscCall(DMPlexCreateFluent_ReadString(viewer, buffer, ')'));
157     else --numClosingParens;
158     PetscCheck(!numClosingParens, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "File is not a valid Fluent file");
159   } else if (s->index == 12 || s->index == 2012) { /* Cells */
160     PetscInt numClosingParens = 0;
161 
162     PetscCall(DMPlexCreateFluent_ReadString(viewer, buffer, ')'));
163     snum = sscanf(buffer, "(%x", &s->zoneID);
164     PetscCheck(snum == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "File is not a valid Fluent file");
165     if (s->zoneID == 0) { /* Header section */
166       snum = sscanf(buffer, "(%x %x %x %d)", &s->zoneID, &s->first, &s->last, &s->nd);
167       PetscCheck(snum == 4, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "File is not a valid Fluent file");
168     } else { /* Data section */
169       snum = sscanf(buffer, "(%x %x %x %d %d)", &s->zoneID, &s->first, &s->last, &s->type, &s->nd);
170       PetscCheck(snum == 5, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "File is not a valid Fluent file");
171       if (s->nd == 0) {
172         /* Read cell type definitions for mixed cells */
173         PetscInt numCells = s->last - s->first + 1;
174         PetscCall(DMPlexCreateFluent_ReadString(viewer, buffer, '('));
175         PetscCall(PetscMalloc1(numCells, (PetscInt **)&s->data));
176         PetscCall(DMPlexCreateFluent_ReadValues(viewer, s->data, numCells, PETSC_INT, s->index == 2012 ? PETSC_TRUE : PETSC_FALSE, &numClosingParens));
177         if (!numClosingParens) PetscCall(DMPlexCreateFluent_ReadString(viewer, buffer, ')'));
178         else --numClosingParens;
179       }
180     }
181     if (!numClosingParens) PetscCall(DMPlexCreateFluent_ReadString(viewer, buffer, ')'));
182     else --numClosingParens;
183     PetscCheck(!numClosingParens, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "File is not a valid Fluent file");
184   } else if (s->index == 13 || s->index == 2013) { /* Faces */
185     PetscInt numClosingParens = 0;
186 
187     PetscCall(DMPlexCreateFluent_ReadString(viewer, buffer, ')'));
188     snum = sscanf(buffer, "(%x", &s->zoneID);
189     PetscCheck(snum == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "File is not a valid Fluent file");
190     if (s->zoneID == 0) { /* Header section */
191       snum = sscanf(buffer, "(%x %x %x %d)", &s->zoneID, &s->first, &s->last, &s->nd);
192       PetscCheck(snum == 4, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "File is not a valid Fluent file");
193     } else { /* Data section */
194       PetscInt numEntries, numFaces, maxsize = 0, offset = 0;
195 
196       snum = sscanf(buffer, "(%x %x %x %d %d)", &s->zoneID, &s->first, &s->last, &s->type, &s->nd);
197       PetscCheck(snum == 5, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "File is not a valid Fluent file");
198       PetscCall(DMPlexCreateFluent_ReadString(viewer, buffer, '('));
199       switch (s->nd) {
200       case 0:
201         numEntries = PETSC_DETERMINE;
202         break;
203       case 2:
204         numEntries = 2 + 2;
205         break; /* linear */
206       case 3:
207         numEntries = 2 + 3;
208         break; /* triangular */
209       case 4:
210         numEntries = 2 + 4;
211         break; /* quadrilateral */
212       default:
213         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unknown face type in Fluent file");
214       }
215       numFaces = s->last - s->first + 1;
216       if (numEntries != PETSC_DETERMINE) {
217         /* Allocate space only if we already know the size of the block */
218         PetscCall(PetscMalloc1(numEntries * numFaces, (PetscInt **)&s->data));
219       }
220       for (PetscInt f = 0; f < numFaces; f++) {
221         if (s->nd == 0) {
222           /* Determine the size of the block for "mixed" facets */
223           PetscInt numFaceVert = 0;
224           PetscCall(DMPlexCreateFluent_ReadValues(viewer, &numFaceVert, 1, PETSC_INT, s->index == 2013 ? PETSC_TRUE : PETSC_FALSE, &numClosingParens));
225           if (!f) {
226             maxsize = (numFaceVert + 3) * numFaces;
227             PetscCall(PetscMalloc1(maxsize, (PetscInt **)&s->data));
228           } else {
229             if (offset + numFaceVert + 3 >= maxsize) {
230               PetscInt *tmp;
231 
232               PetscCall(PetscMalloc1(maxsize * 2, &tmp));
233               PetscCall(PetscArraycpy(tmp, (PetscInt *)s->data, maxsize));
234               PetscCall(PetscFree(s->data));
235               maxsize *= 2;
236               s->data = tmp;
237             }
238           }
239           ((PetscInt *)s->data)[offset] = numFaceVert;
240           ++offset;
241           numEntries = numFaceVert + 2;
242         }
243         PetscCall(DMPlexCreateFluent_ReadValues(viewer, &(((PetscInt *)s->data)[offset]), numEntries, PETSC_INT, s->index == 2013 ? PETSC_TRUE : PETSC_FALSE, &numClosingParens));
244         offset += numEntries;
245       }
246       if (s->nd != 0) PetscCall(PetscMPIIntCast(numEntries - 2, &s->nd));
247       if (!numClosingParens) PetscCall(DMPlexCreateFluent_ReadString(viewer, buffer, ')'));
248       else --numClosingParens;
249     }
250     if (!numClosingParens) PetscCall(DMPlexCreateFluent_ReadString(viewer, buffer, ')'));
251     else --numClosingParens;
252     PetscCheck(!numClosingParens, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "File is not a valid Fluent file");
253   } else if (s->index == 39) { /* Label information */
254     char labelName[PETSC_MAX_PATH_LEN];
255     char caseName[PETSC_MAX_PATH_LEN];
256 
257     PetscCall(DMPlexCreateFluent_ReadString(viewer, buffer, ')'));
258     snum = sscanf(buffer, "(%u %s %s %d)", &s->zoneID, caseName, labelName, &s->nd);
259     PetscCheck(snum == 4, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "File is not a valid Fluent file: %d", snum);
260     PetscInt depth = 1;
261     do {
262       /* Match parentheses when parsing unknown sections */
263       do PetscCall(PetscViewerRead(viewer, &buffer[0], 1, NULL, PETSC_CHAR));
264       while (buffer[0] != '(' && buffer[0] != ')');
265       if (buffer[0] == '(') depth++;
266       if (buffer[0] == ')') depth--;
267     } while (depth > 0);
268     PetscCall(DMPlexCreateFluent_ReadString(viewer, buffer, '\n'));
269     PetscCall(PetscStrallocpy(labelName, (char **)&s->data));
270     PetscCall(PetscInfo((PetscObject)viewer, "CASE: Zone ID %u is label %s\n", s->zoneID, labelName));
271   } else { /* Unknown section type */
272     PetscInt depth = 1;
273     do {
274       /* Match parentheses when parsing unknown sections */
275       do PetscCall(PetscViewerRead(viewer, &buffer[0], 1, NULL, PETSC_CHAR));
276       while (buffer[0] != '(' && buffer[0] != ')');
277       if (buffer[0] == '(') depth++;
278       if (buffer[0] == ')') depth--;
279     } while (depth > 0);
280     PetscCall(DMPlexCreateFluent_ReadString(viewer, buffer, '\n'));
281   }
282   PetscFunctionReturn(PETSC_SUCCESS);
283 }
284 
285 // Inserts point `face` with orientation `ornt` into the cone of point `cell` at position `c`, which is the first empty slot
286 static PetscErrorCode InsertFace(DM dm, PetscInt cell, PetscInt face, PetscInt ornt)
287 {
288   const PetscInt *cone;
289   PetscInt        coneSize, c;
290 
291   PetscFunctionBegin;
292   PetscCall(DMPlexGetCone(dm, cell, &cone));
293   PetscCall(DMPlexGetConeSize(dm, cell, &coneSize));
294   for (c = 0; c < coneSize; ++c)
295     if (cone[c] < 0) break;
296   PetscCheck(c < coneSize, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " could not be inserted in cone of cell %" PetscInt_FMT " with size %" PetscInt_FMT, face, cell, coneSize);
297   PetscCall(DMPlexInsertCone(dm, cell, c, face));
298   PetscCall(DMPlexInsertConeOrientation(dm, cell, c, ornt));
299   PetscFunctionReturn(PETSC_SUCCESS);
300 }
301 
302 static PetscErrorCode ReorderPolygon(DM dm, PetscInt cell)
303 {
304   const PetscInt *cone, *ornt;
305   PetscInt        coneSize, newCone[16], newOrnt[16];
306 
307   PetscFunctionBegin;
308   PetscCall(DMPlexGetOrientedCone(dm, cell, &cone, &ornt));
309   PetscCall(DMPlexGetConeSize(dm, cell, &coneSize));
310   newCone[0] = cone[0];
311   newOrnt[0] = ornt[0];
312   for (PetscInt c = 1; c < coneSize; ++c) {
313     const PetscInt *fcone;
314     PetscInt        firstVertex, lastVertex, c2;
315 
316     PetscCall(DMPlexGetCone(dm, newCone[c - 1], &fcone));
317     lastVertex = newOrnt[c - 1] ? fcone[0] : fcone[1];
318     for (c2 = 0; c2 < coneSize; ++c2) {
319       const PetscInt *fcone2;
320 
321       PetscCall(DMPlexGetCone(dm, cone[c2], &fcone2));
322       firstVertex = ornt[c2] ? fcone2[1] : fcone2[0];
323       if (lastVertex == firstVertex) {
324         // Point `cell` matched point `lastVertex` on face `cone[c2]` with orientation `ornt[c2]`
325         break;
326       }
327     }
328     PetscCheck(c2 < coneSize, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " could not find a face match as position %" PetscInt_FMT, cell, c);
329     newCone[c] = cone[c2];
330     newOrnt[c] = ornt[c2];
331   }
332   {
333     const PetscInt *fcone, *fcone2;
334     PetscInt        vertex, vertex2;
335 
336     PetscCall(DMPlexGetCone(dm, newCone[coneSize - 1], &fcone));
337     PetscCall(DMPlexGetCone(dm, newCone[0], &fcone2));
338     vertex  = newOrnt[coneSize - 1] ? fcone[0] : fcone[1];
339     vertex2 = newOrnt[0] ? fcone2[1] : fcone2[0];
340     PetscCheck(vertex == vertex2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " did not match at the endpoint", cell);
341   }
342   PetscCall(DMPlexSetCone(dm, cell, newCone));
343   PetscCall(DMPlexSetConeOrientation(dm, cell, newOrnt));
344   PetscCall(DMPlexRestoreOrientedCone(dm, cell, &cone, &ornt));
345   PetscFunctionReturn(PETSC_SUCCESS);
346 }
347 
348 static PetscErrorCode ReorderTetrahedron(PetscViewer viewer, DM dm, PetscInt cell)
349 {
350   const PetscInt *cone, *ornt, *fcone, *fornt, *farr, faces[4] = {0, 1, 3, 2};
351   PetscInt        newCone[16], newOrnt[16];
352 
353   PetscFunctionBegin;
354   PetscCall(DMPlexGetOrientedCone(dm, cell, &cone, &ornt));
355   newCone[0] = cone[0];
356   newOrnt[0] = ornt[0];
357   PetscCall(DMPlexGetOrientedCone(dm, newCone[0], &fcone, &fornt));
358   farr = DMPolytopeTypeGetArrangement(DM_POLYTOPE_TRIANGLE, newOrnt[0]);
359   // Loop over each edge in the initial triangle
360   for (PetscInt e = 0; e < 3; ++e) {
361     const PetscInt edge = fcone[farr[e * 2 + 0]], eornt = DMPolytopeTypeComposeOrientation(DM_POLYTOPE_SEGMENT, farr[e * 2 + 1], fornt[farr[e * 2 + 0]]);
362     PetscInt       c;
363 
364     // Loop over each remaining face in the tetrahedron
365     //   On face `newCone[0]`, trying to match edge `edge` with final orientation `eornt` to an edge on another face
366     for (c = 1; c < 4; ++c) {
367       const PetscInt *fcone2, *fornt2, *farr2;
368       PetscInt        c2;
369       PetscBool       flip = PETSC_FALSE;
370 
371       // Checking face `cone[c]` with orientation `ornt[c]`
372       PetscCall(DMPlexGetOrientedCone(dm, cone[c], &fcone2, &fornt2));
373       farr2 = DMPolytopeTypeGetArrangement(DM_POLYTOPE_TRIANGLE, ornt[c]);
374       // Check for edge
375       for (c2 = 0; c2 < 3; ++c2) {
376         const PetscInt edge2 = fcone2[farr2[c2 * 2 + 0]], eornt2 = DMPolytopeTypeComposeOrientation(DM_POLYTOPE_SEGMENT, farr2[c2 * 2 + 1], fornt2[farr2[c2 * 2 + 0]]);
377         // Trying to match edge `edge2` with final orientation `eornt2`
378         if (edge == edge2) {
379           //PetscCheck(eornt == -(eornt2 + 1), PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell % " PetscInt_FMT " edge %" PetscInt_FMT " (%" PetscInt_FMT ") found twice with the same orientation in face %" PetscInt_FMT " edge %" PetscInt_FMT, cell, edge, e, c, c2);
380           // Matched face `newCone[0]` with orientation `newOrnt[0]` to face `cone[c]` with orientation `ornt[c]` along edge `edge`
381           PetscCall(PetscInfo((PetscObject)viewer, "CASE: Matched cell %" PetscInt_FMT " edge %" PetscInt_FMT "/%" PetscInt_FMT " (%" PetscInt_FMT ") to face %" PetscInt_FMT "/%" PetscInt_FMT " edge %" PetscInt_FMT " (%" PetscInt_FMT ")\n", cell, edge, e, eornt, cone[c], c, c2, eornt2));
382           flip = eornt != -(eornt2 + 1) ? PETSC_TRUE : PETSC_FALSE;
383           break;
384         }
385       }
386       if (c2 < 3) {
387         newCone[faces[e + 1]] = cone[c];
388         // Compute new orientation of face based on which edge was matched (only the first edge matches a side different from 0)
389         //   Face 1 should match its edge 2
390         //   Face 2 should match its edge 0
391         //   Face 3 should match its edge 0
392         if (flip) {
393           newOrnt[faces[e + 1]] = DMPolytopeTypeComposeOrientation(DM_POLYTOPE_TRIANGLE, -((c2 + (!e ? 1 : 2)) % 3 + 1), ornt[c]);
394         } else {
395           newOrnt[faces[e + 1]] = DMPolytopeTypeComposeOrientation(DM_POLYTOPE_TRIANGLE, !e ? (c2 + 1) % 3 : c2, ornt[c]);
396         }
397         break;
398       }
399     }
400     PetscCheck(c < 4, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " could not find a face match for edge %" PetscInt_FMT, cell, e);
401   }
402   PetscCall(DMPlexRestoreOrientedCone(dm, newCone[0], &fcone, &fornt));
403   PetscCall(DMPlexSetCone(dm, cell, newCone));
404   PetscCall(DMPlexSetConeOrientation(dm, cell, newOrnt));
405   PetscCall(DMPlexRestoreOrientedCone(dm, cell, &cone, &ornt));
406   PetscFunctionReturn(PETSC_SUCCESS);
407 }
408 
409 static PetscErrorCode ReorderHexahedron(DM dm, PetscInt cell)
410 {
411   const PetscInt *cone, *ornt, *fcone, *fornt, *farr;
412   const PetscInt  faces[6] = {0, 5, 3, 4, 2, 1};
413   PetscInt        used[6]  = {1, 0, 0, 0, 0, 0};
414   PetscInt        newCone[16], newOrnt[16];
415 
416   PetscFunctionBegin;
417   PetscCall(DMPlexGetOrientedCone(dm, cell, &cone, &ornt));
418   newCone[0] = cone[0];
419   newOrnt[0] = ornt[0];
420   PetscCall(DMPlexGetOrientedCone(dm, newCone[0], &fcone, &fornt));
421   farr = DMPolytopeTypeGetArrangement(DM_POLYTOPE_QUADRILATERAL, newOrnt[0]);
422   // Loop over each edge in the initial quadrilateral
423   for (PetscInt e = 0; e < 4; ++e) {
424     const PetscInt edge = fcone[farr[e * 2 + 0]], eornt = DMPolytopeTypeComposeOrientation(DM_POLYTOPE_SEGMENT, farr[e * 2 + 1], fornt[farr[e * 2 + 0]]);
425     PetscInt       c;
426 
427     // Loop over each remaining face in the hexahedron
428     //   On face `newCone[0]`, trying to match edge `edge` with final orientation `eornt` to an edge on another face
429     for (c = 1; c < 6; ++c) {
430       const PetscInt *fcone2, *fornt2, *farr2;
431       PetscInt        c2;
432 
433       // Checking face `cone[c]` with orientation `ornt[c]`
434       PetscCall(DMPlexGetOrientedCone(dm, cone[c], &fcone2, &fornt2));
435       farr2 = DMPolytopeTypeGetArrangement(DM_POLYTOPE_QUADRILATERAL, ornt[c]);
436       // Check for edge
437       for (c2 = 0; c2 < 4; ++c2) {
438         const PetscInt edge2 = fcone2[farr2[c2 * 2 + 0]], eornt2 = DMPolytopeTypeComposeOrientation(DM_POLYTOPE_SEGMENT, farr2[c2 * 2 + 1], fornt2[farr2[c2 * 2 + 0]]);
439         // Trying to match edge `edge2` with final orientation `eornt2`
440         if (edge == edge2) {
441           PetscCheck(eornt == -(eornt2 + 1), PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " found twice with the same orientation", edge);
442           // Matched face `newCone[0]` with orientation `newOrnt[0]` to face `cone[c]` with orientation `ornt[c]` along edge `edge`
443           break;
444         }
445       }
446       if (c2 < 4) {
447         used[c]               = 1;
448         newCone[faces[e + 1]] = cone[c];
449         // Compute new orientation of face based on which edge was matched (only the first edge matches a side different from 0)
450         newOrnt[faces[e + 1]] = DMPolytopeTypeComposeOrientation(DM_POLYTOPE_QUADRILATERAL, !e ? (c2 + 1) % 4 : c2, ornt[c]);
451         break;
452       }
453     }
454     PetscCheck(c < 6, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " could not find a face match for edge %" PetscInt_FMT, cell, e);
455   }
456   PetscCall(DMPlexRestoreOrientedCone(dm, newCone[0], &fcone, &fornt));
457   // Add last face
458   {
459     PetscInt c, c2;
460 
461     for (c = 1; c < 6; ++c)
462       if (!used[c]) break;
463     PetscCheck(c < 6, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " could not find an available face", cell);
464     // Match first edge to 3rd edge in newCone[2]
465     {
466       const PetscInt *fcone2, *fornt2, *farr2;
467 
468       PetscCall(DMPlexGetOrientedCone(dm, newCone[2], &fcone, &fornt));
469       farr = DMPolytopeTypeGetArrangement(DM_POLYTOPE_QUADRILATERAL, newOrnt[2]);
470       PetscCall(DMPlexGetOrientedCone(dm, cone[c], &fcone2, &fornt2));
471       farr2 = DMPolytopeTypeGetArrangement(DM_POLYTOPE_QUADRILATERAL, ornt[c]);
472 
473       const PetscInt e    = 2;
474       const PetscInt edge = fcone[farr[e * 2 + 0]], eornt = DMPolytopeTypeComposeOrientation(DM_POLYTOPE_SEGMENT, farr[e * 2 + 1], fornt[farr[e * 2 + 0]]);
475       // Trying to match edge `edge` with final orientation `eornt` of face `newCone[2]` to some edge of face `cone[c]` with orientation `ornt[c]`
476       for (c2 = 0; c2 < 4; ++c2) {
477         const PetscInt edge2 = fcone2[farr2[c2 * 2 + 0]], eornt2 = DMPolytopeTypeComposeOrientation(DM_POLYTOPE_SEGMENT, farr2[c2 * 2 + 1], fornt2[farr2[c2 * 2 + 0]]);
478         // Trying to match edge `edge2` with final orientation `eornt2`
479         if (edge == edge2) {
480           PetscCheck(eornt == -(eornt2 + 1), PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " found twice with the same orientation", edge);
481           // Matched face `newCone[2]` with orientation `newOrnt[2]` to face `cone[c]` with orientation `ornt[c]` along edge `edge`
482           break;
483         }
484       }
485       PetscCheck(c2 < 4, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Could not fit last face in");
486     }
487     newCone[faces[5]] = cone[c];
488     // Compute new orientation of face based on which edge was matched
489     newOrnt[faces[5]] = DMPolytopeTypeComposeOrientation(DM_POLYTOPE_QUADRILATERAL, c2, ornt[c]);
490     PetscCall(DMPlexRestoreOrientedCone(dm, newCone[0], &fcone, &fornt));
491   }
492   PetscCall(DMPlexSetCone(dm, cell, newCone));
493   PetscCall(DMPlexSetConeOrientation(dm, cell, newOrnt));
494   PetscCall(DMPlexRestoreOrientedCone(dm, cell, &cone, &ornt));
495   PetscFunctionReturn(PETSC_SUCCESS);
496 }
497 
498 // {0, 1, 2}, {3, 4, 5}, {0, 2, 4, 3}, {2, 1, 5, 4}, {1, 0, 3, 5}
499 static PetscErrorCode ReorderWedge(DM dm, PetscInt cell)
500 {
501   const PetscInt *cone, *ornt, *fcone, *fornt, *farr;
502   const PetscInt  faces[5] = {0, 4, 3, 2, 1};
503   PetscInt        used[5]  = {0, 0, 0, 0, 0};
504   PetscInt        newCone[16], newOrnt[16], cS, bottom = 0;
505 
506   PetscFunctionBegin;
507   PetscCall(DMPlexGetConeSize(dm, cell, &cS));
508   PetscCall(DMPlexGetOrientedCone(dm, cell, &cone, &ornt));
509   for (PetscInt c = 0; c < cS; ++c) {
510     DMPolytopeType ct;
511 
512     PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
513     if (ct == DM_POLYTOPE_TRIANGLE) {
514       bottom = c;
515       break;
516     }
517   }
518   used[bottom] = 1;
519   newCone[0]   = cone[bottom];
520   newOrnt[0]   = ornt[bottom];
521   PetscCall(DMPlexGetOrientedCone(dm, newCone[0], &fcone, &fornt));
522   farr = DMPolytopeTypeGetArrangement(DM_POLYTOPE_TRIANGLE, newOrnt[0]);
523   // Loop over each edge in the initial triangle
524   for (PetscInt e = 0; e < 3; ++e) {
525     const PetscInt edge = fcone[farr[e * 2 + 0]], eornt = DMPolytopeTypeComposeOrientation(DM_POLYTOPE_SEGMENT, farr[e * 2 + 1], fornt[farr[e * 2 + 0]]);
526     PetscInt       c;
527 
528     // Loop over each remaining face in the prism
529     //   On face `newCone[0]`, trying to match edge `edge` with final orientation `eornt` to an edge on another face
530     for (c = 0; c < 5; ++c) {
531       const PetscInt *fcone2, *fornt2, *farr2;
532       DMPolytopeType  ct;
533       PetscInt        c2;
534 
535       if (c == bottom) continue;
536       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
537       if (ct != DM_POLYTOPE_QUADRILATERAL) continue;
538       // Checking face `cone[c]` with orientation `ornt[c]`
539       PetscCall(DMPlexGetOrientedCone(dm, cone[c], &fcone2, &fornt2));
540       farr2 = DMPolytopeTypeGetArrangement(DM_POLYTOPE_QUADRILATERAL, ornt[c]);
541       // Check for edge
542       for (c2 = 0; c2 < 4; ++c2) {
543         const PetscInt edge2 = fcone2[farr2[c2 * 2 + 0]], eornt2 = DMPolytopeTypeComposeOrientation(DM_POLYTOPE_SEGMENT, farr2[c2 * 2 + 1], fornt2[farr2[c2 * 2 + 0]]);
544         // Trying to match edge `edge2` with final orientation `eornt2`
545         if (edge == edge2) {
546           PetscCheck(eornt == -(eornt2 + 1), PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " found twice with the same orientation", edge);
547           // Matched face `newCone[0]` with orientation `newOrnt[0]` to face `cone[c]` with orientation `ornt[c]` along edge `edge`
548           break;
549         }
550       }
551       if (c2 < 4) {
552         used[c]               = 1;
553         newCone[faces[e + 1]] = cone[c];
554         // Compute new orientation of face based on which edge was matched, edge 0 should always match the bottom
555         newOrnt[faces[e + 1]] = DMPolytopeTypeComposeOrientation(DM_POLYTOPE_QUADRILATERAL, c2, ornt[c]);
556         break;
557       }
558     }
559     PetscCheck(c < 5, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " could not find a face match for edge %" PetscInt_FMT, cell, e);
560   }
561   PetscCall(DMPlexRestoreOrientedCone(dm, newCone[0], &fcone, &fornt));
562   // Add last face
563   {
564     PetscInt c, c2;
565 
566     for (c = 0; c < 5; ++c)
567       if (!used[c]) break;
568     PetscCheck(c < 5, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " could not find an available face", cell);
569     // Match first edge to 3rd edge in newCone[2]
570     {
571       const PetscInt *fcone2, *fornt2, *farr2;
572 
573       PetscCall(DMPlexGetOrientedCone(dm, newCone[2], &fcone, &fornt));
574       farr = DMPolytopeTypeGetArrangement(DM_POLYTOPE_QUADRILATERAL, newOrnt[2]);
575       PetscCall(DMPlexGetOrientedCone(dm, cone[c], &fcone2, &fornt2));
576       farr2 = DMPolytopeTypeGetArrangement(DM_POLYTOPE_TRIANGLE, ornt[c]);
577 
578       const PetscInt e    = 2;
579       const PetscInt edge = fcone[farr[e * 2 + 0]], eornt = DMPolytopeTypeComposeOrientation(DM_POLYTOPE_SEGMENT, farr[e * 2 + 1], fornt[farr[e * 2 + 0]]);
580       // Trying to match edge `edge` with final orientation `eornt` of face `newCone[2]` to some edge of face `cone[c]` with orientation `ornt[c]`
581       for (c2 = 0; c2 < 3; ++c2) {
582         const PetscInt edge2 = fcone2[farr2[c2 * 2 + 0]], eornt2 = DMPolytopeTypeComposeOrientation(DM_POLYTOPE_SEGMENT, farr2[c2 * 2 + 1], fornt2[farr2[c2 * 2 + 0]]);
583         // Trying to match edge `edge2` with final orientation `eornt2`
584         if (edge == edge2) {
585           PetscCheck(eornt == -(eornt2 + 1), PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " found twice with the same orientation", edge);
586           // Matched face `newCone[2]` with orientation `newOrnt[2]` to face `cone[c]` with orientation `ornt[c]` along edge `edge`
587           break;
588         }
589       }
590       PetscCheck(c2 < 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Could not fit last face in");
591     }
592     newCone[faces[4]] = cone[c];
593     // Compute new orientation of face based on which edge was matched
594     newOrnt[faces[4]] = DMPolytopeTypeComposeOrientation(DM_POLYTOPE_TRIANGLE, c2, ornt[c]);
595     PetscCall(DMPlexRestoreOrientedCone(dm, newCone[0], &fcone, &fornt));
596   }
597   PetscCall(DMPlexSetCone(dm, cell, newCone));
598   PetscCall(DMPlexSetConeOrientation(dm, cell, newOrnt));
599   PetscCall(DMPlexRestoreOrientedCone(dm, cell, &cone, &ornt));
600   PetscFunctionReturn(PETSC_SUCCESS);
601 }
602 
603 static PetscErrorCode ReorderCell(PetscViewer viewer, DM dm, PetscInt cell, DMPolytopeType ct)
604 {
605   PetscFunctionBegin;
606   switch (ct) {
607   case DM_POLYTOPE_TRIANGLE:
608   case DM_POLYTOPE_QUADRILATERAL:
609     PetscCall(ReorderPolygon(dm, cell));
610     break;
611   case DM_POLYTOPE_TETRAHEDRON:
612     PetscCall(ReorderTetrahedron(viewer, dm, cell));
613     break;
614   case DM_POLYTOPE_HEXAHEDRON:
615     PetscCall(ReorderHexahedron(dm, cell));
616     break;
617   case DM_POLYTOPE_TRI_PRISM:
618     PetscCall(ReorderWedge(dm, cell));
619     break;
620   default:
621     PetscCheck(0, PETSC_COMM_SELF, PETSC_ERR_SUP, "Celltype %s is unsupported", DMPolytopeTypes[ct]);
622     break;
623   }
624   PetscFunctionReturn(PETSC_SUCCESS);
625 }
626 
627 static PetscErrorCode GetNumCellFaces(int nd, PetscInt *numCellFaces, DMPolytopeType *ct)
628 {
629   PetscFunctionBegin;
630   *ct = DM_POLYTOPE_POINT;
631   switch (nd) {
632   case 0:
633     *numCellFaces = PETSC_DETERMINE;
634     break;
635   case 1:
636     *numCellFaces = 3;
637     *ct           = DM_POLYTOPE_TRIANGLE;
638     break;
639   case 2:
640     *numCellFaces = 4;
641     *ct           = DM_POLYTOPE_TETRAHEDRON;
642     break;
643   case 3:
644     *numCellFaces = 4;
645     *ct           = DM_POLYTOPE_QUADRILATERAL;
646     break;
647   case 4:
648     *numCellFaces = 6;
649     *ct           = DM_POLYTOPE_HEXAHEDRON;
650     break;
651   case 5:
652     *numCellFaces = 5;
653     *ct           = DM_POLYTOPE_PYRAMID;
654     break;
655   case 6:
656     *numCellFaces = 5;
657     *ct           = DM_POLYTOPE_TRI_PRISM;
658     break;
659   default:
660     *numCellFaces = PETSC_DETERMINE;
661   }
662   PetscFunctionReturn(PETSC_SUCCESS);
663 }
664 
665 /*@C
666   DMPlexCreateFluent - Create a `DMPLEX` mesh from a Fluent mesh file <http://aerojet.engr.ucdavis.edu/fluenthelp/html/ug/node1490.htm>.
667 
668   Collective
669 
670   Input Parameters:
671 + comm        - The MPI communicator
672 . viewer      - The `PetscViewer` associated with a Fluent mesh file
673 - interpolate - Create faces and edges in the mesh
674 
675   Output Parameter:
676 . dm - The `DM` object representing the mesh
677 
678   Level: beginner
679 
680 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`
681 @*/
682 PetscErrorCode DMPlexCreateFluent(MPI_Comm comm, PetscViewer viewer, PetscBool interpolate, DM *dm)
683 {
684   PetscInt        dim          = PETSC_DETERMINE;
685   PetscInt        numCells     = 0;
686   PetscInt        numVertices  = 0;
687   PetscInt       *cellSizes    = NULL;
688   DMPolytopeType *cellTypes    = NULL;
689   PetscInt        numFaces     = 0;
690   PetscInt       *faces        = NULL;
691   PetscInt       *faceSizes    = NULL;
692   PetscInt       *faceAdjCell  = NULL;
693   PetscInt       *cellVertices = NULL;
694   unsigned int   *faceZoneIDs  = NULL;
695   DMLabel         faceSets     = NULL;
696   DMLabel        *zoneLabels   = NULL;
697   const char    **zoneNames    = NULL;
698   unsigned int    maxZoneID    = 0;
699   PetscScalar    *coordsIn     = NULL;
700   PetscScalar    *coords;
701   PetscSection    coordSection;
702   Vec             coordinates;
703   PetscInt        coordSize, maxFaceSize = 0, totFaceVert = 0, f;
704   PetscMPIInt     rank;
705 
706   PetscFunctionBegin;
707   PetscCallMPI(MPI_Comm_rank(comm, &rank));
708 
709   if (rank == 0) {
710     FluentSection s;
711 
712     s.data   = NULL;
713     numFaces = PETSC_DETERMINE;
714     do {
715       PetscCall(DMPlexCreateFluent_ReadSection(viewer, &s));
716       if (s.index == 2) { /* Dimension */
717         dim = s.nd;
718         PetscCall(PetscInfo((PetscObject)viewer, "CASE: Found dimension: %" PetscInt_FMT "\n", dim));
719       } else if (s.index == 10 || s.index == 2010) { /* Vertices */
720         if (s.zoneID == 0) {
721           numVertices = s.last;
722           PetscCall(PetscInfo((PetscObject)viewer, "CASE: Found number of vertices: %" PetscInt_FMT "\n", numVertices));
723         } else {
724           PetscCall(PetscInfo((PetscObject)viewer, "CASE: Found vertex coordinates\n"));
725           PetscCheck(!coordsIn, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Currently no support for multiple coordinate sets in Fluent files");
726           coordsIn = (PetscScalar *)s.data;
727         }
728 
729       } else if (s.index == 12 || s.index == 2012) { /* Cells */
730         if (s.zoneID == 0) {
731           numCells = s.last;
732           PetscCall(PetscInfo((PetscObject)viewer, "CASE: Found number of cells %" PetscInt_FMT "\n", numCells));
733         } else {
734           PetscCall(PetscMalloc2(numCells, &cellSizes, numCells, &cellTypes));
735           for (PetscInt c = 0; c < numCells; ++c) PetscCall(GetNumCellFaces(s.nd ? s.nd : ((PetscInt *)s.data)[c], &cellSizes[c], &cellTypes[c]));
736           PetscCall(PetscFree(s.data));
737           PetscCall(PetscInfo((PetscObject)viewer, "CASE: Found number of cell faces %" PetscInt_FMT "\n", numCells && s.nd ? cellSizes[0] : 0));
738         }
739       } else if (s.index == 13 || s.index == 2013) { /* Facets */
740         if (s.zoneID == 0) {                         /* Header section */
741           numFaces = (PetscInt)(s.last - s.first + 1);
742           PetscCall(PetscInfo((PetscObject)viewer, "CASE: Found number of faces %" PetscInt_FMT " face vertices: %d\n", numFaces, s.nd));
743         } else { /* Data section */
744           PetscInt *tmp;
745           PetscInt  totSize = 0, offset = 0, doffset;
746 
747           PetscCheck(numFaces >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "No header section for facets in Fluent file");
748           if (!faceZoneIDs) PetscCall(PetscMalloc3(numFaces, &faceSizes, numFaces * 2, &faceAdjCell, numFaces, &faceZoneIDs));
749           // Record the zoneID and face size for each face set
750           for (unsigned int z = s.first - 1; z < s.last; z++) {
751             faceZoneIDs[z] = s.zoneID;
752             if (s.nd) {
753               faceSizes[z] = s.nd;
754             } else {
755               faceSizes[z] = ((PetscInt *)s.data)[offset];
756               offset += faceSizes[z] + 3;
757             }
758             totSize += faceSizes[z];
759             maxFaceSize = PetscMax(maxFaceSize, faceSizes[z]);
760           }
761 
762           offset  = totFaceVert;
763           doffset = s.nd ? 0 : 1;
764           PetscCall(PetscMalloc1(totFaceVert + totSize, &tmp));
765           if (faces) PetscCall(PetscArraycpy(tmp, faces, totFaceVert));
766           PetscCall(PetscFree(faces));
767           totFaceVert += totSize;
768           faces = tmp;
769 
770           // Record face vertices and adjacent faces
771           const PetscInt Nfz = s.last - s.first + 1;
772           for (PetscInt f = 0; f < Nfz; ++f) {
773             const PetscInt face     = f + s.first - 1;
774             const PetscInt faceSize = faceSizes[face];
775 
776             for (PetscInt v = 0; v < faceSize; ++v) faces[offset + v] = ((PetscInt *)s.data)[doffset + v];
777             faceAdjCell[face * 2 + 0] = ((PetscInt *)s.data)[doffset + faceSize + 0];
778             faceAdjCell[face * 2 + 1] = ((PetscInt *)s.data)[doffset + faceSize + 1];
779             offset += faceSize;
780             doffset += faceSize + (s.nd ? 2 : 3);
781           }
782           PetscCall(PetscFree(s.data));
783         }
784       } else if (s.index == 39) { /* Label information */
785         if (s.zoneID >= maxZoneID) {
786           DMLabel     *tmpL;
787           const char **tmp;
788           unsigned int newmax = maxZoneID + 1;
789 
790           while (newmax < s.zoneID + 1) newmax *= 2;
791           PetscCall(PetscCalloc2(newmax, &tmp, newmax, &tmpL));
792           for (PetscInt i = 0; i < (PetscInt)maxZoneID; ++i) {
793             tmp[i]  = zoneNames[i];
794             tmpL[i] = zoneLabels[i];
795           }
796           maxZoneID = newmax;
797           PetscCall(PetscFree2(zoneNames, zoneLabels));
798           zoneNames  = tmp;
799           zoneLabels = tmpL;
800         }
801         zoneNames[s.zoneID] = (const char *)s.data;
802       }
803     } while (s.index >= 0);
804   }
805   PetscCallMPI(MPI_Bcast(&dim, 1, MPIU_INT, 0, comm));
806   PetscCheck(dim >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Fluent file does not include dimension");
807 
808   /* Allocate cell-vertex mesh */
809   PetscCall(DMCreate(comm, dm));
810   PetscCall(DMSetType(*dm, DMPLEX));
811   PetscCall(DMSetDimension(*dm, dim));
812   // We do not want this label automatically computed, instead we fill it here
813   PetscCall(DMCreateLabel(*dm, "celltype"));
814   PetscCall(DMPlexSetChart(*dm, 0, numCells + numFaces + numVertices));
815   if (rank == 0) {
816     PetscCheck(numCells >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unknown number of cells in Fluent file");
817     for (PetscInt c = 0; c < numCells; ++c) {
818       PetscCall(DMPlexSetConeSize(*dm, c, cellSizes[c]));
819       PetscCall(DMPlexSetCellType(*dm, c, cellTypes[c]));
820     }
821     for (PetscInt v = numCells; v < numCells + numVertices; ++v) PetscCall(DMPlexSetCellType(*dm, v, DM_POLYTOPE_POINT));
822     for (PetscInt f = 0; f < numFaces; ++f) {
823       DMPolytopeType ct;
824 
825       switch (faceSizes[f]) {
826       case 2:
827         ct = DM_POLYTOPE_SEGMENT;
828         break;
829       case 3:
830         ct = DM_POLYTOPE_TRIANGLE;
831         break;
832       case 4:
833         ct = DM_POLYTOPE_QUADRILATERAL;
834         break;
835       default:
836         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unknown face type in Fluent file with cone size %" PetscInt_FMT, faceSizes[f]);
837       }
838       PetscCall(DMPlexSetConeSize(*dm, f + numCells + numVertices, faceSizes[f]));
839       PetscCall(DMPlexSetCellType(*dm, f + numCells + numVertices, ct));
840     }
841   }
842   PetscCall(DMSetUp(*dm));
843 
844   if (rank == 0 && faces) {
845     PetscSection s;
846     PetscInt    *cones, csize, foffset = 0;
847 
848     PetscCall(DMPlexGetCones(*dm, &cones));
849     PetscCall(DMPlexGetConeSection(*dm, &s));
850     PetscCall(PetscSectionGetConstrainedStorageSize(s, &csize));
851     for (PetscInt c = 0; c < csize; ++c) cones[c] = -1;
852     for (PetscInt f = 0; f < numFaces; f++) {
853       const PetscInt cl   = faceAdjCell[f * 2 + 0] - 1;
854       const PetscInt cr   = faceAdjCell[f * 2 + 1] - 1;
855       const PetscInt face = f + numCells + numVertices;
856       PetscInt       fcone[16];
857 
858       // How could Fluent define the outward normal differently? Is there no end to the pain?
859       if (dim == 3) {
860         if (cl >= 0) PetscCall(InsertFace(*dm, cl, face, -1));
861         if (cr >= 0) PetscCall(InsertFace(*dm, cr, face, 0));
862       } else {
863         if (cl >= 0) PetscCall(InsertFace(*dm, cl, face, 0));
864         if (cr >= 0) PetscCall(InsertFace(*dm, cr, face, -1));
865       }
866       PetscCheck(faceSizes[f] < 16, PETSC_COMM_SELF, PETSC_ERR_SUP, "Number of face vertices %" PetscInt_FMT " exceeds temporary storage", faceSizes[f]);
867       for (PetscInt v = 0; v < faceSizes[f]; ++v) fcone[v] = faces[foffset + v] + numCells - 1;
868       foffset += faceSizes[f];
869       PetscCall(DMPlexSetCone(*dm, face, fcone));
870     }
871   }
872   PetscCall(DMPlexSymmetrize(*dm));
873   PetscCall(DMPlexStratify(*dm));
874   if (dim == 3) {
875     DM idm;
876 
877     PetscCall(DMCreate(PetscObjectComm((PetscObject)*dm), &idm));
878     PetscCall(DMSetType(idm, DMPLEX));
879     PetscCall(DMSetDimension(idm, dim));
880     PetscCall(DMPlexInterpolateFaces_Internal(*dm, 1, idm));
881     PetscCall(DMDestroy(dm));
882     *dm = idm;
883   }
884   PetscCall(DMViewFromOptions(*dm, NULL, "-cas_dm_view"));
885   if (rank == 0 && faces) {
886     for (PetscInt c = 0; c < numCells; ++c) PetscCall(ReorderCell(viewer, *dm, c, cellTypes[c]));
887   }
888 
889   if (rank == 0 && faces) {
890     PetscInt        joinSize, meetSize, *fverts, cells[2];
891     const PetscInt *join, *meet;
892     PetscInt        foffset = 0;
893 
894     PetscCall(PetscMalloc1(maxFaceSize, &fverts));
895     /* Mark facets by finding the full join of all adjacent vertices */
896     for (f = 0; f < numFaces; f++) {
897       const PetscInt cl = faceAdjCell[f * 2 + 0] - 1;
898       const PetscInt cr = faceAdjCell[f * 2 + 1] - 1;
899       const PetscInt id = (PetscInt)faceZoneIDs[f];
900 
901       if (cl > 0 && cr > 0) {
902         /* If we know both adjoining cells we can use a single-level meet */
903         cells[0] = cl;
904         cells[1] = cr;
905         PetscCall(DMPlexGetMeet(*dm, 2, cells, &meetSize, &meet));
906         PetscCheck(meetSize == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Could not determine Plex facet for Fluent face %" PetscInt_FMT " cells: %" PetscInt_FMT ", %" PetscInt_FMT, f, cl, cr);
907         PetscCall(DMSetLabelValue_Fast(*dm, &faceSets, "Face Sets", meet[0], id));
908         if (zoneNames && zoneNames[id]) PetscCall(DMSetLabelValue_Fast(*dm, &zoneLabels[id], zoneNames[id], meet[0], 1));
909         PetscCall(DMPlexRestoreMeet(*dm, meetSize, fverts, &meetSize, &meet));
910       } else {
911         for (PetscInt fi = 0; fi < faceSizes[f]; fi++) fverts[fi] = faces[foffset + fi] + numCells - 1;
912         PetscCall(DMPlexGetFullJoin(*dm, faceSizes[f], fverts, &joinSize, &join));
913         PetscCheck(joinSize == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Could not determine Plex facet for Fluent face %" PetscInt_FMT, f);
914         PetscCall(DMSetLabelValue_Fast(*dm, &faceSets, "Face Sets", join[0], id));
915         if (zoneNames && zoneNames[id]) PetscCall(DMSetLabelValue_Fast(*dm, &zoneLabels[id], zoneNames[id], join[0], 1));
916         PetscCall(DMPlexRestoreJoin(*dm, joinSize, fverts, &joinSize, &join));
917       }
918       foffset += faceSizes[f];
919     }
920     PetscCall(PetscFree(fverts));
921   }
922 
923   { /* Create Face Sets label at all processes */
924     enum {
925       n = 1
926     };
927     PetscBool flag[n];
928 
929     flag[0] = faceSets ? PETSC_TRUE : PETSC_FALSE;
930     PetscCallMPI(MPI_Bcast(flag, n, MPIU_BOOL, 0, comm));
931     if (flag[0]) PetscCall(DMCreateLabel(*dm, "Face Sets"));
932     // TODO Code to create all the zone labels on each process
933   }
934 
935   if (!interpolate) {
936     DM udm;
937 
938     PetscCall(DMPlexUninterpolate(*dm, &udm));
939     PetscCall(DMDestroy(dm));
940     *dm = udm;
941   }
942 
943   /* Read coordinates */
944   PetscCall(DMGetCoordinateSection(*dm, &coordSection));
945   PetscCall(PetscSectionSetNumFields(coordSection, 1));
946   PetscCall(PetscSectionSetFieldComponents(coordSection, 0, dim));
947   PetscCall(PetscSectionSetChart(coordSection, numCells, numCells + numVertices));
948   for (PetscInt v = numCells; v < numCells + numVertices; ++v) {
949     PetscCall(PetscSectionSetDof(coordSection, v, dim));
950     PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, dim));
951   }
952   PetscCall(PetscSectionSetUp(coordSection));
953   PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
954   PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates));
955   PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
956   PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
957   PetscCall(VecSetType(coordinates, VECSTANDARD));
958   PetscCall(VecGetArray(coordinates, &coords));
959   if (rank == 0 && coordsIn) {
960     for (PetscInt v = 0; v < numVertices; ++v) {
961       for (PetscInt d = 0; d < dim; ++d) coords[v * dim + d] = coordsIn[v * dim + d];
962     }
963   }
964   PetscCall(VecRestoreArray(coordinates, &coords));
965   PetscCall(DMSetCoordinatesLocal(*dm, coordinates));
966   PetscCall(VecDestroy(&coordinates));
967 
968   if (rank == 0) {
969     PetscCall(PetscFree(cellVertices));
970     PetscCall(PetscFree2(cellSizes, cellTypes));
971     PetscCall(PetscFree(faces));
972     PetscCall(PetscFree3(faceSizes, faceAdjCell, faceZoneIDs));
973     PetscCall(PetscFree(coordsIn));
974     if (zoneNames)
975       for (PetscInt i = 0; i < (PetscInt)maxZoneID; ++i) PetscCall(PetscFree(zoneNames[i]));
976     PetscCall(PetscFree2(zoneNames, zoneLabels));
977   }
978   PetscFunctionReturn(PETSC_SUCCESS);
979 }
980