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 : (int)((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