xref: /petsc/src/dm/impls/plex/plexexodusii.c (revision 98d129c30f3ee9fdddc40fdbc5a989b7be64f888)
1 #define PETSCDM_DLL
2 #include <petsc/private/dmpleximpl.h> /*I   "petscdmplex.h"   I*/
3 
4 #if defined(PETSC_HAVE_EXODUSII)
5   #include <netcdf.h>
6   #include <exodusII.h>
7 #endif
8 
9 #include <petsc/private/viewerimpl.h>
10 #include <petsc/private/viewerexodusiiimpl.h>
11 #if defined(PETSC_HAVE_EXODUSII)
12 /*@C
13   PETSC_VIEWER_EXODUSII_ - Creates an `PETSCVIEWEREXODUSII` `PetscViewer` shared by all processors in a communicator.
14 
15   Collective; No Fortran Support
16 
17   Input Parameter:
18 . comm - the MPI communicator to share the `PETSCVIEWEREXODUSII` `PetscViewer`
19 
20   Level: intermediate
21 
22   Note:
23   Unlike almost all other PETSc routines, `PETSC_VIEWER_EXODUSII_()` does not return
24   an error code.  The GLVIS PetscViewer is usually used in the form
25 $       XXXView(XXX object, PETSC_VIEWER_EXODUSII_(comm));
26 
27 .seealso: `PETSCVIEWEREXODUSII`, `PetscViewer`, `PetscViewer`, `PetscViewerExodusIIOpen()`, `PetscViewerType`, `PetscViewerCreate()`, `PetscViewerDestroy()`
28 @*/
29 PetscViewer PETSC_VIEWER_EXODUSII_(MPI_Comm comm)
30 {
31   PetscViewer    viewer;
32   PetscErrorCode ierr;
33 
34   PetscFunctionBegin;
35   ierr = PetscViewerExodusIIOpen(comm, "mesh.exo", FILE_MODE_WRITE, &viewer);
36   if (ierr) {
37     ierr = PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_EXODUSII_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
38     PetscFunctionReturn(NULL);
39   }
40   ierr = PetscObjectRegisterDestroy((PetscObject)viewer);
41   if (ierr) {
42     ierr = PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_EXODUSII_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
43     PetscFunctionReturn(NULL);
44   }
45   PetscFunctionReturn(viewer);
46 }
47 
48 static PetscErrorCode PetscViewerView_ExodusII(PetscViewer v, PetscViewer viewer)
49 {
50   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)v->data;
51 
52   PetscFunctionBegin;
53   if (exo->filename) PetscCall(PetscViewerASCIIPrintf(viewer, "Filename:    %s\n", exo->filename));
54   if (exo->exoid) PetscCall(PetscViewerASCIIPrintf(viewer, "exoid:       %d\n", exo->exoid));
55   if (exo->btype) PetscCall(PetscViewerASCIIPrintf(viewer, "IO Mode:     %d\n", exo->btype));
56   if (exo->order) PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh order:  %" PetscInt_FMT "\n", exo->order));
57   PetscFunctionReturn(PETSC_SUCCESS);
58 }
59 
60 static PetscErrorCode PetscViewerSetFromOptions_ExodusII(PetscViewer v, PetscOptionItems *PetscOptionsObject)
61 {
62   PetscFunctionBegin;
63   PetscOptionsHeadBegin(PetscOptionsObject, "ExodusII PetscViewer Options");
64   PetscOptionsHeadEnd();
65   PetscFunctionReturn(PETSC_SUCCESS);
66 }
67 
68 static PetscErrorCode PetscViewerSetUp_ExodusII(PetscViewer viewer)
69 {
70   PetscFunctionBegin;
71   PetscFunctionReturn(PETSC_SUCCESS);
72 }
73 
74 static PetscErrorCode PetscViewerDestroy_ExodusII(PetscViewer viewer)
75 {
76   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;
77 
78   PetscFunctionBegin;
79   if (exo->exoid >= 0) PetscCallExternal(ex_close, exo->exoid);
80   PetscCall(PetscFree(exo->filename));
81   PetscCall(PetscFree(exo));
82   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileSetName_C", NULL));
83   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileGetName_C", NULL));
84   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileSetMode_C", NULL));
85   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileGetMode_C", NULL));
86   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGetId_C", NULL));
87   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGetOrder_C", NULL));
88   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerSetOrder_C", NULL));
89   PetscFunctionReturn(PETSC_SUCCESS);
90 }
91 
92 static PetscErrorCode PetscViewerFileSetName_ExodusII(PetscViewer viewer, const char name[])
93 {
94   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;
95   PetscMPIInt           rank;
96   int                   CPU_word_size, IO_word_size, EXO_mode;
97   MPI_Info              mpi_info = MPI_INFO_NULL;
98   float                 EXO_version;
99 
100   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
101   CPU_word_size = sizeof(PetscReal);
102   IO_word_size  = sizeof(PetscReal);
103 
104   PetscFunctionBegin;
105   if (exo->exoid >= 0) {
106     PetscCallExternal(ex_close, exo->exoid);
107     exo->exoid = -1;
108   }
109   if (exo->filename) PetscCall(PetscFree(exo->filename));
110   PetscCall(PetscStrallocpy(name, &exo->filename));
111   switch (exo->btype) {
112   case FILE_MODE_READ:
113     EXO_mode = EX_READ;
114     break;
115   case FILE_MODE_APPEND:
116   case FILE_MODE_UPDATE:
117   case FILE_MODE_APPEND_UPDATE:
118     /* Will fail if the file does not already exist */
119     EXO_mode = EX_WRITE;
120     break;
121   case FILE_MODE_WRITE:
122     /*
123       exodus only allows writing geometry upon file creation, so we will let DMView create the file.
124     */
125     PetscFunctionReturn(PETSC_SUCCESS);
126     break;
127   default:
128     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ORDER, "Must call PetscViewerFileSetMode() before PetscViewerFileSetName()");
129   }
130   #if defined(PETSC_USE_64BIT_INDICES)
131   EXO_mode += EX_ALL_INT64_API;
132   #endif
133   exo->exoid = ex_open_par(name, EXO_mode, &CPU_word_size, &IO_word_size, &EXO_version, PETSC_COMM_WORLD, mpi_info);
134   PetscCheck(exo->exoid >= 0, PETSC_COMM_SELF, PETSC_ERR_LIB, "ex_open_par failed for %s", name);
135   PetscFunctionReturn(PETSC_SUCCESS);
136 }
137 
138 static PetscErrorCode PetscViewerFileGetName_ExodusII(PetscViewer viewer, const char **name)
139 {
140   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;
141 
142   PetscFunctionBegin;
143   *name = exo->filename;
144   PetscFunctionReturn(PETSC_SUCCESS);
145 }
146 
147 static PetscErrorCode PetscViewerFileSetMode_ExodusII(PetscViewer viewer, PetscFileMode type)
148 {
149   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;
150 
151   PetscFunctionBegin;
152   exo->btype = type;
153   PetscFunctionReturn(PETSC_SUCCESS);
154 }
155 
156 static PetscErrorCode PetscViewerFileGetMode_ExodusII(PetscViewer viewer, PetscFileMode *type)
157 {
158   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;
159 
160   PetscFunctionBegin;
161   *type = exo->btype;
162   PetscFunctionReturn(PETSC_SUCCESS);
163 }
164 
165 static PetscErrorCode PetscViewerExodusIIGetId_ExodusII(PetscViewer viewer, int *exoid)
166 {
167   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;
168 
169   PetscFunctionBegin;
170   *exoid = exo->exoid;
171   PetscFunctionReturn(PETSC_SUCCESS);
172 }
173 
174 static PetscErrorCode PetscViewerExodusIIGetOrder_ExodusII(PetscViewer viewer, PetscInt *order)
175 {
176   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;
177 
178   PetscFunctionBegin;
179   *order = exo->order;
180   PetscFunctionReturn(PETSC_SUCCESS);
181 }
182 
183 static PetscErrorCode PetscViewerExodusIISetOrder_ExodusII(PetscViewer viewer, PetscInt order)
184 {
185   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;
186 
187   PetscFunctionBegin;
188   exo->order = order;
189   PetscFunctionReturn(PETSC_SUCCESS);
190 }
191 
192 /*MC
193    PETSCVIEWEREXODUSII - A viewer that writes to an Exodus II file
194 
195   Level: beginner
196 
197 .seealso: `PetscViewerExodusIIOpen()`, `PetscViewerCreate()`, `PETSCVIEWERBINARY`, `PETSCVIEWERHDF5`, `DMView()`,
198           `PetscViewerFileSetName()`, `PetscViewerFileSetMode()`, `PetscViewerFormat`, `PetscViewerType`, `PetscViewerSetType()`
199 M*/
200 
201 PETSC_EXTERN PetscErrorCode PetscViewerCreate_ExodusII(PetscViewer v)
202 {
203   PetscViewer_ExodusII *exo;
204 
205   PetscFunctionBegin;
206   PetscCall(PetscNew(&exo));
207 
208   v->data                = (void *)exo;
209   v->ops->destroy        = PetscViewerDestroy_ExodusII;
210   v->ops->setfromoptions = PetscViewerSetFromOptions_ExodusII;
211   v->ops->setup          = PetscViewerSetUp_ExodusII;
212   v->ops->view           = PetscViewerView_ExodusII;
213   v->ops->flush          = 0;
214   exo->btype             = (PetscFileMode)-1;
215   exo->filename          = 0;
216   exo->exoid             = -1;
217 
218   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerFileSetName_C", PetscViewerFileSetName_ExodusII));
219   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerFileGetName_C", PetscViewerFileGetName_ExodusII));
220   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerFileSetMode_C", PetscViewerFileSetMode_ExodusII));
221   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerFileGetMode_C", PetscViewerFileGetMode_ExodusII));
222   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerGetId_C", PetscViewerExodusIIGetId_ExodusII));
223   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerSetOrder_C", PetscViewerExodusIISetOrder_ExodusII));
224   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerGetOrder_C", PetscViewerExodusIIGetOrder_ExodusII));
225   PetscFunctionReturn(PETSC_SUCCESS);
226 }
227 
228 /*
229   EXOGetVarIndex - Locate a result in an exodus file based on its name
230 
231   Collective
232 
233   Input Parameters:
234 + exoid    - the exodus id of a file (obtained from ex_open or ex_create for instance)
235 . obj_type - the type of entity for instance EX_NODAL, EX_ELEM_BLOCK
236 - name     - the name of the result
237 
238   Output Parameter:
239 . varIndex - the location in the exodus file of the result
240 
241   Level: beginner
242 
243   Notes:
244   The exodus variable index is obtained by comparing name and the
245   names of zonal variables declared in the exodus file. For instance if name is "V"
246   the location in the exodus file will be the first match of "V", "V_X", "V_XX", "V_1", or "V_11"
247   amongst all variables of type obj_type.
248 
249 .seealso: `EXOGetVarIndex()`, `DMPlexView_ExodusII_Internal()`, `VecViewPlex_ExodusII_Nodal_Internal()`, `VecLoadNodal_PlexEXO()`, `VecLoadZonal_PlexEXO()`
250 */
251 static PetscErrorCode EXOGetVarIndex_Internal(int exoid, ex_entity_type obj_type, const char name[], int *varIndex)
252 {
253   int       num_vars, i, j;
254   char      ext_name[MAX_STR_LENGTH + 1], var_name[MAX_STR_LENGTH + 1];
255   const int num_suffix = 5;
256   char     *suffix[5];
257   PetscBool flg;
258 
259   PetscFunctionBegin;
260   suffix[0] = (char *)"";
261   suffix[1] = (char *)"_X";
262   suffix[2] = (char *)"_XX";
263   suffix[3] = (char *)"_1";
264   suffix[4] = (char *)"_11";
265 
266   *varIndex = -1;
267   PetscCallExternal(ex_get_variable_param, exoid, obj_type, &num_vars);
268   for (i = 0; i < num_vars; ++i) {
269     PetscCallExternal(ex_get_variable_name, exoid, obj_type, i + 1, var_name);
270     for (j = 0; j < num_suffix; ++j) {
271       PetscCall(PetscStrncpy(ext_name, name, MAX_STR_LENGTH));
272       PetscCall(PetscStrlcat(ext_name, suffix[j], MAX_STR_LENGTH));
273       PetscCall(PetscStrcasecmp(ext_name, var_name, &flg));
274       if (flg) *varIndex = i + 1;
275     }
276   }
277   PetscFunctionReturn(PETSC_SUCCESS);
278 }
279 
280 /*
281   DMView_PlexExodusII - Write a `DM` to disk in exodus format
282 
283   Collective
284 
285   Input Parameters:
286 + dm     - The dm to be written
287 - viewer - an exodusII viewer
288 
289   Level: beginner
290 
291   Notes:
292   Not all DM can be written to disk this way. For instance, exodus assume that element blocks (mapped to "Cell sets" labels)
293   consists of sequentially numbered cells. If this is not the case, the exodus file will be corrupted.
294 
295   If `dm` has been distributed, only the part of the `DM` on MPI rank 0 (including "ghost" cells and vertices)
296   will be written.
297 
298   `DMPLEX` only represents geometry while most post-processing software expect that a mesh also provides information
299   on the discretization space. This function assumes that the file represents Lagrange finite elements of order 1 or 2.
300   The order of the mesh shall be set using `PetscViewerExodusIISetOrder()`
301   It should be extended to use `PetscFE` objects.
302 
303   This function will only handle TRI, TET, QUAD, and HEX cells.
304 
305 .seealso: `DMPLEX`
306 */
307 PetscErrorCode DMView_PlexExodusII(DM dm, PetscViewer viewer)
308 {
309   enum ElemType {
310     SEGMENT,
311     TRI,
312     QUAD,
313     TET,
314     HEX
315   };
316   MPI_Comm comm;
317   PetscInt degree; /* the order of the mesh */
318   /* Connectivity Variables */
319   PetscInt cellsNotInConnectivity;
320   /* Cell Sets */
321   DMLabel         csLabel;
322   IS              csIS;
323   const PetscInt *csIdx;
324   PetscInt        num_cs, cs;
325   enum ElemType  *type;
326   PetscBool       hasLabel;
327   /* Coordinate Variables */
328   DM           cdm;
329   PetscSection coordSection;
330   Vec          coord;
331   PetscInt   **nodes;
332   PetscInt     depth, d, dim, skipCells = 0;
333   PetscInt     pStart, pEnd, p, cStart, cEnd, numCells, vStart, vEnd, numVertices, eStart, eEnd, numEdges, fStart, fEnd, numFaces, numNodes;
334   PetscInt     num_vs, num_fs;
335   PetscMPIInt  rank, size;
336   const char  *dmName;
337   PetscInt     nodesLineP1[4] = {2, 0, 0, 0};
338   PetscInt     nodesLineP2[4] = {2, 0, 0, 1};
339   PetscInt     nodesTriP1[4]  = {3, 0, 0, 0};
340   PetscInt     nodesTriP2[4]  = {3, 3, 0, 0};
341   PetscInt     nodesQuadP1[4] = {4, 0, 0, 0};
342   PetscInt     nodesQuadP2[4] = {4, 4, 0, 1};
343   PetscInt     nodesTetP1[4]  = {4, 0, 0, 0};
344   PetscInt     nodesTetP2[4]  = {4, 6, 0, 0};
345   PetscInt     nodesHexP1[4]  = {8, 0, 0, 0};
346   PetscInt     nodesHexP2[4]  = {8, 12, 6, 1};
347   int          CPU_word_size, IO_word_size, EXO_mode;
348   float        EXO_version;
349 
350   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;
351 
352   PetscFunctionBegin;
353   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
354   PetscCallMPI(MPI_Comm_rank(comm, &rank));
355   PetscCallMPI(MPI_Comm_size(comm, &size));
356 
357   /*
358     Creating coordSection is a collective operation so we do it somewhat out of sequence
359   */
360   PetscCall(PetscSectionCreate(comm, &coordSection));
361   PetscCall(DMGetCoordinatesLocalSetUp(dm));
362   /*
363     Check that all points are on rank 0 since we don't know how to save distributed DM in exodus format
364   */
365   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
366   PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
367   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
368   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
369   numCells    = cEnd - cStart;
370   numEdges    = eEnd - eStart;
371   numVertices = vEnd - vStart;
372   PetscCheck(!(rank && (numCells || numEdges || numVertices)), PETSC_COMM_SELF, PETSC_ERR_SUP, "Writing distributed DM in exodusII format not supported");
373   if (rank == 0) {
374     switch (exo->btype) {
375     case FILE_MODE_READ:
376     case FILE_MODE_APPEND:
377     case FILE_MODE_UPDATE:
378     case FILE_MODE_APPEND_UPDATE:
379       /* exodusII does not allow writing geometry to an existing file */
380       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_LIB, "cannot add geometry to existing file %s", exo->filename);
381     case FILE_MODE_WRITE:
382       /* Create an empty file if one already exists*/
383       EXO_mode = EX_CLOBBER;
384   #if defined(PETSC_USE_64BIT_INDICES)
385       EXO_mode += EX_ALL_INT64_API;
386   #endif
387       CPU_word_size = sizeof(PetscReal);
388       IO_word_size  = sizeof(PetscReal);
389       exo->exoid    = ex_create(exo->filename, EXO_mode, &CPU_word_size, &IO_word_size);
390       PetscCheck(exo->exoid >= 0, PETSC_COMM_SELF, PETSC_ERR_LIB, "ex_create failed for %s", exo->filename);
391 
392       break;
393     default:
394       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ORDER, "Must call PetscViewerFileSetMode() before PetscViewerFileSetName()");
395     }
396 
397     /* --- Get DM info --- */
398     PetscCall(PetscObjectGetName((PetscObject)dm, &dmName));
399     PetscCall(DMPlexGetDepth(dm, &depth));
400     PetscCall(DMGetDimension(dm, &dim));
401     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
402     if (depth == 3) {
403       numFaces = fEnd - fStart;
404     } else {
405       numFaces = 0;
406     }
407     PetscCall(DMGetLabelSize(dm, "Cell Sets", &num_cs));
408     PetscCall(DMGetLabelSize(dm, "Vertex Sets", &num_vs));
409     PetscCall(DMGetLabelSize(dm, "Face Sets", &num_fs));
410     PetscCall(DMGetCoordinatesLocal(dm, &coord));
411     PetscCall(DMGetCoordinateDM(dm, &cdm));
412     if (num_cs > 0) {
413       PetscCall(DMGetLabel(dm, "Cell Sets", &csLabel));
414       PetscCall(DMLabelGetValueIS(csLabel, &csIS));
415       PetscCall(ISGetIndices(csIS, &csIdx));
416     }
417     PetscCall(PetscMalloc1(num_cs, &nodes));
418     /* Set element type for each block and compute total number of nodes */
419     PetscCall(PetscMalloc1(num_cs, &type));
420     numNodes = numVertices;
421 
422     PetscCall(PetscViewerExodusIIGetOrder(viewer, &degree));
423     if (degree == 2) numNodes += numEdges;
424     cellsNotInConnectivity = numCells;
425     for (cs = 0; cs < num_cs; ++cs) {
426       IS              stratumIS;
427       const PetscInt *cells;
428       PetscScalar    *xyz = NULL;
429       PetscInt        csSize, closureSize;
430 
431       PetscCall(DMLabelGetStratumIS(csLabel, csIdx[cs], &stratumIS));
432       PetscCall(ISGetIndices(stratumIS, &cells));
433       PetscCall(ISGetSize(stratumIS, &csSize));
434       PetscCall(DMPlexVecGetClosure(cdm, NULL, coord, cells[0], &closureSize, &xyz));
435       switch (dim) {
436       case 1:
437         if (closureSize == 2 * dim) {
438           type[cs] = SEGMENT;
439         } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Number of vertices %" PetscInt_FMT " in dimension %" PetscInt_FMT " has no ExodusII type", closureSize / dim, dim);
440       case 2:
441         if (closureSize == 3 * dim) {
442           type[cs] = TRI;
443         } else if (closureSize == 4 * dim) {
444           type[cs] = QUAD;
445         } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Number of vertices %" PetscInt_FMT " in dimension %" PetscInt_FMT " has no ExodusII type", closureSize / dim, dim);
446         break;
447       case 3:
448         if (closureSize == 4 * dim) {
449           type[cs] = TET;
450         } else if (closureSize == 8 * dim) {
451           type[cs] = HEX;
452         } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Number of vertices %" PetscInt_FMT " in dimension %" PetscInt_FMT " has no ExodusII type", closureSize / dim, dim);
453         break;
454       default:
455         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Dimension %" PetscInt_FMT " not handled by ExodusII viewer", dim);
456       }
457       if ((degree == 2) && (type[cs] == SEGMENT)) numNodes += csSize;
458       if ((degree == 2) && (type[cs] == QUAD)) numNodes += csSize;
459       if ((degree == 2) && (type[cs] == HEX)) {
460         numNodes += csSize;
461         numNodes += numFaces;
462       }
463       PetscCall(DMPlexVecRestoreClosure(cdm, NULL, coord, cells[0], &closureSize, &xyz));
464       /* Set nodes and Element type */
465       if (type[cs] == SEGMENT) {
466         if (degree == 1) nodes[cs] = nodesLineP1;
467         else if (degree == 2) nodes[cs] = nodesLineP2;
468       } else if (type[cs] == TRI) {
469         if (degree == 1) nodes[cs] = nodesTriP1;
470         else if (degree == 2) nodes[cs] = nodesTriP2;
471       } else if (type[cs] == QUAD) {
472         if (degree == 1) nodes[cs] = nodesQuadP1;
473         else if (degree == 2) nodes[cs] = nodesQuadP2;
474       } else if (type[cs] == TET) {
475         if (degree == 1) nodes[cs] = nodesTetP1;
476         else if (degree == 2) nodes[cs] = nodesTetP2;
477       } else if (type[cs] == HEX) {
478         if (degree == 1) nodes[cs] = nodesHexP1;
479         else if (degree == 2) nodes[cs] = nodesHexP2;
480       }
481       /* Compute the number of cells not in the connectivity table */
482       cellsNotInConnectivity -= nodes[cs][3] * csSize;
483 
484       PetscCall(ISRestoreIndices(stratumIS, &cells));
485       PetscCall(ISDestroy(&stratumIS));
486     }
487     if (num_cs) PetscCallExternal(ex_put_init, exo->exoid, dmName, dim, numNodes, numCells, num_cs, num_vs, num_fs);
488     /* --- Connectivity --- */
489     for (cs = 0; cs < num_cs; ++cs) {
490       IS              stratumIS;
491       const PetscInt *cells;
492       PetscInt       *connect, off = 0;
493       PetscInt        edgesInClosure = 0, facesInClosure = 0, verticesInClosure = 0;
494       PetscInt        csSize, c, connectSize, closureSize;
495       char           *elem_type        = NULL;
496       char            elem_type_bar2[] = "BAR2", elem_type_bar3[] = "BAR3";
497       char            elem_type_tri3[] = "TRI3", elem_type_quad4[] = "QUAD4";
498       char            elem_type_tri6[] = "TRI6", elem_type_quad9[] = "QUAD9";
499       char            elem_type_tet4[] = "TET4", elem_type_hex8[] = "HEX8";
500       char            elem_type_tet10[] = "TET10", elem_type_hex27[] = "HEX27";
501 
502       PetscCall(DMLabelGetStratumIS(csLabel, csIdx[cs], &stratumIS));
503       PetscCall(ISGetIndices(stratumIS, &cells));
504       PetscCall(ISGetSize(stratumIS, &csSize));
505       /* Set Element type */
506       if (type[cs] == SEGMENT) {
507         if (degree == 1) elem_type = elem_type_bar2;
508         else if (degree == 2) elem_type = elem_type_bar3;
509       } else if (type[cs] == TRI) {
510         if (degree == 1) elem_type = elem_type_tri3;
511         else if (degree == 2) elem_type = elem_type_tri6;
512       } else if (type[cs] == QUAD) {
513         if (degree == 1) elem_type = elem_type_quad4;
514         else if (degree == 2) elem_type = elem_type_quad9;
515       } else if (type[cs] == TET) {
516         if (degree == 1) elem_type = elem_type_tet4;
517         else if (degree == 2) elem_type = elem_type_tet10;
518       } else if (type[cs] == HEX) {
519         if (degree == 1) elem_type = elem_type_hex8;
520         else if (degree == 2) elem_type = elem_type_hex27;
521       }
522       connectSize = nodes[cs][0] + nodes[cs][1] + nodes[cs][2] + nodes[cs][3];
523       PetscCall(PetscMalloc1(PetscMax(27, connectSize) * csSize, &connect));
524       PetscCallExternal(ex_put_block, exo->exoid, EX_ELEM_BLOCK, csIdx[cs], elem_type, csSize, connectSize, 0, 0, 1);
525       /* Find number of vertices, edges, and faces in the closure */
526       verticesInClosure = nodes[cs][0];
527       if (depth > 1) {
528         if (dim == 2) {
529           PetscCall(DMPlexGetConeSize(dm, cells[0], &edgesInClosure));
530         } else if (dim == 3) {
531           PetscInt *closure = NULL;
532 
533           PetscCall(DMPlexGetConeSize(dm, cells[0], &facesInClosure));
534           PetscCall(DMPlexGetTransitiveClosure(dm, cells[0], PETSC_TRUE, &closureSize, &closure));
535           edgesInClosure = closureSize - facesInClosure - 1 - verticesInClosure;
536           PetscCall(DMPlexRestoreTransitiveClosure(dm, cells[0], PETSC_TRUE, &closureSize, &closure));
537         }
538       }
539       /* Get connectivity for each cell */
540       for (c = 0; c < csSize; ++c) {
541         PetscInt *closure = NULL;
542         PetscInt  temp, i;
543 
544         PetscCall(DMPlexGetTransitiveClosure(dm, cells[c], PETSC_TRUE, &closureSize, &closure));
545         for (i = 0; i < connectSize; ++i) {
546           if (i < nodes[cs][0]) { /* Vertices */
547             connect[i + off] = closure[(i + edgesInClosure + facesInClosure + 1) * 2] + 1;
548             connect[i + off] -= cellsNotInConnectivity;
549           } else if (i < nodes[cs][0] + nodes[cs][1]) { /* Edges */
550             connect[i + off] = closure[(i - verticesInClosure + facesInClosure + 1) * 2] + 1;
551             if (nodes[cs][2] == 0) connect[i + off] -= numFaces;
552             connect[i + off] -= cellsNotInConnectivity;
553           } else if (i < nodes[cs][0] + nodes[cs][1] + nodes[cs][3]) { /* Cells */
554             connect[i + off] = closure[0] + 1;
555             connect[i + off] -= skipCells;
556           } else if (i < nodes[cs][0] + nodes[cs][1] + nodes[cs][3] + nodes[cs][2]) { /* Faces */
557             connect[i + off] = closure[(i - edgesInClosure - verticesInClosure) * 2] + 1;
558             connect[i + off] -= cellsNotInConnectivity;
559           } else {
560             connect[i + off] = -1;
561           }
562         }
563         /* Tetrahedra are inverted */
564         if (type[cs] == TET) {
565           temp             = connect[0 + off];
566           connect[0 + off] = connect[1 + off];
567           connect[1 + off] = temp;
568           if (degree == 2) {
569             temp             = connect[5 + off];
570             connect[5 + off] = connect[6 + off];
571             connect[6 + off] = temp;
572             temp             = connect[7 + off];
573             connect[7 + off] = connect[8 + off];
574             connect[8 + off] = temp;
575           }
576         }
577         /* Hexahedra are inverted */
578         if (type[cs] == HEX) {
579           temp             = connect[1 + off];
580           connect[1 + off] = connect[3 + off];
581           connect[3 + off] = temp;
582           if (degree == 2) {
583             temp              = connect[8 + off];
584             connect[8 + off]  = connect[11 + off];
585             connect[11 + off] = temp;
586             temp              = connect[9 + off];
587             connect[9 + off]  = connect[10 + off];
588             connect[10 + off] = temp;
589             temp              = connect[16 + off];
590             connect[16 + off] = connect[17 + off];
591             connect[17 + off] = temp;
592             temp              = connect[18 + off];
593             connect[18 + off] = connect[19 + off];
594             connect[19 + off] = temp;
595 
596             temp              = connect[12 + off];
597             connect[12 + off] = connect[16 + off];
598             connect[16 + off] = temp;
599             temp              = connect[13 + off];
600             connect[13 + off] = connect[17 + off];
601             connect[17 + off] = temp;
602             temp              = connect[14 + off];
603             connect[14 + off] = connect[18 + off];
604             connect[18 + off] = temp;
605             temp              = connect[15 + off];
606             connect[15 + off] = connect[19 + off];
607             connect[19 + off] = temp;
608 
609             temp              = connect[23 + off];
610             connect[23 + off] = connect[26 + off];
611             connect[26 + off] = temp;
612             temp              = connect[24 + off];
613             connect[24 + off] = connect[25 + off];
614             connect[25 + off] = temp;
615             temp              = connect[25 + off];
616             connect[25 + off] = connect[26 + off];
617             connect[26 + off] = temp;
618           }
619         }
620         off += connectSize;
621         PetscCall(DMPlexRestoreTransitiveClosure(dm, cells[c], PETSC_TRUE, &closureSize, &closure));
622       }
623       PetscCallExternal(ex_put_conn, exo->exoid, EX_ELEM_BLOCK, csIdx[cs], connect, 0, 0);
624       skipCells += (nodes[cs][3] == 0) * csSize;
625       PetscCall(PetscFree(connect));
626       PetscCall(ISRestoreIndices(stratumIS, &cells));
627       PetscCall(ISDestroy(&stratumIS));
628     }
629     PetscCall(PetscFree(type));
630     /* --- Coordinates --- */
631     PetscCall(PetscSectionSetChart(coordSection, pStart, pEnd));
632     if (num_cs) {
633       for (d = 0; d < depth; ++d) {
634         PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
635         for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(coordSection, p, nodes[0][d] > 0));
636       }
637     }
638     for (cs = 0; cs < num_cs; ++cs) {
639       IS              stratumIS;
640       const PetscInt *cells;
641       PetscInt        csSize, c;
642 
643       PetscCall(DMLabelGetStratumIS(csLabel, csIdx[cs], &stratumIS));
644       PetscCall(ISGetIndices(stratumIS, &cells));
645       PetscCall(ISGetSize(stratumIS, &csSize));
646       for (c = 0; c < csSize; ++c) PetscCall(PetscSectionSetDof(coordSection, cells[c], nodes[cs][3] > 0));
647       PetscCall(ISRestoreIndices(stratumIS, &cells));
648       PetscCall(ISDestroy(&stratumIS));
649     }
650     if (num_cs) {
651       PetscCall(ISRestoreIndices(csIS, &csIdx));
652       PetscCall(ISDestroy(&csIS));
653     }
654     PetscCall(PetscFree(nodes));
655     PetscCall(PetscSectionSetUp(coordSection));
656     if (numNodes) {
657       const char  *coordNames[3] = {"x", "y", "z"};
658       PetscScalar *closure, *cval;
659       PetscReal   *coords;
660       PetscInt     hasDof, n = 0;
661 
662       /* There can't be more than 24 values in the closure of a point for the coord coordSection */
663       PetscCall(PetscCalloc3(numNodes * 3, &coords, dim, &cval, 24, &closure));
664       PetscCall(DMGetCoordinatesLocalNoncollective(dm, &coord));
665       PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
666       for (p = pStart; p < pEnd; ++p) {
667         PetscCall(PetscSectionGetDof(coordSection, p, &hasDof));
668         if (hasDof) {
669           PetscInt closureSize = 24, j;
670 
671           PetscCall(DMPlexVecGetClosure(cdm, NULL, coord, p, &closureSize, &closure));
672           for (d = 0; d < dim; ++d) {
673             cval[d] = 0.0;
674             for (j = 0; j < closureSize / dim; j++) cval[d] += closure[j * dim + d];
675             coords[d * numNodes + n] = PetscRealPart(cval[d]) * dim / closureSize;
676           }
677           ++n;
678         }
679       }
680       PetscCallExternal(ex_put_coord, exo->exoid, &coords[0 * numNodes], &coords[1 * numNodes], &coords[2 * numNodes]);
681       PetscCall(PetscFree3(coords, cval, closure));
682       PetscCallExternal(ex_put_coord_names, exo->exoid, (char **)coordNames);
683     }
684 
685     /* --- Node Sets/Vertex Sets --- */
686     PetscCall(DMHasLabel(dm, "Vertex Sets", &hasLabel));
687     if (hasLabel) {
688       PetscInt        i, vs, vsSize;
689       const PetscInt *vsIdx, *vertices;
690       PetscInt       *nodeList;
691       IS              vsIS, stratumIS;
692       DMLabel         vsLabel;
693       PetscCall(DMGetLabel(dm, "Vertex Sets", &vsLabel));
694       PetscCall(DMLabelGetValueIS(vsLabel, &vsIS));
695       PetscCall(ISGetIndices(vsIS, &vsIdx));
696       for (vs = 0; vs < num_vs; ++vs) {
697         PetscCall(DMLabelGetStratumIS(vsLabel, vsIdx[vs], &stratumIS));
698         PetscCall(ISGetIndices(stratumIS, &vertices));
699         PetscCall(ISGetSize(stratumIS, &vsSize));
700         PetscCall(PetscMalloc1(vsSize, &nodeList));
701         for (i = 0; i < vsSize; ++i) nodeList[i] = vertices[i] - skipCells + 1;
702         PetscCallExternal(ex_put_set_param, exo->exoid, EX_NODE_SET, vsIdx[vs], vsSize, 0);
703         PetscCallExternal(ex_put_set, exo->exoid, EX_NODE_SET, vsIdx[vs], nodeList, NULL);
704         PetscCall(ISRestoreIndices(stratumIS, &vertices));
705         PetscCall(ISDestroy(&stratumIS));
706         PetscCall(PetscFree(nodeList));
707       }
708       PetscCall(ISRestoreIndices(vsIS, &vsIdx));
709       PetscCall(ISDestroy(&vsIS));
710     }
711     /* --- Side Sets/Face Sets --- */
712     PetscCall(DMHasLabel(dm, "Face Sets", &hasLabel));
713     if (hasLabel) {
714       PetscInt        i, j, fs, fsSize;
715       const PetscInt *fsIdx, *faces;
716       IS              fsIS, stratumIS;
717       DMLabel         fsLabel;
718       PetscInt        numPoints, *points;
719       PetscInt        elem_list_size = 0;
720       PetscInt       *elem_list, *elem_ind, *side_list;
721 
722       PetscCall(DMGetLabel(dm, "Face Sets", &fsLabel));
723       /* Compute size of Node List and Element List */
724       PetscCall(DMLabelGetValueIS(fsLabel, &fsIS));
725       PetscCall(ISGetIndices(fsIS, &fsIdx));
726       for (fs = 0; fs < num_fs; ++fs) {
727         PetscCall(DMLabelGetStratumIS(fsLabel, fsIdx[fs], &stratumIS));
728         PetscCall(ISGetSize(stratumIS, &fsSize));
729         elem_list_size += fsSize;
730         PetscCall(ISDestroy(&stratumIS));
731       }
732       if (num_fs) {
733         PetscCall(PetscMalloc3(num_fs, &elem_ind, elem_list_size, &elem_list, elem_list_size, &side_list));
734         elem_ind[0] = 0;
735         for (fs = 0; fs < num_fs; ++fs) {
736           PetscCall(DMLabelGetStratumIS(fsLabel, fsIdx[fs], &stratumIS));
737           PetscCall(ISGetIndices(stratumIS, &faces));
738           PetscCall(ISGetSize(stratumIS, &fsSize));
739           /* Set Parameters */
740           PetscCallExternal(ex_put_set_param, exo->exoid, EX_SIDE_SET, fsIdx[fs], fsSize, 0);
741           /* Indices */
742           if (fs < num_fs - 1) elem_ind[fs + 1] = elem_ind[fs] + fsSize;
743 
744           for (i = 0; i < fsSize; ++i) {
745             /* Element List */
746             points = NULL;
747             PetscCall(DMPlexGetTransitiveClosure(dm, faces[i], PETSC_FALSE, &numPoints, &points));
748             elem_list[elem_ind[fs] + i] = points[2] + 1;
749             PetscCall(DMPlexRestoreTransitiveClosure(dm, faces[i], PETSC_FALSE, &numPoints, &points));
750 
751             /* Side List */
752             points = NULL;
753             PetscCall(DMPlexGetTransitiveClosure(dm, elem_list[elem_ind[fs] + i] - 1, PETSC_TRUE, &numPoints, &points));
754             for (j = 1; j < numPoints; ++j) {
755               if (points[j * 2] == faces[i]) break;
756             }
757             /* Convert HEX sides */
758             if (numPoints == 27) {
759               if (j == 1) {
760                 j = 5;
761               } else if (j == 2) {
762                 j = 6;
763               } else if (j == 3) {
764                 j = 1;
765               } else if (j == 4) {
766                 j = 3;
767               } else if (j == 5) {
768                 j = 2;
769               } else if (j == 6) {
770                 j = 4;
771               }
772             }
773             /* Convert TET sides */
774             if (numPoints == 15) {
775               --j;
776               if (j == 0) j = 4;
777             }
778             side_list[elem_ind[fs] + i] = j;
779             PetscCall(DMPlexRestoreTransitiveClosure(dm, elem_list[elem_ind[fs] + i] - 1, PETSC_TRUE, &numPoints, &points));
780           }
781           PetscCall(ISRestoreIndices(stratumIS, &faces));
782           PetscCall(ISDestroy(&stratumIS));
783         }
784         PetscCall(ISRestoreIndices(fsIS, &fsIdx));
785         PetscCall(ISDestroy(&fsIS));
786 
787         /* Put side sets */
788         for (fs = 0; fs < num_fs; ++fs) PetscCallExternal(ex_put_set, exo->exoid, EX_SIDE_SET, fsIdx[fs], &elem_list[elem_ind[fs]], &side_list[elem_ind[fs]]);
789         PetscCall(PetscFree3(elem_ind, elem_list, side_list));
790       }
791     }
792     /*
793       close the exodus file
794     */
795     ex_close(exo->exoid);
796     exo->exoid = -1;
797   }
798   PetscCall(PetscSectionDestroy(&coordSection));
799 
800   /*
801     reopen the file in parallel
802   */
803   EXO_mode = EX_WRITE;
804   #if defined(PETSC_USE_64BIT_INDICES)
805   EXO_mode += EX_ALL_INT64_API;
806   #endif
807   CPU_word_size = sizeof(PetscReal);
808   IO_word_size  = sizeof(PetscReal);
809   exo->exoid    = ex_open_par(exo->filename, EXO_mode, &CPU_word_size, &IO_word_size, &EXO_version, comm, MPI_INFO_NULL);
810   PetscCheck(exo->exoid >= 0, PETSC_COMM_SELF, PETSC_ERR_LIB, "ex_open_par failed for %s", exo->filename);
811   PetscFunctionReturn(PETSC_SUCCESS);
812 }
813 
814 static PetscErrorCode VecViewPlex_ExodusII_Nodal_Internal(Vec v, int exoid, int step, int offset);
815 static PetscErrorCode VecViewPlex_ExodusII_Zonal_Internal(Vec v, int exoid, int step, int offset);
816 static PetscErrorCode VecLoadPlex_ExodusII_Nodal_Internal(Vec v, int exoid, int step, int offset);
817 static PetscErrorCode VecLoadPlex_ExodusII_Zonal_Internal(Vec v, int exoid, int step, int offset);
818 
819 /*
820   VecView_PlexExodusII_Internal - Write a Vec corresponding in an exodus file
821 
822   Collective
823 
824   Input Parameters:
825 + v  - The vector to be written
826 - viewer - The PetscViewerExodusII viewer associate to an exodus file
827 
828   Level: beginner
829 
830   Notes:
831   The exodus result variable index is obtained by comparing the Vec name and the
832   names of variables declared in the exodus file. For instance for a Vec named "V"
833   the location in the exodus file will be the first match of "V", "V_X", "V_XX", "V_1", or "V_11"
834   amongst all variables.
835   In the event where a nodal and zonal variable both match, the function will return an error instead of
836   possibly corrupting the file
837 
838 .seealso: `EXOGetVarIndex_Internal()`, `DMPlexView_ExodusII()`, `VecView_PlexExodusII()`
839 @*/
840 PetscErrorCode VecView_PlexExodusII_Internal(Vec v, PetscViewer viewer)
841 {
842   DM          dm;
843   MPI_Comm    comm;
844   PetscMPIInt rank;
845 
846   int         exoid, offsetN = 0, offsetZ = 0;
847   const char *vecname;
848   PetscInt    step;
849 
850   PetscFunctionBegin;
851   PetscCall(PetscObjectGetComm((PetscObject)v, &comm));
852   PetscCallMPI(MPI_Comm_rank(comm, &rank));
853   PetscCall(PetscViewerExodusIIGetId(viewer, &exoid));
854   PetscCall(VecGetDM(v, &dm));
855   PetscCall(PetscObjectGetName((PetscObject)v, &vecname));
856 
857   PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
858   PetscCall(EXOGetVarIndex_Internal(exoid, EX_NODAL, vecname, &offsetN));
859   PetscCall(EXOGetVarIndex_Internal(exoid, EX_ELEM_BLOCK, vecname, &offsetZ));
860   PetscCheck(offsetN > 0 || offsetZ > 0, comm, PETSC_ERR_FILE_UNEXPECTED, "Found both nodal and zonal variable %s in exodus file. ", vecname);
861   if (offsetN > 0) {
862     PetscCall(VecViewPlex_ExodusII_Nodal_Internal(v, exoid, (int)step + 1, offsetN));
863   } else if (offsetZ > 0) {
864     PetscCall(VecViewPlex_ExodusII_Zonal_Internal(v, exoid, (int)step + 1, offsetZ));
865   } else SETERRQ(comm, PETSC_ERR_FILE_UNEXPECTED, "Could not find nodal or zonal variable %s in exodus file. ", vecname);
866   PetscFunctionReturn(PETSC_SUCCESS);
867 }
868 
869 /*
870   VecLoad_PlexExodusII_Internal - Write a Vec corresponding in an exodus file
871 
872   Collective
873 
874   Input Parameters:
875 + v  - The vector to be written
876 - viewer - The PetscViewerExodusII viewer associate to an exodus file
877 
878   Level: beginner
879 
880   Notes:
881   The exodus result variable index is obtained by comparing the Vec name and the
882   names of variables declared in the exodus file. For instance for a Vec named "V"
883   the location in the exodus file will be the first match of "V", "V_X", "V_XX", "V_1", or "V_11"
884   amongst all variables.
885   In the event where a nodal and zonal variable both match, the function will return an error instead of
886   possibly corrupting the file
887 
888 .seealso: `EXOGetVarIndex_Internal()`, `DMPlexView_ExodusII()`, `VecView_PlexExodusII()`
889 @*/
890 PetscErrorCode VecLoad_PlexExodusII_Internal(Vec v, PetscViewer viewer)
891 {
892   DM          dm;
893   MPI_Comm    comm;
894   PetscMPIInt rank;
895 
896   int         exoid, offsetN = 0, offsetZ = 0;
897   const char *vecname;
898   PetscInt    step;
899 
900   PetscFunctionBegin;
901   PetscCall(PetscObjectGetComm((PetscObject)v, &comm));
902   PetscCallMPI(MPI_Comm_rank(comm, &rank));
903   PetscCall(PetscViewerExodusIIGetId(viewer, &exoid));
904   PetscCall(VecGetDM(v, &dm));
905   PetscCall(PetscObjectGetName((PetscObject)v, &vecname));
906 
907   PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
908   PetscCall(EXOGetVarIndex_Internal(exoid, EX_NODAL, vecname, &offsetN));
909   PetscCall(EXOGetVarIndex_Internal(exoid, EX_ELEM_BLOCK, vecname, &offsetZ));
910   PetscCheck(offsetN > 0 || offsetZ > 0, comm, PETSC_ERR_FILE_UNEXPECTED, "Found both nodal and zonal variable %s in exodus file. ", vecname);
911   if (offsetN > 0) PetscCall(VecLoadPlex_ExodusII_Nodal_Internal(v, exoid, (int)step + 1, offsetN));
912   else if (offsetZ > 0) PetscCall(VecLoadPlex_ExodusII_Zonal_Internal(v, exoid, (int)step + 1, offsetZ));
913   else SETERRQ(comm, PETSC_ERR_FILE_UNEXPECTED, "Could not find nodal or zonal variable %s in exodus file. ", vecname);
914   PetscFunctionReturn(PETSC_SUCCESS);
915 }
916 
917 /*
918   VecViewPlex_ExodusII_Nodal_Internal - Write a Vec corresponding to a nodal field to an exodus file
919 
920   Collective
921 
922   Input Parameters:
923 + v  - The vector to be written
924 . exoid - the exodus id of a file (obtained from ex_open or ex_create for instance)
925 . step - the time step to write at (exodus steps are numbered starting from 1)
926 - offset - the location of the variable in the file
927 
928   Level: beginner
929 
930   Notes:
931   The exodus result nodal variable index is obtained by comparing the Vec name and the
932   names of nodal variables declared in the exodus file. For instance for a Vec named "V"
933   the location in the exodus file will be the first match of "V", "V_X", "V_XX", "V_1", or "V_11"
934   amongst all nodal variables.
935 
936 .seealso: `EXOGetVarIndex_Internal()`, `DMPlexView_ExodusII_Internal()`, `VecLoadNodal_PlexEXO()`, `VecViewZonal_PlexEXO()`, `VecLoadZonal_PlexEXO()`
937 @*/
938 static PetscErrorCode VecViewPlex_ExodusII_Nodal_Internal(Vec v, int exoid, int step, int offset)
939 {
940   MPI_Comm           comm;
941   PetscMPIInt        size;
942   DM                 dm;
943   Vec                vNatural, vComp;
944   const PetscScalar *varray;
945   PetscInt           xs, xe, bs;
946   PetscBool          useNatural;
947 
948   PetscFunctionBegin;
949   PetscCall(PetscObjectGetComm((PetscObject)v, &comm));
950   PetscCallMPI(MPI_Comm_size(comm, &size));
951   PetscCall(VecGetDM(v, &dm));
952   PetscCall(DMGetUseNatural(dm, &useNatural));
953   useNatural = useNatural && size > 1 ? PETSC_TRUE : PETSC_FALSE;
954   if (useNatural) {
955     PetscCall(DMPlexCreateNaturalVector(dm, &vNatural));
956     PetscCall(DMPlexGlobalToNaturalBegin(dm, v, vNatural));
957     PetscCall(DMPlexGlobalToNaturalEnd(dm, v, vNatural));
958   } else {
959     vNatural = v;
960   }
961 
962   /* Write local chunk of the result in the exodus file
963      exodus stores each component of a vector-valued field as a separate variable.
964      We assume that they are stored sequentially */
965   PetscCall(VecGetOwnershipRange(vNatural, &xs, &xe));
966   PetscCall(VecGetBlockSize(vNatural, &bs));
967   if (bs == 1) {
968     PetscCall(VecGetArrayRead(vNatural, &varray));
969     PetscCallExternal(ex_put_partial_var, exoid, step, EX_NODAL, offset, 1, xs + 1, xe - xs, varray);
970     PetscCall(VecRestoreArrayRead(vNatural, &varray));
971   } else {
972     IS       compIS;
973     PetscInt c;
974 
975     PetscCall(ISCreateStride(comm, (xe - xs) / bs, xs, bs, &compIS));
976     for (c = 0; c < bs; ++c) {
977       PetscCall(ISStrideSetStride(compIS, (xe - xs) / bs, xs + c, bs));
978       PetscCall(VecGetSubVector(vNatural, compIS, &vComp));
979       PetscCall(VecGetArrayRead(vComp, &varray));
980       PetscCallExternal(ex_put_partial_var, exoid, step, EX_NODAL, offset + c, 1, xs / bs + 1, (xe - xs) / bs, varray);
981       PetscCall(VecRestoreArrayRead(vComp, &varray));
982       PetscCall(VecRestoreSubVector(vNatural, compIS, &vComp));
983     }
984     PetscCall(ISDestroy(&compIS));
985   }
986   if (useNatural) PetscCall(VecDestroy(&vNatural));
987   PetscFunctionReturn(PETSC_SUCCESS);
988 }
989 
990 /*
991   VecLoadPlex_ExodusII_Nodal_Internal - Read a Vec corresponding to a nodal field from an exodus file
992 
993   Collective
994 
995   Input Parameters:
996 + v  - The vector to be written
997 . exoid - the exodus id of a file (obtained from ex_open or ex_create for instance)
998 . step - the time step to read at (exodus steps are numbered starting from 1)
999 - offset - the location of the variable in the file
1000 
1001   Level: beginner
1002 
1003   Notes:
1004   The exodus result nodal variable index is obtained by comparing the Vec name and the
1005   names of nodal variables declared in the exodus file. For instance for a Vec named "V"
1006   the location in the exodus file will be the first match of "V", "V_X", "V_XX", "V_1", or "V_11"
1007   amongst all nodal variables.
1008 
1009 .seealso: `EXOGetVarIndex_Internal()`, `DMPlexView_ExodusII_Internal()`, `VecViewPlex_ExodusII_Nodal_Internal()`, `VecViewZonal_PlexEXO()`, `VecLoadZonal_PlexEXO()`
1010 */
1011 static PetscErrorCode VecLoadPlex_ExodusII_Nodal_Internal(Vec v, int exoid, int step, int offset)
1012 {
1013   MPI_Comm     comm;
1014   PetscMPIInt  size;
1015   DM           dm;
1016   Vec          vNatural, vComp;
1017   PetscScalar *varray;
1018   PetscInt     xs, xe, bs;
1019   PetscBool    useNatural;
1020 
1021   PetscFunctionBegin;
1022   PetscCall(PetscObjectGetComm((PetscObject)v, &comm));
1023   PetscCallMPI(MPI_Comm_size(comm, &size));
1024   PetscCall(VecGetDM(v, &dm));
1025   PetscCall(DMGetUseNatural(dm, &useNatural));
1026   useNatural = useNatural && size > 1 ? PETSC_TRUE : PETSC_FALSE;
1027   if (useNatural) PetscCall(DMPlexCreateNaturalVector(dm, &vNatural));
1028   else vNatural = v;
1029 
1030   /* Read local chunk from the file */
1031   PetscCall(VecGetOwnershipRange(vNatural, &xs, &xe));
1032   PetscCall(VecGetBlockSize(vNatural, &bs));
1033   if (bs == 1) {
1034     PetscCall(VecGetArray(vNatural, &varray));
1035     PetscCallExternal(ex_get_partial_var, exoid, step, EX_NODAL, offset, 1, xs + 1, xe - xs, varray);
1036     PetscCall(VecRestoreArray(vNatural, &varray));
1037   } else {
1038     IS       compIS;
1039     PetscInt c;
1040 
1041     PetscCall(ISCreateStride(comm, (xe - xs) / bs, xs, bs, &compIS));
1042     for (c = 0; c < bs; ++c) {
1043       PetscCall(ISStrideSetStride(compIS, (xe - xs) / bs, xs + c, bs));
1044       PetscCall(VecGetSubVector(vNatural, compIS, &vComp));
1045       PetscCall(VecGetArray(vComp, &varray));
1046       PetscCallExternal(ex_get_partial_var, exoid, step, EX_NODAL, offset + c, 1, xs / bs + 1, (xe - xs) / bs, varray);
1047       PetscCall(VecRestoreArray(vComp, &varray));
1048       PetscCall(VecRestoreSubVector(vNatural, compIS, &vComp));
1049     }
1050     PetscCall(ISDestroy(&compIS));
1051   }
1052   if (useNatural) {
1053     PetscCall(DMPlexNaturalToGlobalBegin(dm, vNatural, v));
1054     PetscCall(DMPlexNaturalToGlobalEnd(dm, vNatural, v));
1055     PetscCall(VecDestroy(&vNatural));
1056   }
1057   PetscFunctionReturn(PETSC_SUCCESS);
1058 }
1059 
1060 /*
1061   VecViewPlex_ExodusII_Zonal_Internal - Write a Vec corresponding to a zonal (cell based) field to an exodus file
1062 
1063   Collective
1064 
1065   Input Parameters:
1066 + v  - The vector to be written
1067 . exoid - the exodus id of a file (obtained from ex_open or ex_create for instance)
1068 . step - the time step to write at (exodus steps are numbered starting from 1)
1069 - offset - the location of the variable in the file
1070 
1071   Level: beginner
1072 
1073   Notes:
1074   The exodus result zonal variable index is obtained by comparing the Vec name and the
1075   names of zonal variables declared in the exodus file. For instance for a Vec named "V"
1076   the location in the exodus file will be the first match of "V", "V_X", "V_XX", "V_1", or "V_11"
1077   amongst all zonal variables.
1078 
1079 .seealso: `EXOGetVarIndex_Internal()`, `DMPlexView_ExodusII_Internal()`, `VecViewPlex_ExodusII_Nodal_Internal()`, `VecLoadPlex_ExodusII_Nodal_Internal()`, `VecLoadPlex_ExodusII_Zonal_Internal()`
1080 */
1081 static PetscErrorCode VecViewPlex_ExodusII_Zonal_Internal(Vec v, int exoid, int step, int offset)
1082 {
1083   MPI_Comm           comm;
1084   PetscMPIInt        size;
1085   DM                 dm;
1086   Vec                vNatural, vComp;
1087   const PetscScalar *varray;
1088   PetscInt           xs, xe, bs;
1089   PetscBool          useNatural;
1090   IS                 compIS;
1091   PetscInt          *csSize, *csID;
1092   PetscInt           numCS, set, csxs = 0;
1093 
1094   PetscFunctionBegin;
1095   PetscCall(PetscObjectGetComm((PetscObject)v, &comm));
1096   PetscCallMPI(MPI_Comm_size(comm, &size));
1097   PetscCall(VecGetDM(v, &dm));
1098   PetscCall(DMGetUseNatural(dm, &useNatural));
1099   useNatural = useNatural && size > 1 ? PETSC_TRUE : PETSC_FALSE;
1100   if (useNatural) {
1101     PetscCall(DMPlexCreateNaturalVector(dm, &vNatural));
1102     PetscCall(DMPlexGlobalToNaturalBegin(dm, v, vNatural));
1103     PetscCall(DMPlexGlobalToNaturalEnd(dm, v, vNatural));
1104   } else {
1105     vNatural = v;
1106   }
1107 
1108   /* Write local chunk of the result in the exodus file
1109      exodus stores each component of a vector-valued field as a separate variable.
1110      We assume that they are stored sequentially
1111      Zonal variables are accessed one element block at a time, so we loop through the cell sets,
1112      but once the vector has been reordered to natural size, we cannot use the label information
1113      to figure out what to save where. */
1114   numCS = ex_inquire_int(exoid, EX_INQ_ELEM_BLK);
1115   PetscCall(PetscMalloc2(numCS, &csID, numCS, &csSize));
1116   PetscCallExternal(ex_get_ids, exoid, EX_ELEM_BLOCK, csID);
1117   for (set = 0; set < numCS; ++set) {
1118     ex_block block;
1119 
1120     block.id   = csID[set];
1121     block.type = EX_ELEM_BLOCK;
1122     PetscCallExternal(ex_get_block_param, exoid, &block);
1123     csSize[set] = block.num_entry;
1124   }
1125   PetscCall(VecGetOwnershipRange(vNatural, &xs, &xe));
1126   PetscCall(VecGetBlockSize(vNatural, &bs));
1127   if (bs > 1) PetscCall(ISCreateStride(comm, (xe - xs) / bs, xs, bs, &compIS));
1128   for (set = 0; set < numCS; set++) {
1129     PetscInt csLocalSize, c;
1130 
1131     /* range of indices for set setID[set]: csxs:csxs + csSize[set]-1
1132        local slice of zonal values:         xs/bs,xm/bs-1
1133        intersection:                        max(xs/bs,csxs),min(xm/bs-1,csxs + csSize[set]-1) */
1134     csLocalSize = PetscMax(0, PetscMin(xe / bs, csxs + csSize[set]) - PetscMax(xs / bs, csxs));
1135     if (bs == 1) {
1136       PetscCall(VecGetArrayRead(vNatural, &varray));
1137       PetscCallExternal(ex_put_partial_var, exoid, step, EX_ELEM_BLOCK, offset, csID[set], PetscMax(xs - csxs, 0) + 1, csLocalSize, &varray[PetscMax(0, csxs - xs)]);
1138       PetscCall(VecRestoreArrayRead(vNatural, &varray));
1139     } else {
1140       for (c = 0; c < bs; ++c) {
1141         PetscCall(ISStrideSetStride(compIS, (xe - xs) / bs, xs + c, bs));
1142         PetscCall(VecGetSubVector(vNatural, compIS, &vComp));
1143         PetscCall(VecGetArrayRead(vComp, &varray));
1144         PetscCallExternal(ex_put_partial_var, exoid, step, EX_ELEM_BLOCK, offset + c, csID[set], PetscMax(xs / bs - csxs, 0) + 1, csLocalSize, &varray[PetscMax(0, csxs - xs / bs)]);
1145         PetscCall(VecRestoreArrayRead(vComp, &varray));
1146         PetscCall(VecRestoreSubVector(vNatural, compIS, &vComp));
1147       }
1148     }
1149     csxs += csSize[set];
1150   }
1151   PetscCall(PetscFree2(csID, csSize));
1152   if (bs > 1) PetscCall(ISDestroy(&compIS));
1153   if (useNatural) PetscCall(VecDestroy(&vNatural));
1154   PetscFunctionReturn(PETSC_SUCCESS);
1155 }
1156 
1157 /*
1158   VecLoadPlex_ExodusII_Zonal_Internal - Read a Vec corresponding to a zonal (cell based) field from an exodus file
1159 
1160   Collective
1161 
1162   Input Parameters:
1163 + v  - The vector to be written
1164 . exoid - the exodus id of a file (obtained from ex_open or ex_create for instance)
1165 . step - the time step to read at (exodus steps are numbered starting from 1)
1166 - offset - the location of the variable in the file
1167 
1168   Level: beginner
1169 
1170   Notes:
1171   The exodus result zonal variable index is obtained by comparing the Vec name and the
1172   names of zonal variables declared in the exodus file. For instance for a Vec named "V"
1173   the location in the exodus file will be the first match of "V", "V_X", "V_XX", "V_1", or "V_11"
1174   amongst all zonal variables.
1175 
1176 .seealso: `EXOGetVarIndex_Internal()`, `DMPlexView_ExodusII_Internal()`, `VecViewPlex_ExodusII_Nodal_Internal()`, `VecLoadPlex_ExodusII_Nodal_Internal()`, `VecLoadPlex_ExodusII_Zonal_Internal()`
1177 */
1178 static PetscErrorCode VecLoadPlex_ExodusII_Zonal_Internal(Vec v, int exoid, int step, int offset)
1179 {
1180   MPI_Comm     comm;
1181   PetscMPIInt  size;
1182   DM           dm;
1183   Vec          vNatural, vComp;
1184   PetscScalar *varray;
1185   PetscInt     xs, xe, bs;
1186   PetscBool    useNatural;
1187   IS           compIS;
1188   PetscInt    *csSize, *csID;
1189   PetscInt     numCS, set, csxs = 0;
1190 
1191   PetscFunctionBegin;
1192   PetscCall(PetscObjectGetComm((PetscObject)v, &comm));
1193   PetscCallMPI(MPI_Comm_size(comm, &size));
1194   PetscCall(VecGetDM(v, &dm));
1195   PetscCall(DMGetUseNatural(dm, &useNatural));
1196   useNatural = useNatural && size > 1 ? PETSC_TRUE : PETSC_FALSE;
1197   if (useNatural) PetscCall(DMPlexCreateNaturalVector(dm, &vNatural));
1198   else vNatural = v;
1199 
1200   /* Read local chunk of the result in the exodus file
1201      exodus stores each component of a vector-valued field as a separate variable.
1202      We assume that they are stored sequentially
1203      Zonal variables are accessed one element block at a time, so we loop through the cell sets,
1204      but once the vector has been reordered to natural size, we cannot use the label information
1205      to figure out what to save where. */
1206   numCS = ex_inquire_int(exoid, EX_INQ_ELEM_BLK);
1207   PetscCall(PetscMalloc2(numCS, &csID, numCS, &csSize));
1208   PetscCallExternal(ex_get_ids, exoid, EX_ELEM_BLOCK, csID);
1209   for (set = 0; set < numCS; ++set) {
1210     ex_block block;
1211 
1212     block.id   = csID[set];
1213     block.type = EX_ELEM_BLOCK;
1214     PetscCallExternal(ex_get_block_param, exoid, &block);
1215     csSize[set] = block.num_entry;
1216   }
1217   PetscCall(VecGetOwnershipRange(vNatural, &xs, &xe));
1218   PetscCall(VecGetBlockSize(vNatural, &bs));
1219   if (bs > 1) PetscCall(ISCreateStride(comm, (xe - xs) / bs, xs, bs, &compIS));
1220   for (set = 0; set < numCS; ++set) {
1221     PetscInt csLocalSize, c;
1222 
1223     /* range of indices for set setID[set]: csxs:csxs + csSize[set]-1
1224        local slice of zonal values:         xs/bs,xm/bs-1
1225        intersection:                        max(xs/bs,csxs),min(xm/bs-1,csxs + csSize[set]-1) */
1226     csLocalSize = PetscMax(0, PetscMin(xe / bs, csxs + csSize[set]) - PetscMax(xs / bs, csxs));
1227     if (bs == 1) {
1228       PetscCall(VecGetArray(vNatural, &varray));
1229       PetscCallExternal(ex_get_partial_var, exoid, step, EX_ELEM_BLOCK, offset, csID[set], PetscMax(xs - csxs, 0) + 1, csLocalSize, &varray[PetscMax(0, csxs - xs)]);
1230       PetscCall(VecRestoreArray(vNatural, &varray));
1231     } else {
1232       for (c = 0; c < bs; ++c) {
1233         PetscCall(ISStrideSetStride(compIS, (xe - xs) / bs, xs + c, bs));
1234         PetscCall(VecGetSubVector(vNatural, compIS, &vComp));
1235         PetscCall(VecGetArray(vComp, &varray));
1236         PetscCallExternal(ex_get_partial_var, exoid, step, EX_ELEM_BLOCK, offset + c, csID[set], PetscMax(xs / bs - csxs, 0) + 1, csLocalSize, &varray[PetscMax(0, csxs - xs / bs)]);
1237         PetscCall(VecRestoreArray(vComp, &varray));
1238         PetscCall(VecRestoreSubVector(vNatural, compIS, &vComp));
1239       }
1240     }
1241     csxs += csSize[set];
1242   }
1243   PetscCall(PetscFree2(csID, csSize));
1244   if (bs > 1) PetscCall(ISDestroy(&compIS));
1245   if (useNatural) {
1246     PetscCall(DMPlexNaturalToGlobalBegin(dm, vNatural, v));
1247     PetscCall(DMPlexNaturalToGlobalEnd(dm, vNatural, v));
1248     PetscCall(VecDestroy(&vNatural));
1249   }
1250   PetscFunctionReturn(PETSC_SUCCESS);
1251 }
1252 #endif
1253 
1254 /*@
1255   PetscViewerExodusIIGetId - Get the file id of the `PETSCVIEWEREXODUSII` file
1256 
1257   Logically Collective
1258 
1259   Input Parameter:
1260 . viewer - the `PetscViewer`
1261 
1262   Output Parameter:
1263 . exoid - The ExodusII file id
1264 
1265   Level: intermediate
1266 
1267 .seealso: `PETSCVIEWEREXODUSII`, `PetscViewer`, `PetscViewerFileSetMode()`, `PetscViewerCreate()`, `PetscViewerSetType()`, `PetscViewerBinaryOpen()`
1268 @*/
1269 PetscErrorCode PetscViewerExodusIIGetId(PetscViewer viewer, int *exoid)
1270 {
1271   PetscFunctionBegin;
1272   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);
1273   PetscTryMethod(viewer, "PetscViewerGetId_C", (PetscViewer, int *), (viewer, exoid));
1274   PetscFunctionReturn(PETSC_SUCCESS);
1275 }
1276 
1277 /*@
1278   PetscViewerExodusIISetOrder - Set the elements order in the exodusII file.
1279 
1280   Collective
1281 
1282   Input Parameters:
1283 + viewer - the `PETSCVIEWEREXODUSII` viewer
1284 - order  - elements order
1285 
1286   Output Parameter:
1287 
1288   Level: beginner
1289 
1290 .seealso: `PETSCVIEWEREXODUSII`, `PetscViewer`, `PetscViewerExodusIIGetId()`, `PetscViewerExodusIIGetOrder()`
1291 @*/
1292 PetscErrorCode PetscViewerExodusIISetOrder(PetscViewer viewer, PetscInt order)
1293 {
1294   PetscFunctionBegin;
1295   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);
1296   PetscTryMethod(viewer, "PetscViewerSetOrder_C", (PetscViewer, PetscInt), (viewer, order));
1297   PetscFunctionReturn(PETSC_SUCCESS);
1298 }
1299 
1300 /*@
1301   PetscViewerExodusIIGetOrder - Get the elements order in the exodusII file.
1302 
1303   Collective
1304 
1305   Input Parameters:
1306 + viewer - the `PETSCVIEWEREXODUSII` viewer
1307 - order  - elements order
1308 
1309   Output Parameter:
1310 
1311   Level: beginner
1312 
1313 .seealso: `PETSCVIEWEREXODUSII`, `PetscViewer`, `PetscViewerExodusIIGetId()`, `PetscViewerExodusIISetOrder()`
1314 @*/
1315 PetscErrorCode PetscViewerExodusIIGetOrder(PetscViewer viewer, PetscInt *order)
1316 {
1317   PetscFunctionBegin;
1318   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);
1319   PetscTryMethod(viewer, "PetscViewerGetOrder_C", (PetscViewer, PetscInt *), (viewer, order));
1320   PetscFunctionReturn(PETSC_SUCCESS);
1321 }
1322 
1323 /*@C
1324   PetscViewerExodusIIOpen - Opens a file for ExodusII input/output.
1325 
1326   Collective
1327 
1328   Input Parameters:
1329 + comm - MPI communicator
1330 . name - name of file
1331 - type - type of file
1332 .vb
1333     FILE_MODE_WRITE - create new file for binary output
1334     FILE_MODE_READ - open existing file for binary input
1335     FILE_MODE_APPEND - open existing file for binary output
1336 .ve
1337 
1338   Output Parameter:
1339 . exo - `PETSCVIEWEREXODUSII` `PetscViewer` for Exodus II input/output to use with the specified file
1340 
1341   Level: beginner
1342 
1343 .seealso: `PETSCVIEWEREXODUSII`, `PetscViewer`, `PetscViewerPushFormat()`, `PetscViewerDestroy()`,
1344           `DMLoad()`, `PetscFileMode`, `PetscViewerSetType()`, `PetscViewerFileSetMode()`, `PetscViewerFileSetName()`
1345 @*/
1346 PetscErrorCode PetscViewerExodusIIOpen(MPI_Comm comm, const char name[], PetscFileMode type, PetscViewer *exo)
1347 {
1348   PetscFunctionBegin;
1349   PetscCall(PetscViewerCreate(comm, exo));
1350   PetscCall(PetscViewerSetType(*exo, PETSCVIEWEREXODUSII));
1351   PetscCall(PetscViewerFileSetMode(*exo, type));
1352   PetscCall(PetscViewerFileSetName(*exo, name));
1353   PetscCall(PetscViewerSetFromOptions(*exo));
1354   PetscFunctionReturn(PETSC_SUCCESS);
1355 }
1356 
1357 /*@C
1358   DMPlexCreateExodusFromFile - Create a `DMPLEX` mesh from an ExodusII file.
1359 
1360   Collective
1361 
1362   Input Parameters:
1363 + comm        - The MPI communicator
1364 . filename    - The name of the ExodusII file
1365 - interpolate - Create faces and edges in the mesh
1366 
1367   Output Parameter:
1368 . dm - The `DM` object representing the mesh
1369 
1370   Level: beginner
1371 
1372 .seealso: [](ch_unstructured), `DM`, `PETSCVIEWEREXODUSII`, `DMPLEX`, `DMCreate()`, `DMPlexCreateExodus()`
1373 @*/
1374 PetscErrorCode DMPlexCreateExodusFromFile(MPI_Comm comm, const char filename[], PetscBool interpolate, DM *dm)
1375 {
1376   PetscMPIInt rank;
1377 #if defined(PETSC_HAVE_EXODUSII)
1378   int   CPU_word_size = sizeof(PetscReal), IO_word_size = 0, exoid = -1;
1379   float version;
1380 #endif
1381 
1382   PetscFunctionBegin;
1383   PetscAssertPointer(filename, 2);
1384   PetscCallMPI(MPI_Comm_rank(comm, &rank));
1385 #if defined(PETSC_HAVE_EXODUSII)
1386   if (rank == 0) {
1387     exoid = ex_open(filename, EX_READ, &CPU_word_size, &IO_word_size, &version);
1388     PetscCheck(exoid > 0, PETSC_COMM_SELF, PETSC_ERR_LIB, "ex_open(\"%s\",...) did not return a valid file ID", filename);
1389   }
1390   PetscCall(DMPlexCreateExodus(comm, exoid, interpolate, dm));
1391   if (rank == 0) PetscCallExternal(ex_close, exoid);
1392   PetscFunctionReturn(PETSC_SUCCESS);
1393 #else
1394   SETERRQ(comm, PETSC_ERR_SUP, "This method requires ExodusII support. Reconfigure using --download-exodusii");
1395 #endif
1396 }
1397 
1398 #if defined(PETSC_HAVE_EXODUSII)
1399 static PetscErrorCode ExodusGetCellType_Internal(const char *elem_type, DMPolytopeType *ct)
1400 {
1401   PetscBool flg;
1402 
1403   PetscFunctionBegin;
1404   *ct = DM_POLYTOPE_UNKNOWN;
1405   PetscCall(PetscStrcmp(elem_type, "BAR2", &flg));
1406   if (flg) {
1407     *ct = DM_POLYTOPE_SEGMENT;
1408     goto done;
1409   }
1410   PetscCall(PetscStrcmp(elem_type, "BAR3", &flg));
1411   if (flg) {
1412     *ct = DM_POLYTOPE_SEGMENT;
1413     goto done;
1414   }
1415   PetscCall(PetscStrcmp(elem_type, "TRI", &flg));
1416   if (flg) {
1417     *ct = DM_POLYTOPE_TRIANGLE;
1418     goto done;
1419   }
1420   PetscCall(PetscStrcmp(elem_type, "TRI3", &flg));
1421   if (flg) {
1422     *ct = DM_POLYTOPE_TRIANGLE;
1423     goto done;
1424   }
1425   PetscCall(PetscStrcmp(elem_type, "QUAD", &flg));
1426   if (flg) {
1427     *ct = DM_POLYTOPE_QUADRILATERAL;
1428     goto done;
1429   }
1430   PetscCall(PetscStrcmp(elem_type, "QUAD4", &flg));
1431   if (flg) {
1432     *ct = DM_POLYTOPE_QUADRILATERAL;
1433     goto done;
1434   }
1435   PetscCall(PetscStrcmp(elem_type, "SHELL4", &flg));
1436   if (flg) {
1437     *ct = DM_POLYTOPE_QUADRILATERAL;
1438     goto done;
1439   }
1440   PetscCall(PetscStrcmp(elem_type, "TETRA", &flg));
1441   if (flg) {
1442     *ct = DM_POLYTOPE_TETRAHEDRON;
1443     goto done;
1444   }
1445   PetscCall(PetscStrcmp(elem_type, "TET4", &flg));
1446   if (flg) {
1447     *ct = DM_POLYTOPE_TETRAHEDRON;
1448     goto done;
1449   }
1450   PetscCall(PetscStrcmp(elem_type, "WEDGE", &flg));
1451   if (flg) {
1452     *ct = DM_POLYTOPE_TRI_PRISM;
1453     goto done;
1454   }
1455   PetscCall(PetscStrcmp(elem_type, "HEX", &flg));
1456   if (flg) {
1457     *ct = DM_POLYTOPE_HEXAHEDRON;
1458     goto done;
1459   }
1460   PetscCall(PetscStrcmp(elem_type, "HEX8", &flg));
1461   if (flg) {
1462     *ct = DM_POLYTOPE_HEXAHEDRON;
1463     goto done;
1464   }
1465   PetscCall(PetscStrcmp(elem_type, "HEXAHEDRON", &flg));
1466   if (flg) {
1467     *ct = DM_POLYTOPE_HEXAHEDRON;
1468     goto done;
1469   }
1470   SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unrecognized element type %s", elem_type);
1471 done:
1472   PetscFunctionReturn(PETSC_SUCCESS);
1473 }
1474 #endif
1475 
1476 /*@
1477   DMPlexCreateExodus - Create a `DMPLEX` mesh from an ExodusII file ID.
1478 
1479   Collective
1480 
1481   Input Parameters:
1482 + comm        - The MPI communicator
1483 . exoid       - The ExodusII id associated with a exodus file and obtained using ex_open
1484 - interpolate - Create faces and edges in the mesh
1485 
1486   Output Parameter:
1487 . dm - The `DM` object representing the mesh
1488 
1489   Level: beginner
1490 
1491 .seealso: [](ch_unstructured), `DM`, `PETSCVIEWEREXODUSII`, `DMPLEX`, `DMCreate()`
1492 @*/
1493 PetscErrorCode DMPlexCreateExodus(MPI_Comm comm, PetscInt exoid, PetscBool interpolate, DM *dm)
1494 {
1495 #if defined(PETSC_HAVE_EXODUSII)
1496   PetscMPIInt  num_proc, rank;
1497   DMLabel      cellSets = NULL, faceSets = NULL, vertSets = NULL;
1498   PetscSection coordSection;
1499   Vec          coordinates;
1500   PetscScalar *coords;
1501   PetscInt     coordSize, v;
1502   /* Read from ex_get_init() */
1503   char title[PETSC_MAX_PATH_LEN + 1];
1504   int  dim = 0, dimEmbed = 0, numVertices = 0, numCells = 0;
1505   int  num_cs = 0, num_vs = 0, num_fs = 0;
1506 #endif
1507 
1508   PetscFunctionBegin;
1509 #if defined(PETSC_HAVE_EXODUSII)
1510   PetscCallMPI(MPI_Comm_rank(comm, &rank));
1511   PetscCallMPI(MPI_Comm_size(comm, &num_proc));
1512   PetscCall(DMCreate(comm, dm));
1513   PetscCall(DMSetType(*dm, DMPLEX));
1514   /* Open EXODUS II file and read basic information on rank 0, then broadcast to all processors */
1515   if (rank == 0) {
1516     PetscCall(PetscMemzero(title, PETSC_MAX_PATH_LEN + 1));
1517     PetscCallExternal(ex_get_init, exoid, title, &dimEmbed, &numVertices, &numCells, &num_cs, &num_vs, &num_fs);
1518     PetscCheck(num_cs, PETSC_COMM_SELF, PETSC_ERR_SUP, "Exodus file does not contain any cell set");
1519   }
1520   PetscCallMPI(MPI_Bcast(title, PETSC_MAX_PATH_LEN + 1, MPI_CHAR, 0, comm));
1521   PetscCallMPI(MPI_Bcast(&dim, 1, MPI_INT, 0, comm));
1522   PetscCall(PetscObjectSetName((PetscObject)*dm, title));
1523   PetscCall(DMPlexSetChart(*dm, 0, numCells + numVertices));
1524   /*   We do not want this label automatically computed, instead we compute it here */
1525   PetscCall(DMCreateLabel(*dm, "celltype"));
1526 
1527   /* Read cell sets information */
1528   if (rank == 0) {
1529     PetscInt *cone;
1530     int       c, cs, ncs, c_loc, v, v_loc;
1531     /* Read from ex_get_elem_blk_ids() */
1532     int *cs_id, *cs_order;
1533     /* Read from ex_get_elem_block() */
1534     char buffer[PETSC_MAX_PATH_LEN + 1];
1535     int  num_cell_in_set, num_vertex_per_cell, num_hybrid, num_attr;
1536     /* Read from ex_get_elem_conn() */
1537     int *cs_connect;
1538 
1539     /* Get cell sets IDs */
1540     PetscCall(PetscMalloc2(num_cs, &cs_id, num_cs, &cs_order));
1541     PetscCallExternal(ex_get_ids, exoid, EX_ELEM_BLOCK, cs_id);
1542     /* Read the cell set connectivity table and build mesh topology
1543        EXO standard requires that cells in cell sets be numbered sequentially and be pairwise disjoint. */
1544     /* Check for a hybrid mesh */
1545     for (cs = 0, num_hybrid = 0; cs < num_cs; ++cs) {
1546       DMPolytopeType ct;
1547       char           elem_type[PETSC_MAX_PATH_LEN];
1548 
1549       PetscCall(PetscArrayzero(elem_type, sizeof(elem_type)));
1550       PetscCallExternal(ex_get_elem_type, exoid, cs_id[cs], elem_type);
1551       PetscCall(ExodusGetCellType_Internal(elem_type, &ct));
1552       dim = PetscMax(dim, DMPolytopeTypeGetDim(ct));
1553       PetscCallExternal(ex_get_block, exoid, EX_ELEM_BLOCK, cs_id[cs], buffer, &num_cell_in_set, &num_vertex_per_cell, 0, 0, &num_attr);
1554       switch (ct) {
1555       case DM_POLYTOPE_TRI_PRISM:
1556         cs_order[cs] = cs;
1557         ++num_hybrid;
1558         break;
1559       default:
1560         for (c = cs; c > cs - num_hybrid; --c) cs_order[c] = cs_order[c - 1];
1561         cs_order[cs - num_hybrid] = cs;
1562       }
1563     }
1564     /* First set sizes */
1565     for (ncs = 0, c = 0; ncs < num_cs; ++ncs) {
1566       DMPolytopeType ct;
1567       char           elem_type[PETSC_MAX_PATH_LEN];
1568       const PetscInt cs = cs_order[ncs];
1569 
1570       PetscCall(PetscArrayzero(elem_type, sizeof(elem_type)));
1571       PetscCallExternal(ex_get_elem_type, exoid, cs_id[cs], elem_type);
1572       PetscCall(ExodusGetCellType_Internal(elem_type, &ct));
1573       PetscCallExternal(ex_get_block, exoid, EX_ELEM_BLOCK, cs_id[cs], buffer, &num_cell_in_set, &num_vertex_per_cell, 0, 0, &num_attr);
1574       for (c_loc = 0; c_loc < num_cell_in_set; ++c_loc, ++c) {
1575         PetscCall(DMPlexSetConeSize(*dm, c, num_vertex_per_cell));
1576         PetscCall(DMPlexSetCellType(*dm, c, ct));
1577       }
1578     }
1579     for (v = numCells; v < numCells + numVertices; ++v) PetscCall(DMPlexSetCellType(*dm, v, DM_POLYTOPE_POINT));
1580     PetscCall(DMSetUp(*dm));
1581     for (ncs = 0, c = 0; ncs < num_cs; ++ncs) {
1582       const PetscInt cs = cs_order[ncs];
1583       PetscCallExternal(ex_get_block, exoid, EX_ELEM_BLOCK, cs_id[cs], buffer, &num_cell_in_set, &num_vertex_per_cell, 0, 0, &num_attr);
1584       PetscCall(PetscMalloc2(num_vertex_per_cell * num_cell_in_set, &cs_connect, num_vertex_per_cell, &cone));
1585       PetscCallExternal(ex_get_conn, exoid, EX_ELEM_BLOCK, cs_id[cs], cs_connect, NULL, NULL);
1586       /* EXO uses Fortran-based indexing, DMPlex uses C-style and numbers cell first then vertices. */
1587       for (c_loc = 0, v = 0; c_loc < num_cell_in_set; ++c_loc, ++c) {
1588         DMPolytopeType ct;
1589 
1590         for (v_loc = 0; v_loc < num_vertex_per_cell; ++v_loc, ++v) cone[v_loc] = cs_connect[v] + numCells - 1;
1591         PetscCall(DMPlexGetCellType(*dm, c, &ct));
1592         PetscCall(DMPlexInvertCell(ct, cone));
1593         PetscCall(DMPlexSetCone(*dm, c, cone));
1594         PetscCall(DMSetLabelValue_Fast(*dm, &cellSets, "Cell Sets", c, cs_id[cs]));
1595       }
1596       PetscCall(PetscFree2(cs_connect, cone));
1597     }
1598     PetscCall(PetscFree2(cs_id, cs_order));
1599   }
1600   {
1601     PetscInt ints[] = {dim, dimEmbed};
1602 
1603     PetscCallMPI(MPI_Bcast(ints, 2, MPIU_INT, 0, comm));
1604     PetscCall(DMSetDimension(*dm, ints[0]));
1605     PetscCall(DMSetCoordinateDim(*dm, ints[1]));
1606     dim      = ints[0];
1607     dimEmbed = ints[1];
1608   }
1609   PetscCall(DMPlexSymmetrize(*dm));
1610   PetscCall(DMPlexStratify(*dm));
1611   if (interpolate) {
1612     DM idm;
1613 
1614     PetscCall(DMPlexInterpolate(*dm, &idm));
1615     PetscCall(DMDestroy(dm));
1616     *dm = idm;
1617   }
1618 
1619   /* Create vertex set label */
1620   if (rank == 0 && (num_vs > 0)) {
1621     int vs, v;
1622     /* Read from ex_get_node_set_ids() */
1623     int *vs_id;
1624     /* Read from ex_get_node_set_param() */
1625     int num_vertex_in_set;
1626     /* Read from ex_get_node_set() */
1627     int *vs_vertex_list;
1628 
1629     /* Get vertex set ids */
1630     PetscCall(PetscMalloc1(num_vs, &vs_id));
1631     PetscCallExternal(ex_get_ids, exoid, EX_NODE_SET, vs_id);
1632     for (vs = 0; vs < num_vs; ++vs) {
1633       PetscCallExternal(ex_get_set_param, exoid, EX_NODE_SET, vs_id[vs], &num_vertex_in_set, NULL);
1634       PetscCall(PetscMalloc1(num_vertex_in_set, &vs_vertex_list));
1635       PetscCallExternal(ex_get_set, exoid, EX_NODE_SET, vs_id[vs], vs_vertex_list, NULL);
1636       for (v = 0; v < num_vertex_in_set; ++v) PetscCall(DMSetLabelValue_Fast(*dm, &vertSets, "Vertex Sets", vs_vertex_list[v] + numCells - 1, vs_id[vs]));
1637       PetscCall(PetscFree(vs_vertex_list));
1638     }
1639     PetscCall(PetscFree(vs_id));
1640   }
1641   /* Read coordinates */
1642   PetscCall(DMGetCoordinateSection(*dm, &coordSection));
1643   PetscCall(PetscSectionSetNumFields(coordSection, 1));
1644   PetscCall(PetscSectionSetFieldComponents(coordSection, 0, dimEmbed));
1645   PetscCall(PetscSectionSetChart(coordSection, numCells, numCells + numVertices));
1646   for (v = numCells; v < numCells + numVertices; ++v) {
1647     PetscCall(PetscSectionSetDof(coordSection, v, dimEmbed));
1648     PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, dimEmbed));
1649   }
1650   PetscCall(PetscSectionSetUp(coordSection));
1651   PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
1652   PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates));
1653   PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
1654   PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
1655   PetscCall(VecSetBlockSize(coordinates, dimEmbed));
1656   PetscCall(VecSetType(coordinates, VECSTANDARD));
1657   PetscCall(VecGetArray(coordinates, &coords));
1658   if (rank == 0) {
1659     PetscReal *x, *y, *z;
1660 
1661     PetscCall(PetscMalloc3(numVertices, &x, numVertices, &y, numVertices, &z));
1662     PetscCallExternal(ex_get_coord, exoid, x, y, z);
1663     if (dimEmbed > 0) {
1664       for (v = 0; v < numVertices; ++v) coords[v * dimEmbed + 0] = x[v];
1665     }
1666     if (dimEmbed > 1) {
1667       for (v = 0; v < numVertices; ++v) coords[v * dimEmbed + 1] = y[v];
1668     }
1669     if (dimEmbed > 2) {
1670       for (v = 0; v < numVertices; ++v) coords[v * dimEmbed + 2] = z[v];
1671     }
1672     PetscCall(PetscFree3(x, y, z));
1673   }
1674   PetscCall(VecRestoreArray(coordinates, &coords));
1675   PetscCall(DMSetCoordinatesLocal(*dm, coordinates));
1676   PetscCall(VecDestroy(&coordinates));
1677 
1678   /* Create side set label */
1679   if (rank == 0 && interpolate && (num_fs > 0)) {
1680     int fs, f, voff;
1681     /* Read from ex_get_side_set_ids() */
1682     int *fs_id;
1683     /* Read from ex_get_side_set_param() */
1684     int num_side_in_set;
1685     /* Read from ex_get_side_set_node_list() */
1686     int *fs_vertex_count_list, *fs_vertex_list, *fs_side_list;
1687     /* Read side set labels */
1688     char   fs_name[MAX_STR_LENGTH + 1];
1689     size_t fs_name_len;
1690 
1691     /* Get side set ids */
1692     PetscCall(PetscMalloc1(num_fs, &fs_id));
1693     PetscCallExternal(ex_get_ids, exoid, EX_SIDE_SET, fs_id);
1694     // Ids 1 and 2 are reserved by ExodusII for indicating things in 3D
1695     for (fs = 0; fs < num_fs; ++fs) {
1696       PetscCallExternal(ex_get_set_param, exoid, EX_SIDE_SET, fs_id[fs], &num_side_in_set, NULL);
1697       PetscCall(PetscMalloc3(num_side_in_set, &fs_vertex_count_list, num_side_in_set * 4, &fs_vertex_list, num_side_in_set, &fs_side_list));
1698       PetscCallExternal(ex_get_side_set_node_list, exoid, fs_id[fs], fs_vertex_count_list, fs_vertex_list);
1699       PetscCallExternal(ex_get_set, exoid, EX_SIDE_SET, fs_id[fs], NULL, fs_side_list);
1700 
1701       /* Get the specific name associated with this side set ID. */
1702       int fs_name_err = ex_get_name(exoid, EX_SIDE_SET, fs_id[fs], fs_name);
1703       if (!fs_name_err) {
1704         PetscCall(PetscStrlen(fs_name, &fs_name_len));
1705         if (fs_name_len == 0) PetscCall(PetscStrncpy(fs_name, "Face Sets", MAX_STR_LENGTH));
1706       }
1707       for (f = 0, voff = 0; f < num_side_in_set; ++f) {
1708         const PetscInt *faces    = NULL;
1709         PetscInt        faceSize = fs_vertex_count_list[f], numFaces;
1710         PetscInt        faceVertices[4], v;
1711 
1712         PetscCheck(faceSize <= 4, comm, PETSC_ERR_ARG_WRONG, "ExodusII side cannot have %" PetscInt_FMT " > 4 vertices", faceSize);
1713         for (v = 0; v < faceSize; ++v, ++voff) faceVertices[v] = fs_vertex_list[voff] + numCells - 1;
1714         PetscCall(DMPlexGetFullJoin(*dm, faceSize, faceVertices, &numFaces, &faces));
1715         PetscCheck(numFaces == 1, comm, PETSC_ERR_ARG_WRONG, "Invalid ExodusII side %d in set %d maps to %" PetscInt_FMT " faces", f, fs, numFaces);
1716         PetscCall(DMSetLabelValue_Fast(*dm, &faceSets, "Face Sets", faces[0], fs_id[fs]));
1717         /* Only add the label if one has been detected for this side set. */
1718         if (!fs_name_err) PetscCall(DMSetLabelValue(*dm, fs_name, faces[0], fs_id[fs]));
1719         PetscCall(DMPlexRestoreJoin(*dm, faceSize, faceVertices, &numFaces, &faces));
1720       }
1721       PetscCall(PetscFree3(fs_vertex_count_list, fs_vertex_list, fs_side_list));
1722     }
1723     PetscCall(PetscFree(fs_id));
1724   }
1725 
1726   { /* Create Cell/Face/Vertex Sets labels at all processes */
1727     enum {
1728       n = 3
1729     };
1730     PetscBool flag[n];
1731 
1732     flag[0] = cellSets ? PETSC_TRUE : PETSC_FALSE;
1733     flag[1] = faceSets ? PETSC_TRUE : PETSC_FALSE;
1734     flag[2] = vertSets ? PETSC_TRUE : PETSC_FALSE;
1735     PetscCallMPI(MPI_Bcast(flag, n, MPIU_BOOL, 0, comm));
1736     if (flag[0]) PetscCall(DMCreateLabel(*dm, "Cell Sets"));
1737     if (flag[1]) PetscCall(DMCreateLabel(*dm, "Face Sets"));
1738     if (flag[2]) PetscCall(DMCreateLabel(*dm, "Vertex Sets"));
1739   }
1740   PetscFunctionReturn(PETSC_SUCCESS);
1741 #else
1742   SETERRQ(comm, PETSC_ERR_SUP, "This method requires ExodusII support. Reconfigure using --download-exodusii");
1743 #endif
1744 }
1745