Actual source code: plexexodusii2.c

  1: #define PETSCDM_DLL
  2: #include <petsc/private/dmpleximpl.h>

  4: #include <netcdf.h>
  5: #include <exodusII.h>

  7: #include <petsc/private/viewerimpl.h>
  8: #include <petsc/private/viewerexodusiiimpl.h>
  9: /*@C
 10:   PETSC_VIEWER_EXODUSII_ - Creates an `PETSCVIEWEREXODUSII` `PetscViewer` shared by all processors in a communicator.

 12:   Collective; No Fortran Support

 14:   Input Parameter:
 15: . comm - the MPI communicator to share the `PETSCVIEWEREXODUSII` `PetscViewer`

 17:   Level: intermediate

 19:   Note:
 20:   Unlike almost all other PETSc routines, `PETSC_VIEWER_EXODUSII_()` does not return
 21:   an error code.  The GLVIS PetscViewer is usually used in the form
 22: $       XXXView(XXX object, PETSC_VIEWER_EXODUSII_(comm));

 24: .seealso: `PETSCVIEWEREXODUSII`, `PetscViewer`, `PetscViewer`, `PetscViewerExodusIIOpen()`, `PetscViewerType`, `PetscViewerCreate()`, `PetscViewerDestroy()`
 25: @*/
 26: PetscViewer PETSC_VIEWER_EXODUSII_(MPI_Comm comm)
 27: {
 28:   PetscViewer viewer;

 30:   PetscFunctionBegin;
 31:   PetscCallNull(PetscViewerExodusIIOpen(comm, "mesh.exo", FILE_MODE_WRITE, &viewer));
 32:   PetscCallNull(PetscObjectRegisterDestroy((PetscObject)viewer));
 33:   PetscFunctionReturn(viewer);
 34: }

 36: static PetscErrorCode PetscViewerView_ExodusII(PetscViewer v, PetscViewer viewer)
 37: {
 38:   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)v->data;

 40:   PetscFunctionBegin;
 41:   if (exo->filename) PetscCall(PetscViewerASCIIPrintf(viewer, "Filename:    %s\n", exo->filename));
 42:   if (exo->exoid) PetscCall(PetscViewerASCIIPrintf(viewer, "exoid:       %" PetscExodusIIInt_FMT "\n", exo->exoid));
 43:   if (exo->btype) PetscCall(PetscViewerASCIIPrintf(viewer, "IO Mode:     %d\n", exo->btype));
 44:   if (exo->order) PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh order:  %" PetscInt_FMT "\n", exo->order));
 45:   PetscCall(PetscViewerASCIIPrintf(viewer, "Number of nodal variables:  %" PetscExodusIIInt_FMT "\n", exo->numNodalVariables));
 46:   for (int i = 0; i < exo->numNodalVariables; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "   %d: %s\n", i, exo->nodalVariableNames[i]));
 47:   PetscCall(PetscViewerASCIIPrintf(viewer, "Number of zonal variables:  %" PetscExodusIIInt_FMT "\n", exo->numZonalVariables));
 48:   for (int i = 0; i < exo->numZonalVariables; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "   %d: %s\n", i, exo->zonalVariableNames[i]));
 49:   PetscFunctionReturn(PETSC_SUCCESS);
 50: }

 52: static PetscErrorCode PetscViewerFlush_ExodusII(PetscViewer v)
 53: {
 54:   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)v->data;

 56:   PetscFunctionBegin;
 57:   if (exo->exoid >= 0) PetscCallExternal(ex_update, exo->exoid);
 58:   PetscFunctionReturn(PETSC_SUCCESS);
 59: }

 61: static PetscErrorCode PetscViewerSetFromOptions_ExodusII(PetscViewer v, PetscOptionItems PetscOptionsObject)
 62: {
 63:   PetscFunctionBegin;
 64:   PetscOptionsHeadBegin(PetscOptionsObject, "ExodusII PetscViewer Options");
 65:   PetscOptionsHeadEnd();
 66:   PetscFunctionReturn(PETSC_SUCCESS);
 67: }

 69: static PetscErrorCode PetscViewerDestroy_ExodusII(PetscViewer viewer)
 70: {
 71:   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;

 73:   PetscFunctionBegin;
 74:   if (exo->exoid >= 0) PetscCallExternal(ex_close, exo->exoid);
 75:   for (PetscInt i = 0; i < exo->numZonalVariables; i++) PetscCall(PetscFree(exo->zonalVariableNames[i]));
 76:   PetscCall(PetscFree(exo->zonalVariableNames));
 77:   for (PetscInt i = 0; i < exo->numNodalVariables; i++) PetscCall(PetscFree(exo->nodalVariableNames[i]));
 78:   PetscCall(PetscFree(exo->nodalVariableNames));
 79:   PetscCall(PetscFree(exo->filename));
 80:   PetscCall(PetscFree(exo));
 81:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileSetName_C", NULL));
 82:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileGetName_C", NULL));
 83:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileSetMode_C", NULL));
 84:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileGetMode_C", NULL));
 85:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGetId_C", NULL));
 86:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGetOrder_C", NULL));
 87:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerSetOrder_C", NULL));
 88:   PetscFunctionReturn(PETSC_SUCCESS);
 89: }

 91: static PetscErrorCode PetscViewerFileSetName_ExodusII(PetscViewer viewer, const char name[])
 92: {
 93:   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;
 94:   PetscMPIInt           rank;
 95:   PetscExodusIIInt      CPU_word_size, IO_word_size, EXO_mode;
 96:   MPI_Info              mpi_info = MPI_INFO_NULL;
 97:   PetscExodusIIFloat    EXO_version;

 99:   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
100:   CPU_word_size = sizeof(PetscReal);
101:   IO_word_size  = sizeof(PetscReal);

103:   PetscFunctionBegin;
104:   if (exo->exoid >= 0) {
105:     PetscCallExternal(ex_close, exo->exoid);
106:     exo->exoid = -1;
107:   }
108:   if (exo->filename) PetscCall(PetscFree(exo->filename));
109:   PetscCall(PetscStrallocpy(name, &exo->filename));
110:   switch (exo->btype) {
111:   case FILE_MODE_READ:
112:     EXO_mode = EX_READ;
113:     break;
114:   case FILE_MODE_APPEND:
115:   case FILE_MODE_UPDATE:
116:   case FILE_MODE_APPEND_UPDATE:
117:     /* Will fail if the file does not already exist */
118:     EXO_mode = EX_WRITE;
119:     break;
120:   case FILE_MODE_WRITE:
121:     /*
122:       exodus only allows writing geometry upon file creation, so we will let DMView create the file.
123:     */
124:     PetscFunctionReturn(PETSC_SUCCESS);
125:     break;
126:   default:
127:     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ORDER, "Must call PetscViewerFileSetMode() before PetscViewerFileSetName()");
128:   }
129: #if defined(PETSC_USE_64BIT_INDICES)
130:   EXO_mode += EX_ALL_INT64_API;
131: #endif
132:   exo->exoid = ex_open_par(name, EXO_mode, &CPU_word_size, &IO_word_size, &EXO_version, PETSC_COMM_WORLD, mpi_info);
133:   PetscCheck(exo->exoid >= 0, PETSC_COMM_SELF, PETSC_ERR_LIB, "ex_open_par failed for %s", name);
134:   PetscFunctionReturn(PETSC_SUCCESS);
135: }

137: static PetscErrorCode PetscViewerFileGetName_ExodusII(PetscViewer viewer, const char *name[])
138: {
139:   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;

141:   PetscFunctionBegin;
142:   *name = exo->filename;
143:   PetscFunctionReturn(PETSC_SUCCESS);
144: }

146: static PetscErrorCode PetscViewerFileSetMode_ExodusII(PetscViewer viewer, PetscFileMode type)
147: {
148:   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;

150:   PetscFunctionBegin;
151:   exo->btype = type;
152:   PetscFunctionReturn(PETSC_SUCCESS);
153: }

155: static PetscErrorCode PetscViewerFileGetMode_ExodusII(PetscViewer viewer, PetscFileMode *type)
156: {
157:   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;

159:   PetscFunctionBegin;
160:   *type = exo->btype;
161:   PetscFunctionReturn(PETSC_SUCCESS);
162: }

164: static PetscErrorCode PetscViewerExodusIIGetId_ExodusII(PetscViewer viewer, PetscExodusIIInt *exoid)
165: {
166:   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;

168:   PetscFunctionBegin;
169:   *exoid = exo->exoid;
170:   PetscFunctionReturn(PETSC_SUCCESS);
171: }

173: static PetscErrorCode PetscViewerExodusIIGetOrder_ExodusII(PetscViewer viewer, PetscInt *order)
174: {
175:   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;

177:   PetscFunctionBegin;
178:   *order = exo->order;
179:   PetscFunctionReturn(PETSC_SUCCESS);
180: }

182: static PetscErrorCode PetscViewerExodusIISetOrder_ExodusII(PetscViewer viewer, PetscInt order)
183: {
184:   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;

186:   PetscFunctionBegin;
187:   exo->order = order;
188:   PetscFunctionReturn(PETSC_SUCCESS);
189: }

191: /*@
192:   PetscViewerExodusIISetZonalVariable - Sets the number of zonal variables in an exodusII file

194:   Collective;

196:   Input Parameters:
197: + viewer - a `PetscViewer` of type `PETSCVIEWEREXODUSII`
198: - num    - the number of zonal variables in the exodusII file

200:   Level: intermediate

202:   Notes:
203:   The exodusII API does not allow changing the number of variables in a file so this function will return an error
204:   if called twice, called on a read-only file, or called on file for which the number of variables has already been specified

206: .seealso: `PETSCVIEWEREXODUSII`, `PetscViewer`, `PetscViewerCreate()`, `PetscViewerDestroy()`, `PetscViewerExodusIIOpen()`, `PetscViewerSetType()`, `PetscViewerType`, `PetscViewerExodusIIGetZonalVariable()`
207: @*/
208: PetscErrorCode PetscViewerExodusIISetZonalVariable(PetscViewer viewer, PetscExodusIIInt num)
209: {
210:   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;
211:   MPI_Comm              comm;
212:   PetscExodusIIInt      exoid = -1;

214:   PetscFunctionBegin;
215:   PetscCall(PetscObjectGetComm((PetscObject)viewer, &comm));
216:   PetscCheck(exo->numZonalVariables == -1, comm, PETSC_ERR_SUP, "The number of zonal variables has already been set to %d and cannot be overwritten", exo->numZonalVariables);
217:   PetscCheck((exo->btype != FILE_MODE_READ) && (exo->btype != FILE_MODE_UNDEFINED), comm, PETSC_ERR_FILE_WRITE, "Cannot set the number of variables because the file is not writable");

219:   exo->numZonalVariables = num;
220:   PetscCall(PetscMalloc1(num, &exo->zonalVariableNames));
221:   for (int i = 0; i < num; i++) { exo->zonalVariableNames[i] = NULL; }
222:   PetscCall(PetscViewerExodusIIGetId(viewer, &exoid));
223:   PetscCallExternal(ex_put_variable_param, exoid, EX_ELEM_BLOCK, num);
224:   PetscFunctionReturn(PETSC_SUCCESS);
225: }

227: /*@
228:   PetscViewerExodusIISetNodalVariable - Sets the number of nodal variables in an exodusII file

230:   Collective;

232:   Input Parameters:
233: + viewer - a `PetscViewer` of type `PETSCVIEWEREXODUSII`
234: - num    - the number of nodal variables in the exodusII file

236:   Level: intermediate

238:   Notes:
239:   The exodusII API does not allow changing the number of variables in a file so this function will return an error
240:   if called twice, called on a read-only file, or called on file for which the number of variables has already been specified

242: .seealso: `PETSCVIEWEREXODUSII`, `PetscViewer`, `PetscViewerCreate()`, `PetscViewerDestroy()`, `PetscViewerExodusIIOpen()`, `PetscViewerSetType()`, `PetscViewerType`, `PetscViewerExodusIIGetNodalVariable()`
243: @*/
244: PetscErrorCode PetscViewerExodusIISetNodalVariable(PetscViewer viewer, PetscExodusIIInt num)
245: {
246:   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;
247:   MPI_Comm              comm;
248:   PetscExodusIIInt      exoid = -1;

250:   PetscFunctionBegin;
251:   PetscCall(PetscObjectGetComm((PetscObject)viewer, &comm));
252:   PetscCheck(exo->numNodalVariables == -1, comm, PETSC_ERR_SUP, "The number of nodal variables has already been set to %d and cannot be overwritten", exo->numNodalVariables);
253:   PetscCheck((exo->btype != FILE_MODE_READ) && (exo->btype != FILE_MODE_UNDEFINED), comm, PETSC_ERR_FILE_WRITE, "Cannot set the number of variables because the file is not writable");

255:   exo->numNodalVariables = num;
256:   PetscCall(PetscMalloc1(num, &exo->nodalVariableNames));
257:   for (int i = 0; i < num; i++) { exo->nodalVariableNames[i] = NULL; }
258:   PetscCall(PetscViewerExodusIIGetId(viewer, &exoid));
259:   PetscCallExternal(ex_put_variable_param, exoid, EX_NODAL, num);
260:   PetscFunctionReturn(PETSC_SUCCESS);
261: }

263: /*@
264:   PetscViewerExodusIIGetZonalVariable - Gets the number of zonal variables in an exodusII file

266:   Collective

268:   Input Parameters:
269: . viewer - a `PetscViewer` of type `PETSCVIEWEREXODUSII`

271:   Output Parameter:
272: . num - the number variables in the exodusII file

274:   Level: intermediate

276:   Notes:
277:   The number of variables in the exodusII file is cached in the viewer

279: .seealso: `PETSCVIEWEREXODUSII`, `PetscViewer`, `PetscViewerCreate()`, `PetscViewerDestroy()`, `PetscViewerExodusIIOpen()`, `PetscViewerSetType()`, `PetscViewerType`, `PetscViewerExodusIIsetZonalVariable()`
280: @*/
281: PetscErrorCode PetscViewerExodusIIGetZonalVariable(PetscViewer viewer, PetscExodusIIInt *num)
282: {
283:   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;
284:   MPI_Comm              comm;
285:   PetscExodusIIInt      exoid = -1;

287:   PetscFunctionBegin;
288:   if (exo->numZonalVariables > -1) {
289:     *num = exo->numZonalVariables;
290:   } else {
291:     PetscCall(PetscViewerExodusIIGetId(viewer, &exoid));
292:     PetscCall(PetscObjectGetComm((PetscObject)viewer, &comm));
293:     PetscCheck(exoid > 0, comm, PETSC_ERR_FILE_OPEN, "Exodus file is not open");
294:     PetscCallExternal(ex_get_variable_param, exoid, EX_ELEM_BLOCK, num);
295:     exo->numZonalVariables = *num;
296:     PetscCall(PetscMalloc1(*num, &exo->zonalVariableNames));
297:     for (int i = 0; i < *num; i++) { exo->zonalVariableNames[i] = NULL; }
298:   }
299:   PetscFunctionReturn(PETSC_SUCCESS);
300: }

302: /*@
303:   PetscViewerExodusIIGetNodalVariable - Gets the number of nodal variables in an exodusII file

305:   Collective

307:   Input Parameters:
308: . viewer - a `PetscViewer` of type `PETSCVIEWEREXODUSII`

310:   Output Parameter:
311: . num - the number variables in the exodusII file

313:   Level: intermediate

315:   Notes:
316:   This function gets the number of nodal variables and saves it in the address of num.

318: .seealso: `PETSCVIEWEREXODUSII`, `PetscViewer`, `PetscViewerCreate()`, `PetscViewerDestroy()`, `PetscViewerExodusIIOpen()`, `PetscViewerSetType()`, `PetscViewerType`, `PetscViewerExodusIISetNodalVariable()`
319: @*/
320: PetscErrorCode PetscViewerExodusIIGetNodalVariable(PetscViewer viewer, PetscExodusIIInt *num)
321: {
322:   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;
323:   MPI_Comm              comm;
324:   PetscExodusIIInt      exoid = -1;

326:   PetscFunctionBegin;
327:   if (exo->numNodalVariables > -1) {
328:     *num = exo->numNodalVariables;
329:   } else {
330:     PetscCall(PetscViewerExodusIIGetId(viewer, &exoid));
331:     PetscCall(PetscObjectGetComm((PetscObject)viewer, &comm));
332:     PetscCheck(exoid > 0, comm, PETSC_ERR_FILE_OPEN, "Exodus file is not open");
333:     PetscCallExternal(ex_get_variable_param, exoid, EX_NODAL, num);
334:     exo->numNodalVariables = *num;
335:     PetscCall(PetscMalloc1(*num, &exo->nodalVariableNames));
336:     for (int i = 0; i < *num; i++) { exo->nodalVariableNames[i] = NULL; }
337:   }
338:   PetscFunctionReturn(PETSC_SUCCESS);
339: }

341: /*@
342:   PetscViewerExodusIISetZonalVariableName - Sets the name of a zonal variable.

344:   Collective;

346:   Input Parameters:
347: + viewer - a `PetscViewer` of type `PETSCVIEWEREXODUSII`
348: . idx    - the index for which you want to save the name
349: - name   - string containing the name characters

351:   Level: intermediate

353: .seealso: `PETSCVIEWEREXODUSII`, `PetscViewer`, `PetscViewerCreate()`, `PetscViewerDestroy()`, `PetscViewerExodusIIOpen()`, `PetscViewerSetType()`, `PetscViewerType`, `PetscViewerExodusIIGetZonalVariableName()`
354: @*/
355: PetscErrorCode PetscViewerExodusIISetZonalVariableName(PetscViewer viewer, PetscExodusIIInt idx, const char name[])
356: {
357:   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;

359:   PetscFunctionBegin;
360:   PetscCheck((idx >= 0) && (idx < exo->numZonalVariables), PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Variable index out of range. Was PetscViewerExodusIISetZonalVariable called?");
361:   PetscCall(PetscStrallocpy(name, (char **)&exo->zonalVariableNames[idx]));
362:   PetscCallExternal(ex_put_variable_name, exo->exoid, EX_ELEM_BLOCK, idx + 1, name);
363:   PetscFunctionReturn(PETSC_SUCCESS);
364: }

366: /*@
367:   PetscViewerExodusIISetNodalVariableName - Sets the name of a nodal variable.

369:   Collective;

371:   Input Parameters:
372: + viewer - a `PetscViewer` of type `PETSCVIEWEREXODUSII`
373: . idx    - the index for which you want to save the name
374: - name   - string containing the name characters

376:   Level: intermediate

378: .seealso: `PETSCVIEWEREXODUSII`, `PetscViewer`, `PetscViewerCreate()`, `PetscViewerDestroy()`, `PetscViewerExodusIIOpen()`, `PetscViewerSetType()`, `PetscViewerType`, `PetscViewerExodusIIGetNodalVariableName()`
379: @*/
380: PetscErrorCode PetscViewerExodusIISetNodalVariableName(PetscViewer viewer, PetscExodusIIInt idx, const char name[])
381: {
382:   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;

384:   PetscFunctionBegin;
385:   PetscCheck((idx >= 0) && (idx < exo->numNodalVariables), PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Variable index out of range. Was PetscViewerExodusIISetNodalVariable called?");
386:   PetscCall(PetscStrallocpy(name, (char **)&exo->nodalVariableNames[idx]));
387:   PetscCallExternal(ex_put_variable_name, exo->exoid, EX_NODAL, idx + 1, name);
388:   PetscFunctionReturn(PETSC_SUCCESS);
389: }

391: /*@
392:   PetscViewerExodusIIGetZonalVariableName - Gets the name of a zonal variable.

394:   Collective;

396:   Input Parameters:
397: + viewer - a `PetscViewer` of type `PETSCVIEWEREXODUSII`
398: - idx    - the index for which you want to get the name

400:   Output Parameter:
401: . name - pointer to the string containing the name characters

403:   Level: intermediate

405: .seealso: `PETSCVIEWEREXODUSII`, `PetscViewer`, `PetscViewerCreate()`, `PetscViewerDestroy()`, `PetscViewerExodusIIOpen()`, `PetscViewerSetType()`, `PetscViewerType`, `PetscViewerExodusIISetZonalVariableName()`
406: @*/
407: PetscErrorCode PetscViewerExodusIIGetZonalVariableName(PetscViewer viewer, PetscExodusIIInt idx, const char *name[])
408: {
409:   PetscViewer_ExodusII *exo   = (PetscViewer_ExodusII *)viewer->data;
410:   PetscExodusIIInt      exoid = -1;
411:   char                  tmpName[MAX_NAME_LENGTH + 1];

413:   PetscFunctionBegin;
414:   PetscCheck(idx >= 0 && idx < exo->numZonalVariables, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Variable index out of range. Was PetscViewerExodusIISetZonalVariable called?");
415:   if (!exo->zonalVariableNames[idx]) {
416:     PetscCall(PetscViewerExodusIIGetId(viewer, &exoid));
417:     PetscCallExternal(ex_get_variable_name, exoid, EX_ELEM_BLOCK, idx + 1, tmpName);
418:     PetscCall(PetscStrallocpy(tmpName, (char **)&exo->zonalVariableNames[idx]));
419:   }
420:   *name = exo->zonalVariableNames[idx];
421:   PetscFunctionReturn(PETSC_SUCCESS);
422: }

424: /*@
425:   PetscViewerExodusIIGetNodalVariableName - Gets the name of a nodal variable.

427:   Collective;

429:   Input Parameters:
430: + viewer - a `PetscViewer` of type `PETSCVIEWEREXODUSII`
431: - idx    - the index for which you want to save the name

433:   Output Parameter:
434: . name - string array containing name characters

436:   Level: intermediate

438: .seealso: `PETSCVIEWEREXODUSII`, `PetscViewer`, `PetscViewerCreate()`, `PetscViewerDestroy()`, `PetscViewerExodusIIOpen()`, `PetscViewerSetType()`, `PetscViewerType`, `PetscViewerExodusIISetNodalVariableName()`
439: @*/
440: PetscErrorCode PetscViewerExodusIIGetNodalVariableName(PetscViewer viewer, PetscExodusIIInt idx, const char *name[])
441: {
442:   PetscViewer_ExodusII *exo   = (PetscViewer_ExodusII *)viewer->data;
443:   PetscExodusIIInt      exoid = -1;
444:   char                  tmpName[MAX_NAME_LENGTH + 1];

446:   PetscFunctionBegin;
447:   PetscCheck((idx >= 0) && (idx < exo->numNodalVariables), PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Variable index out of range. Was PetscViewerExodusIISetNodalVariable called?");
448:   if (!exo->nodalVariableNames[idx]) {
449:     PetscCall(PetscViewerExodusIIGetId(viewer, &exoid));
450:     PetscCallExternal(ex_get_variable_name, exoid, EX_NODAL, idx + 1, tmpName);
451:     PetscCall(PetscStrallocpy(tmpName, (char **)&exo->nodalVariableNames[idx]));
452:   }
453:   *name = exo->nodalVariableNames[idx];
454:   PetscFunctionReturn(PETSC_SUCCESS);
455: }

457: /*@C
458:   PetscViewerExodusIISetZonalVariableNames - Sets the names of all nodal variables

460:   Collective; No Fortran Support

462:   Input Parameters:
463: + viewer - a `PetscViewer` of type `PETSCVIEWEREXODUSII`
464: - names  - an array of string names to be set, the strings are copied into the `PetscViewer`

466:   Level: intermediate

468:   Notes:
469:   This function allows users to set multiple zonal variable names at a time.

471: .seealso: `PETSCVIEWEREXODUSII`, `PetscViewer`, `PetscViewerCreate()`, `PetscViewerDestroy()`, `PetscViewerExodusIIOpen()`, `PetscViewerSetType()`, `PetscViewerType`, `PetscViewerExodusIIGetZonalVariableNames()`
472: @*/
473: PetscErrorCode PetscViewerExodusIISetZonalVariableNames(PetscViewer viewer, const char *const names[])
474: {
475:   PetscExodusIIInt      numNames;
476:   PetscExodusIIInt      exoid = -1;
477:   PetscViewer_ExodusII *exo   = (PetscViewer_ExodusII *)viewer->data;

479:   PetscFunctionBegin;
480:   PetscCall(PetscViewerExodusIIGetZonalVariable(viewer, &numNames));
481:   PetscCheck(numNames >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Number of zonal variables not set. Was PetscViewerExodusIISetZonalVariable called?");

483:   PetscCall(PetscViewerExodusIIGetId(viewer, &exoid));
484:   for (int i = 0; i < numNames; i++) {
485:     PetscCall(PetscStrallocpy(names[i], &exo->zonalVariableNames[i]));
486:     PetscCallExternal(ex_put_variable_name, exoid, EX_ELEM_BLOCK, i + 1, names[i]);
487:   }
488:   PetscFunctionReturn(PETSC_SUCCESS);
489: }

491: /*@C
492:   PetscViewerExodusIISetNodalVariableNames - Sets the names of all nodal variables.

494:   Collective; No Fortran Support

496:   Input Parameters:
497: + viewer - a `PetscViewer` of type `PETSCVIEWEREXODUSII`
498: - names  - an array of string names to be set, the strings are copied into the `PetscViewer`

500:   Level: intermediate

502:   Notes:
503:   This function allows users to set multiple nodal variable names at a time.

505: .seealso: `PETSCVIEWEREXODUSII`, `PetscViewer`, `PetscViewerCreate()`, `PetscViewerDestroy()`, `PetscViewerExodusIIOpen()`, `PetscViewerSetType()`, `PetscViewerType`, `PetscViewerExodusIIGetNodalVariableNames()`
506: @*/
507: PetscErrorCode PetscViewerExodusIISetNodalVariableNames(PetscViewer viewer, const char *const names[])
508: {
509:   PetscExodusIIInt      numNames;
510:   PetscExodusIIInt      exoid = -1;
511:   PetscViewer_ExodusII *exo   = (PetscViewer_ExodusII *)viewer->data;

513:   PetscFunctionBegin;
514:   PetscCall(PetscViewerExodusIIGetNodalVariable(viewer, &numNames));
515:   PetscCheck(numNames >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Number of nodal variables not set. Was PetscViewerExodusIISetNodalVariable called?");

517:   PetscCall(PetscViewerExodusIIGetId(viewer, &exoid));
518:   for (int i = 0; i < numNames; i++) {
519:     PetscCall(PetscStrallocpy(names[i], &exo->nodalVariableNames[i]));
520:     PetscCallExternal(ex_put_variable_name, exoid, EX_NODAL, i + 1, names[i]);
521:   }
522:   PetscFunctionReturn(PETSC_SUCCESS);
523: }

525: /*@C
526:   PetscViewerExodusIIGetZonalVariableNames - Gets the names of all zonal variables.

528:   Collective; No Fortran Support

530:   Input Parameters:
531: + viewer  - a `PetscViewer` of type `PETSCVIEWEREXODUSII`
532: - numVars - the number of zonal variable names to retrieve

534:   Output Parameter:
535: . varNames - returns an array of char pointers where the zonal variable names are

537:   Level: intermediate

539:   Notes:
540:   This function returns a borrowed pointer which should not be freed.

542: .seealso: `PETSCVIEWEREXODUSII`, `PetscViewer`, `PetscViewerCreate()`, `PetscViewerDestroy()`, `PetscViewerExodusIIOpen()`, `PetscViewerSetType()`, `PetscViewerType`, `PetscViewerExodusIISetZonalVariableNames()`
543: @*/
544: PetscErrorCode PetscViewerExodusIIGetZonalVariableNames(PetscViewer viewer, PetscExodusIIInt *numVars, const char *const *varNames[])
545: {
546:   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;
547:   PetscExodusIIInt      idx;
548:   char                  tmpName[MAX_NAME_LENGTH + 1];
549:   PetscExodusIIInt      exoid = -1;

551:   PetscFunctionBegin;
552:   PetscCall(PetscViewerExodusIIGetZonalVariable(viewer, numVars));
553:   /*
554:     Cache variable names if necessary
555:   */
556:   for (idx = 0; idx < *numVars; idx++) {
557:     if (!exo->zonalVariableNames[idx]) {
558:       PetscCall(PetscViewerExodusIIGetId(viewer, &exoid));
559:       PetscCallExternal(ex_get_variable_name, exoid, EX_ELEM_BLOCK, idx + 1, tmpName);
560:       PetscCall(PetscStrallocpy(tmpName, (char **)&exo->zonalVariableNames[idx]));
561:     }
562:   }
563:   *varNames = (const char *const *)exo->zonalVariableNames;
564:   PetscFunctionReturn(PETSC_SUCCESS);
565: }

567: /*@C
568:   PetscViewerExodusIIGetNodalVariableNames - Gets the names of all nodal variables.

570:   Collective; No Fortran Support

572:   Input Parameters:
573: + viewer  - a `PetscViewer` of type `PETSCVIEWEREXODUSII`
574: - numVars - the number of nodal variable names to retrieve

576:   Output Parameter:
577: . varNames - returns an array of char pointers where the nodal variable names are

579:   Level: intermediate

581:   Notes:
582:   This function returns a borrowed pointer which should not be freed.

584: .seealso: `PETSCVIEWEREXODUSII`, `PetscViewer`, `PetscViewerCreate()`, `PetscViewerDestroy()`, `PetscViewerExodusIIOpen()`, `PetscViewerSetType()`, `PetscViewerType`, `PetscViewerExodusIISetNodalVariableNames()`
585: @*/
586: PetscErrorCode PetscViewerExodusIIGetNodalVariableNames(PetscViewer viewer, PetscExodusIIInt *numVars, const char *const *varNames[])
587: {
588:   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;
589:   PetscExodusIIInt      idx;
590:   char                  tmpName[MAX_NAME_LENGTH + 1];
591:   PetscExodusIIInt      exoid = -1;

593:   PetscFunctionBegin;
594:   PetscCall(PetscViewerExodusIIGetNodalVariable(viewer, numVars));
595:   /*
596:     Cache variable names if necessary
597:   */
598:   for (idx = 0; idx < *numVars; idx++) {
599:     if (!exo->nodalVariableNames[idx]) {
600:       PetscCall(PetscViewerExodusIIGetId(viewer, &exoid));
601:       PetscCallExternal(ex_get_variable_name, exoid, EX_NODAL, idx + 1, tmpName);
602:       PetscCall(PetscStrallocpy(tmpName, (char **)&exo->nodalVariableNames[idx]));
603:     }
604:   }
605:   *varNames = (const char *const *)exo->nodalVariableNames;
606:   PetscFunctionReturn(PETSC_SUCCESS);
607: }

609: /*MC
610:    PETSCVIEWEREXODUSII - A viewer that writes to an Exodus II file

612:   Level: beginner

614: .seealso: `PetscViewerExodusIIOpen()`, `PetscViewerCreate()`, `PETSCVIEWERBINARY`, `PETSCVIEWERHDF5`, `DMView()`,
615:           `PetscViewerFileSetName()`, `PetscViewerFileSetMode()`, `PetscViewerFormat`, `PetscViewerType`, `PetscViewerSetType()`
616: M*/
617: PETSC_EXTERN PetscErrorCode PetscViewerCreate_ExodusII(PetscViewer v)
618: {
619:   PetscViewer_ExodusII *exo;

621:   PetscFunctionBegin;
622:   PetscCall(PetscNew(&exo));

624:   v->data                 = (void *)exo;
625:   v->ops->destroy         = PetscViewerDestroy_ExodusII;
626:   v->ops->setfromoptions  = PetscViewerSetFromOptions_ExodusII;
627:   v->ops->view            = PetscViewerView_ExodusII;
628:   v->ops->flush           = PetscViewerFlush_ExodusII;
629:   exo->btype              = FILE_MODE_UNDEFINED;
630:   exo->filename           = 0;
631:   exo->exoid              = -1;
632:   exo->numNodalVariables  = -1;
633:   exo->numZonalVariables  = -1;
634:   exo->nodalVariableNames = NULL;
635:   exo->zonalVariableNames = NULL;

637:   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerFileSetName_C", PetscViewerFileSetName_ExodusII));
638:   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerFileGetName_C", PetscViewerFileGetName_ExodusII));
639:   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerFileSetMode_C", PetscViewerFileSetMode_ExodusII));
640:   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerFileGetMode_C", PetscViewerFileGetMode_ExodusII));
641:   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerGetId_C", PetscViewerExodusIIGetId_ExodusII));
642:   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerSetOrder_C", PetscViewerExodusIISetOrder_ExodusII));
643:   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerGetOrder_C", PetscViewerExodusIIGetOrder_ExodusII));
644:   PetscFunctionReturn(PETSC_SUCCESS);
645: }

647: /*@
648:   PetscViewerExodusIIGetNodalVariableIndex - return the location of a nodal variable in an exodusII file given its name

650:   Collective

652:   Input Parameters:
653: + viewer - a `PetscViewer` of type `PETSCVIEWEREXODUSII`
654: - name   - the name of the result

656:   Output Parameter:
657: . varIndex - the location of the variable in the exodus file or -1 if the variable is not found

659:   Level: beginner

661:   Notes:
662:   The exodus variable index is obtained by comparing the name argument to the
663:   names of zonal variables declared in the exodus file. For instance if name is "V"
664:   the location in the exodus file will be the first match of "V", "V_X", "V_XX", "V_1", or "V_11"
665:   amongst all variables of type obj_type.

667: .seealso: `PetscViewerExodusIISetNodalVariable()`, `PetscViewerExodusIIGetNodalVariable()`, `PetscViewerExodusIISetNodalVariableName()`, `PetscViewerExodusIISetNodalVariableNames()`, `PetscViewerExodusIIGetNodalVariableName()`, `PetscViewerExodusIIGetNodalVariableNames()`
668: @*/
669: PetscErrorCode PetscViewerExodusIIGetNodalVariableIndex(PetscViewer viewer, const char name[], PetscExodusIIInt *varIndex)
670: {
671:   PetscExodusIIInt   num_vars = 0, i, j;
672:   char               ext_name[MAX_STR_LENGTH + 1];
673:   const char *const *var_names;
674:   const int          num_suffix = 5;
675:   char              *suffix[5];
676:   PetscBool          flg;

678:   PetscFunctionBegin;
679:   suffix[0] = (char *)"";
680:   suffix[1] = (char *)"_X";
681:   suffix[2] = (char *)"_XX";
682:   suffix[3] = (char *)"_1";
683:   suffix[4] = (char *)"_11";
684:   *varIndex = -1;

686:   PetscCall(PetscViewerExodusIIGetNodalVariableNames(viewer, &num_vars, &var_names));
687:   for (i = 0; i < num_vars; ++i) {
688:     for (j = 0; j < num_suffix; ++j) {
689:       PetscCall(PetscStrncpy(ext_name, name, MAX_STR_LENGTH));
690:       PetscCall(PetscStrlcat(ext_name, suffix[j], MAX_STR_LENGTH));
691:       PetscCall(PetscStrcasecmp(ext_name, var_names[i], &flg));
692:       if (flg) *varIndex = i;
693:     }
694:     if (flg) break;
695:   }
696:   PetscFunctionReturn(PETSC_SUCCESS);
697: }

699: /*@
700:   PetscViewerExodusIIGetZonalVariableIndex - return the location of a zonal variable in an exodusII file given its name

702:   Collective

704:   Input Parameters:
705: + viewer - a `PetscViewer` of type `PETSCVIEWEREXODUSII`
706: - name   - the name of the result

708:   Output Parameter:
709: . varIndex - the location of the variable in the exodus file or -1 if the variable is not found

711:   Level: beginner

713:   Notes:
714:   The exodus variable index is obtained by comparing the name argument to the
715:   names of zonal variables declared in the exodus file. For instance if name is "V"
716:   the location in the exodus file will be the first match of "V", "V_X", "V_XX", "V_1", or "V_11"
717:   amongst all variables of type obj_type.

719: .seealso: `PetscViewerExodusIISetNodalVariable()`, `PetscViewerExodusIIGetNodalVariable()`, `PetscViewerExodusIISetNodalVariableName()`, `PetscViewerExodusIISetNodalVariableNames()`, `PetscViewerExodusIIGetNodalVariableName()`, `PetscViewerExodusIIGetNodalVariableNames()`
720: @*/
721: PetscErrorCode PetscViewerExodusIIGetZonalVariableIndex(PetscViewer viewer, const char name[], int *varIndex)
722: {
723:   PetscExodusIIInt   num_vars = 0, i, j;
724:   char               ext_name[MAX_STR_LENGTH + 1];
725:   const char *const *var_names;
726:   const int          num_suffix = 5;
727:   char              *suffix[5];
728:   PetscBool          flg;

730:   PetscFunctionBegin;
731:   suffix[0] = (char *)"";
732:   suffix[1] = (char *)"_X";
733:   suffix[2] = (char *)"_XX";
734:   suffix[3] = (char *)"_1";
735:   suffix[4] = (char *)"_11";
736:   *varIndex = -1;

738:   PetscCall(PetscViewerExodusIIGetZonalVariableNames(viewer, &num_vars, &var_names));
739:   for (i = 0; i < num_vars; ++i) {
740:     for (j = 0; j < num_suffix; ++j) {
741:       PetscCall(PetscStrncpy(ext_name, name, MAX_STR_LENGTH));
742:       PetscCall(PetscStrlcat(ext_name, suffix[j], MAX_STR_LENGTH));
743:       PetscCall(PetscStrcasecmp(ext_name, var_names[i], &flg));
744:       if (flg) *varIndex = i;
745:     }
746:     if (flg) break;
747:   }
748:   PetscFunctionReturn(PETSC_SUCCESS);
749: }

751: PetscErrorCode DMView_PlexExodusII(DM dm, PetscViewer viewer)
752: {
753:   enum ElemType {
754:     SEGMENT,
755:     TRI,
756:     QUAD,
757:     TET,
758:     HEX
759:   };
760:   MPI_Comm comm;
761:   PetscInt degree; /* the order of the mesh */
762:   /* Connectivity Variables */
763:   PetscInt cellsNotInConnectivity;
764:   /* Cell Sets */
765:   DMLabel         csLabel;
766:   IS              csIS;
767:   const PetscInt *csIdx;
768:   PetscInt        num_cs, cs;
769:   enum ElemType  *type;
770:   PetscBool       hasLabel;
771:   /* Coordinate Variables */
772:   DM                 cdm;
773:   PetscSection       coordSection;
774:   Vec                coord;
775:   PetscInt         **nodes;
776:   PetscInt           depth, d, dim, skipCells = 0;
777:   PetscInt           pStart, pEnd, p, cStart, cEnd, numCells, vStart, vEnd, numVertices, eStart, eEnd, numEdges, fStart, fEnd, numFaces, numNodes;
778:   PetscInt           num_vs, num_fs;
779:   PetscMPIInt        rank, size;
780:   const char        *dmName;
781:   PetscInt           nodesLineP1[4] = {2, 0, 0, 0};
782:   PetscInt           nodesLineP2[4] = {2, 0, 0, 1};
783:   PetscInt           nodesTriP1[4]  = {3, 0, 0, 0};
784:   PetscInt           nodesTriP2[4]  = {3, 3, 0, 0};
785:   PetscInt           nodesQuadP1[4] = {4, 0, 0, 0};
786:   PetscInt           nodesQuadP2[4] = {4, 4, 0, 1};
787:   PetscInt           nodesTetP1[4]  = {4, 0, 0, 0};
788:   PetscInt           nodesTetP2[4]  = {4, 6, 0, 0};
789:   PetscInt           nodesHexP1[4]  = {8, 0, 0, 0};
790:   PetscInt           nodesHexP2[4]  = {8, 12, 6, 1};
791:   PetscExodusIIInt   CPU_word_size, IO_word_size, EXO_mode;
792:   PetscExodusIIFloat EXO_version;

794:   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;

796:   PetscFunctionBegin;
797:   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
798:   PetscCallMPI(MPI_Comm_rank(comm, &rank));
799:   PetscCallMPI(MPI_Comm_size(comm, &size));

801:   /*
802:     Creating coordSection is a collective operation so we do it somewhat out of sequence
803:   */
804:   PetscCall(PetscSectionCreate(comm, &coordSection));
805:   PetscCall(DMGetCoordinatesLocalSetUp(dm));
806:   /*
807:     Check that all points are on rank 0 since we don't know how to save distributed DM in exodus format
808:   */
809:   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
810:   PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
811:   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
812:   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
813:   numCells    = cEnd - cStart;
814:   numEdges    = eEnd - eStart;
815:   numVertices = vEnd - vStart;
816:   PetscCheck(!(rank && (numCells || numEdges || numVertices)), PETSC_COMM_SELF, PETSC_ERR_SUP, "Writing distributed DM in exodusII format not supported");
817:   if (rank == 0) {
818:     switch (exo->btype) {
819:     case FILE_MODE_READ:
820:     case FILE_MODE_APPEND:
821:     case FILE_MODE_UPDATE:
822:     case FILE_MODE_APPEND_UPDATE:
823:       /* exodusII does not allow writing geometry to an existing file */
824:       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_LIB, "cannot add geometry to existing file %s", exo->filename);
825:     case FILE_MODE_WRITE:
826:       /* Create an empty file if one already exists*/
827:       EXO_mode = EX_CLOBBER;
828: #if defined(PETSC_USE_64BIT_INDICES)
829:       EXO_mode += EX_ALL_INT64_API;
830: #endif
831:       CPU_word_size = sizeof(PetscReal);
832:       IO_word_size  = sizeof(PetscReal);
833:       exo->exoid    = ex_create(exo->filename, EXO_mode, &CPU_word_size, &IO_word_size);
834:       PetscCheck(exo->exoid >= 0, PETSC_COMM_SELF, PETSC_ERR_LIB, "ex_create failed for %s", exo->filename);

836:       break;
837:     default:
838:       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ORDER, "Must call PetscViewerFileSetMode() before PetscViewerFileSetName()");
839:     }

841:     /* --- Get DM info --- */
842:     PetscCall(PetscObjectGetName((PetscObject)dm, &dmName));
843:     PetscCall(DMPlexGetDepth(dm, &depth));
844:     PetscCall(DMGetDimension(dm, &dim));
845:     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
846:     if (depth == 3) {
847:       numFaces = fEnd - fStart;
848:     } else {
849:       numFaces = 0;
850:     }
851:     PetscCall(DMGetLabelSize(dm, "Cell Sets", &num_cs));
852:     PetscCall(DMGetLabelSize(dm, "Vertex Sets", &num_vs));
853:     PetscCall(DMGetLabelSize(dm, "Face Sets", &num_fs));
854:     PetscCall(DMGetCoordinatesLocal(dm, &coord));
855:     PetscCall(DMGetCoordinateDM(dm, &cdm));
856:     if (num_cs > 0) {
857:       PetscCall(DMGetLabel(dm, "Cell Sets", &csLabel));
858:       PetscCall(DMLabelGetValueIS(csLabel, &csIS));
859:       PetscCall(ISGetIndices(csIS, &csIdx));
860:     }
861:     PetscCall(PetscMalloc1(num_cs, &nodes));
862:     /* Set element type for each block and compute total number of nodes */
863:     PetscCall(PetscMalloc1(num_cs, &type));
864:     numNodes = numVertices;

866:     PetscCall(PetscViewerExodusIIGetOrder(viewer, &degree));
867:     if (degree == 2) numNodes += numEdges;
868:     cellsNotInConnectivity = numCells;
869:     for (cs = 0; cs < num_cs; ++cs) {
870:       IS              stratumIS;
871:       const PetscInt *cells;
872:       PetscScalar    *xyz = NULL;
873:       PetscInt        csSize, closureSize;

875:       PetscCall(DMLabelGetStratumIS(csLabel, csIdx[cs], &stratumIS));
876:       PetscCall(ISGetIndices(stratumIS, &cells));
877:       PetscCall(ISGetSize(stratumIS, &csSize));
878:       PetscCall(DMPlexVecGetClosure(cdm, NULL, coord, cells[0], &closureSize, &xyz));
879:       switch (dim) {
880:       case 1:
881:         if (closureSize == 2 * dim) {
882:           type[cs] = SEGMENT;
883:         } 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);
884:         break;
885:       case 2:
886:         if (closureSize == 3 * dim) {
887:           type[cs] = TRI;
888:         } else if (closureSize == 4 * dim) {
889:           type[cs] = QUAD;
890:         } 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);
891:         break;
892:       case 3:
893:         if (closureSize == 4 * dim) {
894:           type[cs] = TET;
895:         } else if (closureSize == 8 * dim) {
896:           type[cs] = HEX;
897:         } 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);
898:         break;
899:       default:
900:         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Dimension %" PetscInt_FMT " not handled by ExodusII viewer", dim);
901:       }
902:       if ((degree == 2) && (type[cs] == SEGMENT)) numNodes += csSize;
903:       if ((degree == 2) && (type[cs] == QUAD)) numNodes += csSize;
904:       if ((degree == 2) && (type[cs] == HEX)) {
905:         numNodes += csSize;
906:         numNodes += numFaces;
907:       }
908:       PetscCall(DMPlexVecRestoreClosure(cdm, NULL, coord, cells[0], &closureSize, &xyz));
909:       /* Set nodes and Element type */
910:       if (type[cs] == SEGMENT) {
911:         if (degree == 1) nodes[cs] = nodesLineP1;
912:         else if (degree == 2) nodes[cs] = nodesLineP2;
913:       } else if (type[cs] == TRI) {
914:         if (degree == 1) nodes[cs] = nodesTriP1;
915:         else if (degree == 2) nodes[cs] = nodesTriP2;
916:       } else if (type[cs] == QUAD) {
917:         if (degree == 1) nodes[cs] = nodesQuadP1;
918:         else if (degree == 2) nodes[cs] = nodesQuadP2;
919:       } else if (type[cs] == TET) {
920:         if (degree == 1) nodes[cs] = nodesTetP1;
921:         else if (degree == 2) nodes[cs] = nodesTetP2;
922:       } else if (type[cs] == HEX) {
923:         if (degree == 1) nodes[cs] = nodesHexP1;
924:         else if (degree == 2) nodes[cs] = nodesHexP2;
925:       }
926:       /* Compute the number of cells not in the connectivity table */
927:       cellsNotInConnectivity -= nodes[cs][3] * csSize;

929:       PetscCall(ISRestoreIndices(stratumIS, &cells));
930:       PetscCall(ISDestroy(&stratumIS));
931:     }
932:     if (num_cs) PetscCallExternal(ex_put_init, exo->exoid, dmName, dim, numNodes, numCells, num_cs, num_vs, num_fs);
933:     /* --- Connectivity --- */
934:     for (cs = 0; cs < num_cs; ++cs) {
935:       IS              stratumIS;
936:       const PetscInt *cells;
937:       PetscInt       *connect, off = 0;
938:       PetscInt        edgesInClosure = 0, facesInClosure = 0, verticesInClosure = 0;
939:       PetscInt        csSize, c, connectSize, closureSize;
940:       char           *elem_type        = NULL;
941:       char            elem_type_bar2[] = "BAR2", elem_type_bar3[] = "BAR3";
942:       char            elem_type_tri3[] = "TRI3", elem_type_quad4[] = "QUAD4";
943:       char            elem_type_tri6[] = "TRI6", elem_type_quad9[] = "QUAD9";
944:       char            elem_type_tet4[] = "TET4", elem_type_hex8[] = "HEX8";
945:       char            elem_type_tet10[] = "TET10", elem_type_hex27[] = "HEX27";

947:       PetscCall(DMLabelGetStratumIS(csLabel, csIdx[cs], &stratumIS));
948:       PetscCall(ISGetIndices(stratumIS, &cells));
949:       PetscCall(ISGetSize(stratumIS, &csSize));
950:       /* Set Element type */
951:       if (type[cs] == SEGMENT) {
952:         if (degree == 1) elem_type = elem_type_bar2;
953:         else if (degree == 2) elem_type = elem_type_bar3;
954:       } else if (type[cs] == TRI) {
955:         if (degree == 1) elem_type = elem_type_tri3;
956:         else if (degree == 2) elem_type = elem_type_tri6;
957:       } else if (type[cs] == QUAD) {
958:         if (degree == 1) elem_type = elem_type_quad4;
959:         else if (degree == 2) elem_type = elem_type_quad9;
960:       } else if (type[cs] == TET) {
961:         if (degree == 1) elem_type = elem_type_tet4;
962:         else if (degree == 2) elem_type = elem_type_tet10;
963:       } else if (type[cs] == HEX) {
964:         if (degree == 1) elem_type = elem_type_hex8;
965:         else if (degree == 2) elem_type = elem_type_hex27;
966:       }
967:       connectSize = nodes[cs][0] + nodes[cs][1] + nodes[cs][2] + nodes[cs][3];
968:       PetscCall(PetscMalloc1(PetscMax(27, connectSize) * csSize, &connect));
969:       PetscCallExternal(ex_put_block, exo->exoid, EX_ELEM_BLOCK, csIdx[cs], elem_type, csSize, connectSize, 0, 0, 1);
970:       /* Find number of vertices, edges, and faces in the closure */
971:       verticesInClosure = nodes[cs][0];
972:       if (depth > 1) {
973:         if (dim == 2) {
974:           PetscCall(DMPlexGetConeSize(dm, cells[0], &edgesInClosure));
975:         } else if (dim == 3) {
976:           PetscInt *closure = NULL;

978:           PetscCall(DMPlexGetConeSize(dm, cells[0], &facesInClosure));
979:           PetscCall(DMPlexGetTransitiveClosure(dm, cells[0], PETSC_TRUE, &closureSize, &closure));
980:           edgesInClosure = closureSize - facesInClosure - 1 - verticesInClosure;
981:           PetscCall(DMPlexRestoreTransitiveClosure(dm, cells[0], PETSC_TRUE, &closureSize, &closure));
982:         }
983:       }
984:       /* Get connectivity for each cell */
985:       for (c = 0; c < csSize; ++c) {
986:         PetscInt *closure = NULL;
987:         PetscInt  temp, i;

989:         PetscCall(DMPlexGetTransitiveClosure(dm, cells[c], PETSC_TRUE, &closureSize, &closure));
990:         for (i = 0; i < connectSize; ++i) {
991:           if (i < nodes[cs][0]) { /* Vertices */
992:             connect[i + off] = closure[(i + edgesInClosure + facesInClosure + 1) * 2] + 1;
993:             connect[i + off] -= cellsNotInConnectivity;
994:           } else if (i < nodes[cs][0] + nodes[cs][1]) { /* Edges */
995:             connect[i + off] = closure[(i - verticesInClosure + facesInClosure + 1) * 2] + 1;
996:             if (nodes[cs][2] == 0) connect[i + off] -= numFaces;
997:             connect[i + off] -= cellsNotInConnectivity;
998:           } else if (i < nodes[cs][0] + nodes[cs][1] + nodes[cs][3]) { /* Cells */
999:             connect[i + off] = closure[0] + 1;
1000:             connect[i + off] -= skipCells;
1001:           } else if (i < nodes[cs][0] + nodes[cs][1] + nodes[cs][3] + nodes[cs][2]) { /* Faces */
1002:             connect[i + off] = closure[(i - edgesInClosure - verticesInClosure) * 2] + 1;
1003:             connect[i + off] -= cellsNotInConnectivity;
1004:           } else {
1005:             connect[i + off] = -1;
1006:           }
1007:         }
1008:         /* Tetrahedra are inverted */
1009:         if (type[cs] == TET) {
1010:           temp             = connect[0 + off];
1011:           connect[0 + off] = connect[1 + off];
1012:           connect[1 + off] = temp;
1013:           if (degree == 2) {
1014:             temp             = connect[5 + off];
1015:             connect[5 + off] = connect[6 + off];
1016:             connect[6 + off] = temp;
1017:             temp             = connect[7 + off];
1018:             connect[7 + off] = connect[8 + off];
1019:             connect[8 + off] = temp;
1020:           }
1021:         }
1022:         /* Hexahedra are inverted */
1023:         if (type[cs] == HEX) {
1024:           temp             = connect[1 + off];
1025:           connect[1 + off] = connect[3 + off];
1026:           connect[3 + off] = temp;
1027:           if (degree == 2) {
1028:             temp              = connect[8 + off];
1029:             connect[8 + off]  = connect[11 + off];
1030:             connect[11 + off] = temp;
1031:             temp              = connect[9 + off];
1032:             connect[9 + off]  = connect[10 + off];
1033:             connect[10 + off] = temp;
1034:             temp              = connect[16 + off];
1035:             connect[16 + off] = connect[17 + off];
1036:             connect[17 + off] = temp;
1037:             temp              = connect[18 + off];
1038:             connect[18 + off] = connect[19 + off];
1039:             connect[19 + off] = temp;

1041:             temp              = connect[12 + off];
1042:             connect[12 + off] = connect[16 + off];
1043:             connect[16 + off] = temp;
1044:             temp              = connect[13 + off];
1045:             connect[13 + off] = connect[17 + off];
1046:             connect[17 + off] = temp;
1047:             temp              = connect[14 + off];
1048:             connect[14 + off] = connect[18 + off];
1049:             connect[18 + off] = temp;
1050:             temp              = connect[15 + off];
1051:             connect[15 + off] = connect[19 + off];
1052:             connect[19 + off] = temp;

1054:             temp              = connect[23 + off];
1055:             connect[23 + off] = connect[26 + off];
1056:             connect[26 + off] = temp;
1057:             temp              = connect[24 + off];
1058:             connect[24 + off] = connect[25 + off];
1059:             connect[25 + off] = temp;
1060:             temp              = connect[25 + off];
1061:             connect[25 + off] = connect[26 + off];
1062:             connect[26 + off] = temp;
1063:           }
1064:         }
1065:         off += connectSize;
1066:         PetscCall(DMPlexRestoreTransitiveClosure(dm, cells[c], PETSC_TRUE, &closureSize, &closure));
1067:       }
1068:       PetscCallExternal(ex_put_conn, exo->exoid, EX_ELEM_BLOCK, csIdx[cs], connect, 0, 0);
1069:       skipCells += (nodes[cs][3] == 0) * csSize;
1070:       PetscCall(PetscFree(connect));
1071:       PetscCall(ISRestoreIndices(stratumIS, &cells));
1072:       PetscCall(ISDestroy(&stratumIS));
1073:     }
1074:     PetscCall(PetscFree(type));
1075:     /* --- Coordinates --- */
1076:     PetscCall(PetscSectionSetChart(coordSection, pStart, pEnd));
1077:     if (num_cs) {
1078:       for (d = 0; d < depth; ++d) {
1079:         PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1080:         for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(coordSection, p, nodes[0][d] > 0));
1081:       }
1082:     }
1083:     for (cs = 0; cs < num_cs; ++cs) {
1084:       IS              stratumIS;
1085:       const PetscInt *cells;
1086:       PetscInt        csSize, c;

1088:       PetscCall(DMLabelGetStratumIS(csLabel, csIdx[cs], &stratumIS));
1089:       PetscCall(ISGetIndices(stratumIS, &cells));
1090:       PetscCall(ISGetSize(stratumIS, &csSize));
1091:       for (c = 0; c < csSize; ++c) PetscCall(PetscSectionSetDof(coordSection, cells[c], nodes[cs][3] > 0));
1092:       PetscCall(ISRestoreIndices(stratumIS, &cells));
1093:       PetscCall(ISDestroy(&stratumIS));
1094:     }
1095:     if (num_cs) {
1096:       PetscCall(ISRestoreIndices(csIS, &csIdx));
1097:       PetscCall(ISDestroy(&csIS));
1098:     }
1099:     PetscCall(PetscFree(nodes));
1100:     PetscCall(PetscSectionSetUp(coordSection));
1101:     if (numNodes) {
1102:       const char  *coordNames[3] = {"x", "y", "z"};
1103:       PetscScalar *closure, *cval;
1104:       PetscReal   *coords;
1105:       PetscInt     hasDof, n = 0;

1107:       /* There can't be more than 24 values in the closure of a point for the coord coordSection */
1108:       PetscCall(PetscCalloc3(numNodes * 3, &coords, dim, &cval, 24, &closure));
1109:       PetscCall(DMGetCoordinatesLocalNoncollective(dm, &coord));
1110:       PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1111:       for (p = pStart; p < pEnd; ++p) {
1112:         PetscCall(PetscSectionGetDof(coordSection, p, &hasDof));
1113:         if (hasDof) {
1114:           PetscInt closureSize = 24, j;

1116:           PetscCall(DMPlexVecGetClosure(cdm, NULL, coord, p, &closureSize, &closure));
1117:           for (d = 0; d < dim; ++d) {
1118:             cval[d] = 0.0;
1119:             for (j = 0; j < closureSize / dim; j++) cval[d] += closure[j * dim + d];
1120:             coords[d * numNodes + n] = PetscRealPart(cval[d]) * dim / closureSize;
1121:           }
1122:           ++n;
1123:         }
1124:       }
1125:       PetscCallExternal(ex_put_coord, exo->exoid, &coords[0 * numNodes], &coords[1 * numNodes], &coords[2 * numNodes]);
1126:       PetscCall(PetscFree3(coords, cval, closure));
1127:       PetscCallExternal(ex_put_coord_names, exo->exoid, (char **)coordNames);
1128:     }

1130:     /* --- Node Sets/Vertex Sets --- */
1131:     PetscCall(DMHasLabel(dm, "Vertex Sets", &hasLabel));
1132:     if (hasLabel) {
1133:       PetscInt        i, vs, vsSize;
1134:       const PetscInt *vsIdx, *vertices;
1135:       PetscInt       *nodeList;
1136:       IS              vsIS, stratumIS;
1137:       DMLabel         vsLabel;
1138:       PetscCall(DMGetLabel(dm, "Vertex Sets", &vsLabel));
1139:       PetscCall(DMLabelGetValueIS(vsLabel, &vsIS));
1140:       PetscCall(ISGetIndices(vsIS, &vsIdx));
1141:       for (vs = 0; vs < num_vs; ++vs) {
1142:         PetscCall(DMLabelGetStratumIS(vsLabel, vsIdx[vs], &stratumIS));
1143:         PetscCall(ISGetIndices(stratumIS, &vertices));
1144:         PetscCall(ISGetSize(stratumIS, &vsSize));
1145:         PetscCall(PetscMalloc1(vsSize, &nodeList));
1146:         for (i = 0; i < vsSize; ++i) nodeList[i] = vertices[i] - skipCells + 1;
1147:         PetscCallExternal(ex_put_set_param, exo->exoid, EX_NODE_SET, vsIdx[vs], vsSize, 0);
1148:         PetscCallExternal(ex_put_set, exo->exoid, EX_NODE_SET, vsIdx[vs], nodeList, NULL);
1149:         PetscCall(ISRestoreIndices(stratumIS, &vertices));
1150:         PetscCall(ISDestroy(&stratumIS));
1151:         PetscCall(PetscFree(nodeList));
1152:       }
1153:       PetscCall(ISRestoreIndices(vsIS, &vsIdx));
1154:       PetscCall(ISDestroy(&vsIS));
1155:     }
1156:     /* --- Side Sets/Face Sets --- */
1157:     PetscCall(DMHasLabel(dm, "Face Sets", &hasLabel));
1158:     if (hasLabel) {
1159:       PetscInt        i, j, fs, fsSize;
1160:       const PetscInt *fsIdx, *faces;
1161:       IS              fsIS, stratumIS;
1162:       DMLabel         fsLabel;
1163:       PetscInt        numPoints, *points;
1164:       PetscInt        elem_list_size = 0;
1165:       PetscInt       *elem_list, *elem_ind, *side_list;

1167:       PetscCall(DMGetLabel(dm, "Face Sets", &fsLabel));
1168:       /* Compute size of Node List and Element List */
1169:       PetscCall(DMLabelGetValueIS(fsLabel, &fsIS));
1170:       PetscCall(ISGetIndices(fsIS, &fsIdx));
1171:       for (fs = 0; fs < num_fs; ++fs) {
1172:         PetscCall(DMLabelGetStratumIS(fsLabel, fsIdx[fs], &stratumIS));
1173:         PetscCall(ISGetSize(stratumIS, &fsSize));
1174:         elem_list_size += fsSize;
1175:         PetscCall(ISDestroy(&stratumIS));
1176:       }
1177:       if (num_fs) {
1178:         PetscCall(PetscMalloc3(num_fs, &elem_ind, elem_list_size, &elem_list, elem_list_size, &side_list));
1179:         elem_ind[0] = 0;
1180:         for (fs = 0; fs < num_fs; ++fs) {
1181:           PetscCall(DMLabelGetStratumIS(fsLabel, fsIdx[fs], &stratumIS));
1182:           PetscCall(ISGetIndices(stratumIS, &faces));
1183:           PetscCall(ISGetSize(stratumIS, &fsSize));
1184:           /* Set Parameters */
1185:           PetscCallExternal(ex_put_set_param, exo->exoid, EX_SIDE_SET, fsIdx[fs], fsSize, 0);
1186:           /* Indices */
1187:           if (fs < num_fs - 1) elem_ind[fs + 1] = elem_ind[fs] + fsSize;

1189:           for (i = 0; i < fsSize; ++i) {
1190:             /* Element List */
1191:             points = NULL;
1192:             PetscCall(DMPlexGetTransitiveClosure(dm, faces[i], PETSC_FALSE, &numPoints, &points));
1193:             elem_list[elem_ind[fs] + i] = points[2] + 1;
1194:             PetscCall(DMPlexRestoreTransitiveClosure(dm, faces[i], PETSC_FALSE, &numPoints, &points));

1196:             /* Side List */
1197:             points = NULL;
1198:             PetscCall(DMPlexGetTransitiveClosure(dm, elem_list[elem_ind[fs] + i] - 1, PETSC_TRUE, &numPoints, &points));
1199:             for (j = 1; j < numPoints; ++j) {
1200:               if (points[j * 2] == faces[i]) break;
1201:             }
1202:             /* Convert HEX sides */
1203:             if (numPoints == 27) {
1204:               if (j == 1) {
1205:                 j = 5;
1206:               } else if (j == 2) {
1207:                 j = 6;
1208:               } else if (j == 3) {
1209:                 j = 1;
1210:               } else if (j == 4) {
1211:                 j = 3;
1212:               } else if (j == 5) {
1213:                 j = 2;
1214:               } else if (j == 6) {
1215:                 j = 4;
1216:               }
1217:             }
1218:             /* Convert TET sides */
1219:             if (numPoints == 15) {
1220:               --j;
1221:               if (j == 0) j = 4;
1222:             }
1223:             side_list[elem_ind[fs] + i] = j;
1224:             PetscCall(DMPlexRestoreTransitiveClosure(dm, elem_list[elem_ind[fs] + i] - 1, PETSC_TRUE, &numPoints, &points));
1225:           }
1226:           PetscCall(ISRestoreIndices(stratumIS, &faces));
1227:           PetscCall(ISDestroy(&stratumIS));
1228:         }
1229:         PetscCall(ISRestoreIndices(fsIS, &fsIdx));
1230:         PetscCall(ISDestroy(&fsIS));

1232:         /* Put side sets */
1233:         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]]);
1234:         PetscCall(PetscFree3(elem_ind, elem_list, side_list));
1235:       }
1236:     }
1237:     /*
1238:       close the exodus file
1239:     */
1240:     ex_close(exo->exoid);
1241:     exo->exoid = -1;
1242:   }
1243:   PetscCall(PetscSectionDestroy(&coordSection));

1245:   /*
1246:     reopen the file in parallel
1247:   */
1248:   EXO_mode = EX_WRITE;
1249: #if defined(PETSC_USE_64BIT_INDICES)
1250:   EXO_mode += EX_ALL_INT64_API;
1251: #endif
1252:   CPU_word_size = sizeof(PetscReal);
1253:   IO_word_size  = sizeof(PetscReal);
1254:   exo->exoid    = ex_open_par(exo->filename, EXO_mode, &CPU_word_size, &IO_word_size, &EXO_version, comm, MPI_INFO_NULL);
1255:   PetscCheck(exo->exoid >= 0, PETSC_COMM_SELF, PETSC_ERR_LIB, "ex_open_par failed for %s", exo->filename);
1256:   PetscFunctionReturn(PETSC_SUCCESS);
1257: }

1259: static PetscErrorCode VecViewPlex_ExodusII_Nodal_Internal(Vec v, PetscExodusIIInt exoid, PetscExodusIIInt step, PetscExodusIIInt offset);
1260: static PetscErrorCode VecViewPlex_ExodusII_Zonal_Internal(Vec v, PetscExodusIIInt exoid, PetscExodusIIInt step, PetscExodusIIInt offset);
1261: static PetscErrorCode VecLoadPlex_ExodusII_Nodal_Internal(Vec v, PetscExodusIIInt exoid, PetscExodusIIInt step, PetscExodusIIInt offset);
1262: static PetscErrorCode VecLoadPlex_ExodusII_Zonal_Internal(Vec v, PetscExodusIIInt exoid, PetscExodusIIInt step, PetscExodusIIInt offset);

1264: PetscErrorCode VecView_PlexExodusII_Internal(Vec v, PetscViewer viewer)
1265: {
1266:   DM          dm;
1267:   MPI_Comm    comm;
1268:   PetscMPIInt rank;

1270:   PetscExodusIIInt exoid, offsetN = -1, offsetZ = -1;
1271:   const char      *vecname;
1272:   PetscInt         step;

1274:   PetscFunctionBegin;
1275:   PetscCall(PetscObjectGetComm((PetscObject)v, &comm));
1276:   PetscCallMPI(MPI_Comm_rank(comm, &rank));
1277:   PetscCall(PetscViewerExodusIIGetId(viewer, &exoid));
1278:   PetscCall(VecGetDM(v, &dm));
1279:   PetscCall(PetscObjectGetName((PetscObject)v, &vecname));

1281:   PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
1282:   PetscCall(PetscViewerExodusIIGetNodalVariableIndex(viewer, vecname, &offsetN));
1283:   PetscCall(PetscViewerExodusIIGetZonalVariableIndex(viewer, vecname, &offsetZ));
1284:   PetscCheck(!(offsetN >= 0 && offsetZ >= 0), comm, PETSC_ERR_FILE_UNEXPECTED, "Found both nodal and zonal variable %s in exodus file. ", vecname);
1285:   if (offsetN >= 0) {
1286:     PetscCall(VecViewPlex_ExodusII_Nodal_Internal(v, exoid, (PetscExodusIIInt)step + 1, offsetN + 1));
1287:   } else if (offsetZ >= 0) {
1288:     PetscCall(VecViewPlex_ExodusII_Zonal_Internal(v, exoid, (PetscExodusIIInt)step + 1, offsetZ + 1));
1289:   } else SETERRQ(comm, PETSC_ERR_FILE_UNEXPECTED, "Could not find nodal or zonal variable %s in exodus file. ", vecname);
1290:   PetscFunctionReturn(PETSC_SUCCESS);
1291: }

1293: PetscErrorCode VecLoad_PlexExodusII_Internal(Vec v, PetscViewer viewer)
1294: {
1295:   DM          dm;
1296:   MPI_Comm    comm;
1297:   PetscMPIInt rank;

1299:   PetscExodusIIInt exoid, offsetN = 0, offsetZ = 0;
1300:   const char      *vecname;
1301:   PetscInt         step;

1303:   PetscFunctionBegin;
1304:   PetscCall(PetscObjectGetComm((PetscObject)v, &comm));
1305:   PetscCallMPI(MPI_Comm_rank(comm, &rank));
1306:   PetscCall(PetscViewerExodusIIGetId(viewer, &exoid));
1307:   PetscCall(VecGetDM(v, &dm));
1308:   PetscCall(PetscObjectGetName((PetscObject)v, &vecname));

1310:   PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
1311:   PetscCall(PetscViewerExodusIIGetNodalVariableIndex(viewer, vecname, &offsetN));
1312:   PetscCall(PetscViewerExodusIIGetZonalVariableIndex(viewer, vecname, &offsetZ));
1313:   PetscCheck(!(offsetN >= 0 && offsetZ >= 0), comm, PETSC_ERR_FILE_UNEXPECTED, "Found both nodal and zonal variable %s in exodus file. ", vecname);
1314:   if (offsetN >= 0) PetscCall(VecLoadPlex_ExodusII_Nodal_Internal(v, exoid, (PetscExodusIIInt)step + 1, offsetN + 1));
1315:   else if (offsetZ >= 0) PetscCall(VecLoadPlex_ExodusII_Zonal_Internal(v, exoid, (PetscExodusIIInt)step + 1, offsetZ + 1));
1316:   else SETERRQ(comm, PETSC_ERR_FILE_UNEXPECTED, "Could not find nodal or zonal variable %s in exodus file. ", vecname);
1317:   PetscFunctionReturn(PETSC_SUCCESS);
1318: }

1320: static PetscErrorCode VecViewPlex_ExodusII_Nodal_Internal(Vec v, PetscExodusIIInt exoid, PetscExodusIIInt step, PetscExodusIIInt offset)
1321: {
1322:   MPI_Comm           comm;
1323:   PetscMPIInt        size;
1324:   DM                 dm;
1325:   Vec                vNatural, vComp;
1326:   const PetscScalar *varray;
1327:   PetscInt           xs, xe, bs;
1328:   PetscBool          useNatural;

1330:   PetscFunctionBegin;
1331:   PetscCall(PetscObjectGetComm((PetscObject)v, &comm));
1332:   PetscCallMPI(MPI_Comm_size(comm, &size));
1333:   PetscCall(VecGetDM(v, &dm));
1334:   PetscCall(DMGetUseNatural(dm, &useNatural));
1335:   useNatural = useNatural && size > 1 ? PETSC_TRUE : PETSC_FALSE;
1336:   if (useNatural) {
1337:     PetscCall(DMPlexCreateNaturalVector(dm, &vNatural));
1338:     PetscCall(DMPlexGlobalToNaturalBegin(dm, v, vNatural));
1339:     PetscCall(DMPlexGlobalToNaturalEnd(dm, v, vNatural));
1340:   } else {
1341:     vNatural = v;
1342:   }

1344:   /* Write local chunk of the result in the exodus file
1345:      exodus stores each component of a vector-valued field as a separate variable.
1346:      We assume that they are stored sequentially */
1347:   PetscCall(VecGetOwnershipRange(vNatural, &xs, &xe));
1348:   PetscCall(VecGetBlockSize(vNatural, &bs));
1349:   if (bs == 1) {
1350:     PetscCall(VecGetArrayRead(vNatural, &varray));
1351:     PetscCallExternal(ex_put_partial_var, exoid, step, EX_NODAL, offset, 1, xs + 1, xe - xs, varray);
1352:     PetscCall(VecRestoreArrayRead(vNatural, &varray));
1353:   } else {
1354:     IS       compIS;
1355:     PetscInt c;

1357:     PetscCall(ISCreateStride(comm, (xe - xs) / bs, xs, bs, &compIS));
1358:     for (c = 0; c < bs; ++c) {
1359:       PetscCall(ISStrideSetStride(compIS, (xe - xs) / bs, xs + c, bs));
1360:       PetscCall(VecGetSubVector(vNatural, compIS, &vComp));
1361:       PetscCall(VecGetArrayRead(vComp, &varray));
1362:       PetscCallExternal(ex_put_partial_var, exoid, step, EX_NODAL, offset + c, 1, xs / bs + 1, (xe - xs) / bs, varray);
1363:       PetscCall(VecRestoreArrayRead(vComp, &varray));
1364:       PetscCall(VecRestoreSubVector(vNatural, compIS, &vComp));
1365:     }
1366:     PetscCall(ISDestroy(&compIS));
1367:   }
1368:   if (useNatural) PetscCall(VecDestroy(&vNatural));
1369:   PetscFunctionReturn(PETSC_SUCCESS);
1370: }

1372: static PetscErrorCode VecLoadPlex_ExodusII_Nodal_Internal(Vec v, PetscExodusIIInt exoid, PetscExodusIIInt step, PetscExodusIIInt offset)
1373: {
1374:   MPI_Comm     comm;
1375:   PetscMPIInt  size;
1376:   DM           dm;
1377:   Vec          vNatural, vComp;
1378:   PetscScalar *varray;
1379:   PetscInt     xs, xe, bs;
1380:   PetscBool    useNatural;

1382:   PetscFunctionBegin;
1383:   PetscCall(PetscObjectGetComm((PetscObject)v, &comm));
1384:   PetscCallMPI(MPI_Comm_size(comm, &size));
1385:   PetscCall(VecGetDM(v, &dm));
1386:   PetscCall(DMGetUseNatural(dm, &useNatural));
1387:   useNatural = useNatural && size > 1 ? PETSC_TRUE : PETSC_FALSE;
1388:   if (useNatural) PetscCall(DMPlexCreateNaturalVector(dm, &vNatural));
1389:   else vNatural = v;

1391:   /* Read local chunk from the file */
1392:   PetscCall(VecGetOwnershipRange(vNatural, &xs, &xe));
1393:   PetscCall(VecGetBlockSize(vNatural, &bs));
1394:   if (bs == 1) {
1395:     PetscCall(VecGetArray(vNatural, &varray));
1396:     PetscCallExternal(ex_get_partial_var, exoid, step, EX_NODAL, offset, 1, xs + 1, xe - xs, varray);
1397:     PetscCall(VecRestoreArray(vNatural, &varray));
1398:   } else {
1399:     IS       compIS;
1400:     PetscInt c;

1402:     PetscCall(ISCreateStride(comm, (xe - xs) / bs, xs, bs, &compIS));
1403:     for (c = 0; c < bs; ++c) {
1404:       PetscCall(ISStrideSetStride(compIS, (xe - xs) / bs, xs + c, bs));
1405:       PetscCall(VecGetSubVector(vNatural, compIS, &vComp));
1406:       PetscCall(VecGetArray(vComp, &varray));
1407:       PetscCallExternal(ex_get_partial_var, exoid, step, EX_NODAL, offset + c, 1, xs / bs + 1, (xe - xs) / bs, varray);
1408:       PetscCall(VecRestoreArray(vComp, &varray));
1409:       PetscCall(VecRestoreSubVector(vNatural, compIS, &vComp));
1410:     }
1411:     PetscCall(ISDestroy(&compIS));
1412:   }
1413:   if (useNatural) {
1414:     PetscCall(DMPlexNaturalToGlobalBegin(dm, vNatural, v));
1415:     PetscCall(DMPlexNaturalToGlobalEnd(dm, vNatural, v));
1416:     PetscCall(VecDestroy(&vNatural));
1417:   }
1418:   PetscFunctionReturn(PETSC_SUCCESS);
1419: }

1421: static PetscErrorCode VecViewPlex_ExodusII_Zonal_Internal(Vec v, PetscExodusIIInt exoid, PetscExodusIIInt step, PetscExodusIIInt offset)
1422: {
1423:   MPI_Comm           comm;
1424:   PetscMPIInt        size;
1425:   DM                 dm;
1426:   Vec                vNatural, vComp;
1427:   const PetscScalar *varray;
1428:   PetscInt           xs, xe, bs;
1429:   PetscBool          useNatural;
1430:   IS                 compIS;
1431:   PetscInt          *csSize, *csID;
1432:   PetscExodusIIInt   numCS, set, csxs = 0;

1434:   PetscFunctionBegin;
1435:   PetscCall(PetscObjectGetComm((PetscObject)v, &comm));
1436:   PetscCallMPI(MPI_Comm_size(comm, &size));
1437:   PetscCall(VecGetDM(v, &dm));
1438:   PetscCall(DMGetUseNatural(dm, &useNatural));
1439:   useNatural = useNatural && size > 1 ? PETSC_TRUE : PETSC_FALSE;
1440:   if (useNatural) {
1441:     PetscCall(DMPlexCreateNaturalVector(dm, &vNatural));
1442:     PetscCall(DMPlexGlobalToNaturalBegin(dm, v, vNatural));
1443:     PetscCall(DMPlexGlobalToNaturalEnd(dm, v, vNatural));
1444:   } else {
1445:     vNatural = v;
1446:   }

1448:   /* Write local chunk of the result in the exodus file
1449:      exodus stores each component of a vector-valued field as a separate variable.
1450:      We assume that they are stored sequentially
1451:      Zonal variables are accessed one element block at a time, so we loop through the cell sets,
1452:      but once the vector has been reordered to natural size, we cannot use the label information
1453:      to figure out what to save where. */
1454:   numCS = (PetscExodusIIInt)ex_inquire_int(exoid, EX_INQ_ELEM_BLK); // This is an int64_t
1455:   PetscCall(PetscMalloc2(numCS, &csID, numCS, &csSize));
1456:   PetscCallExternal(ex_get_ids, exoid, EX_ELEM_BLOCK, csID);
1457:   for (set = 0; set < numCS; ++set) {
1458:     ex_block block;

1460:     block.id   = csID[set];
1461:     block.type = EX_ELEM_BLOCK;
1462:     PetscCallExternal(ex_get_block_param, exoid, &block);
1463:     PetscCall(PetscIntCast(block.num_entry, &csSize[set])); // This is an int64_t
1464:   }
1465:   PetscCall(VecGetOwnershipRange(vNatural, &xs, &xe));
1466:   PetscCall(VecGetBlockSize(vNatural, &bs));
1467:   if (bs > 1) PetscCall(ISCreateStride(comm, (xe - xs) / bs, xs, bs, &compIS));
1468:   for (set = 0; set < numCS; set++) {
1469:     PetscInt csLocalSize, c;

1471:     /* range of indices for set setID[set]: csxs:csxs + csSize[set]-1
1472:        local slice of zonal values:         xs/bs,xm/bs-1
1473:        intersection:                        max(xs/bs,csxs),min(xm/bs-1,csxs + csSize[set]-1) */
1474:     csLocalSize = PetscMax(0, PetscMin(xe / bs, csxs + csSize[set]) - PetscMax(xs / bs, csxs));
1475:     if (bs == 1) {
1476:       PetscCall(VecGetArrayRead(vNatural, &varray));
1477:       PetscCallExternal(ex_put_partial_var, exoid, step, EX_ELEM_BLOCK, offset, csID[set], PetscMax(xs - csxs, 0) + 1, csLocalSize, &varray[PetscMax(0, csxs - xs)]);
1478:       PetscCall(VecRestoreArrayRead(vNatural, &varray));
1479:     } else {
1480:       for (c = 0; c < bs; ++c) {
1481:         PetscCall(ISStrideSetStride(compIS, (xe - xs) / bs, xs + c, bs));
1482:         PetscCall(VecGetSubVector(vNatural, compIS, &vComp));
1483:         PetscCall(VecGetArrayRead(vComp, &varray));
1484:         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)]);
1485:         PetscCall(VecRestoreArrayRead(vComp, &varray));
1486:         PetscCall(VecRestoreSubVector(vNatural, compIS, &vComp));
1487:       }
1488:     }
1489:     csxs += csSize[set];
1490:   }
1491:   PetscCall(PetscFree2(csID, csSize));
1492:   if (bs > 1) PetscCall(ISDestroy(&compIS));
1493:   if (useNatural) PetscCall(VecDestroy(&vNatural));
1494:   PetscFunctionReturn(PETSC_SUCCESS);
1495: }

1497: static PetscErrorCode VecLoadPlex_ExodusII_Zonal_Internal(Vec v, PetscExodusIIInt exoid, PetscExodusIIInt step, PetscExodusIIInt offset)
1498: {
1499:   MPI_Comm         comm;
1500:   PetscMPIInt      size;
1501:   DM               dm;
1502:   Vec              vNatural, vComp;
1503:   PetscScalar     *varray;
1504:   PetscInt         xs, xe, bs;
1505:   PetscBool        useNatural;
1506:   IS               compIS;
1507:   PetscInt        *csSize, *csID;
1508:   PetscExodusIIInt numCS, set, csxs = 0;

1510:   PetscFunctionBegin;
1511:   PetscCall(PetscObjectGetComm((PetscObject)v, &comm));
1512:   PetscCallMPI(MPI_Comm_size(comm, &size));
1513:   PetscCall(VecGetDM(v, &dm));
1514:   PetscCall(DMGetUseNatural(dm, &useNatural));
1515:   useNatural = useNatural && size > 1 ? PETSC_TRUE : PETSC_FALSE;
1516:   if (useNatural) PetscCall(DMPlexCreateNaturalVector(dm, &vNatural));
1517:   else vNatural = v;

1519:   /* Read local chunk of the result in the exodus file
1520:      exodus stores each component of a vector-valued field as a separate variable.
1521:      We assume that they are stored sequentially
1522:      Zonal variables are accessed one element block at a time, so we loop through the cell sets,
1523:      but once the vector has been reordered to natural size, we cannot use the label information
1524:      to figure out what to save where. */
1525:   numCS = (PetscExodusIIInt)ex_inquire_int(exoid, EX_INQ_ELEM_BLK); // This is an int64_t
1526:   PetscCall(PetscMalloc2(numCS, &csID, numCS, &csSize));
1527:   PetscCallExternal(ex_get_ids, exoid, EX_ELEM_BLOCK, csID);
1528:   for (set = 0; set < numCS; ++set) {
1529:     ex_block block;

1531:     block.id   = csID[set];
1532:     block.type = EX_ELEM_BLOCK;
1533:     PetscCallExternal(ex_get_block_param, exoid, &block);
1534:     PetscCall(PetscIntCast(block.num_entry, &csSize[set])); // This is an int64_t
1535:   }
1536:   PetscCall(VecGetOwnershipRange(vNatural, &xs, &xe));
1537:   PetscCall(VecGetBlockSize(vNatural, &bs));
1538:   if (bs > 1) PetscCall(ISCreateStride(comm, (xe - xs) / bs, xs, bs, &compIS));
1539:   for (set = 0; set < numCS; ++set) {
1540:     PetscInt csLocalSize, c;

1542:     /* range of indices for set setID[set]: csxs:csxs + csSize[set]-1
1543:        local slice of zonal values:         xs/bs,xm/bs-1
1544:        intersection:                        max(xs/bs,csxs),min(xm/bs-1,csxs + csSize[set]-1) */
1545:     csLocalSize = PetscMax(0, PetscMin(xe / bs, csxs + csSize[set]) - PetscMax(xs / bs, csxs));
1546:     if (bs == 1) {
1547:       PetscCall(VecGetArray(vNatural, &varray));
1548:       PetscCallExternal(ex_get_partial_var, exoid, step, EX_ELEM_BLOCK, offset, csID[set], PetscMax(xs - csxs, 0) + 1, csLocalSize, &varray[PetscMax(0, csxs - xs)]);
1549:       PetscCall(VecRestoreArray(vNatural, &varray));
1550:     } else {
1551:       for (c = 0; c < bs; ++c) {
1552:         PetscCall(ISStrideSetStride(compIS, (xe - xs) / bs, xs + c, bs));
1553:         PetscCall(VecGetSubVector(vNatural, compIS, &vComp));
1554:         PetscCall(VecGetArray(vComp, &varray));
1555:         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)]);
1556:         PetscCall(VecRestoreArray(vComp, &varray));
1557:         PetscCall(VecRestoreSubVector(vNatural, compIS, &vComp));
1558:       }
1559:     }
1560:     csxs += csSize[set];
1561:   }
1562:   PetscCall(PetscFree2(csID, csSize));
1563:   if (bs > 1) PetscCall(ISDestroy(&compIS));
1564:   if (useNatural) {
1565:     PetscCall(DMPlexNaturalToGlobalBegin(dm, vNatural, v));
1566:     PetscCall(DMPlexNaturalToGlobalEnd(dm, vNatural, v));
1567:     PetscCall(VecDestroy(&vNatural));
1568:   }
1569:   PetscFunctionReturn(PETSC_SUCCESS);
1570: }

1572: static PetscErrorCode ExodusGetCellType_Internal(const char *elem_type, DMPolytopeType *ct)
1573: {
1574:   PetscBool flg;

1576:   PetscFunctionBegin;
1577:   *ct = DM_POLYTOPE_UNKNOWN;
1578:   PetscCall(PetscStrcmp(elem_type, "BAR2", &flg));
1579:   if (flg) {
1580:     *ct = DM_POLYTOPE_SEGMENT;
1581:     goto done;
1582:   }
1583:   PetscCall(PetscStrcmp(elem_type, "BAR3", &flg));
1584:   if (flg) {
1585:     *ct = DM_POLYTOPE_SEGMENT;
1586:     goto done;
1587:   }
1588:   PetscCall(PetscStrcmp(elem_type, "TRI", &flg));
1589:   if (flg) {
1590:     *ct = DM_POLYTOPE_TRIANGLE;
1591:     goto done;
1592:   }
1593:   PetscCall(PetscStrcmp(elem_type, "TRI3", &flg));
1594:   if (flg) {
1595:     *ct = DM_POLYTOPE_TRIANGLE;
1596:     goto done;
1597:   }
1598:   PetscCall(PetscStrcmp(elem_type, "QUAD", &flg));
1599:   if (flg) {
1600:     *ct = DM_POLYTOPE_QUADRILATERAL;
1601:     goto done;
1602:   }
1603:   PetscCall(PetscStrcmp(elem_type, "QUAD4", &flg));
1604:   if (flg) {
1605:     *ct = DM_POLYTOPE_QUADRILATERAL;
1606:     goto done;
1607:   }
1608:   PetscCall(PetscStrcmp(elem_type, "SHELL4", &flg));
1609:   if (flg) {
1610:     *ct = DM_POLYTOPE_QUADRILATERAL;
1611:     goto done;
1612:   }
1613:   PetscCall(PetscStrcmp(elem_type, "TETRA", &flg));
1614:   if (flg) {
1615:     *ct = DM_POLYTOPE_TETRAHEDRON;
1616:     goto done;
1617:   }
1618:   PetscCall(PetscStrcmp(elem_type, "TET4", &flg));
1619:   if (flg) {
1620:     *ct = DM_POLYTOPE_TETRAHEDRON;
1621:     goto done;
1622:   }
1623:   PetscCall(PetscStrcmp(elem_type, "WEDGE", &flg));
1624:   if (flg) {
1625:     *ct = DM_POLYTOPE_TRI_PRISM;
1626:     goto done;
1627:   }
1628:   PetscCall(PetscStrcmp(elem_type, "HEX", &flg));
1629:   if (flg) {
1630:     *ct = DM_POLYTOPE_HEXAHEDRON;
1631:     goto done;
1632:   }
1633:   PetscCall(PetscStrcmp(elem_type, "HEX8", &flg));
1634:   if (flg) {
1635:     *ct = DM_POLYTOPE_HEXAHEDRON;
1636:     goto done;
1637:   }
1638:   PetscCall(PetscStrcmp(elem_type, "HEXAHEDRON", &flg));
1639:   if (flg) {
1640:     *ct = DM_POLYTOPE_HEXAHEDRON;
1641:     goto done;
1642:   }
1643:   SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unrecognized element type %s", elem_type);
1644: done:
1645:   PetscFunctionReturn(PETSC_SUCCESS);
1646: }

1648: /*@
1649:   DMPlexCreateExodus - Create a `DMPLEX` mesh from an ExodusII file ID.

1651:   Collective

1653:   Input Parameters:
1654: + comm        - The MPI communicator
1655: . exoid       - The ExodusII id associated with a exodus file and obtained using ex_open
1656: - interpolate - Create faces and edges in the mesh

1658:   Output Parameter:
1659: . dm - The `DM` object representing the mesh

1661:   Level: beginner

1663: .seealso: [](ch_unstructured), `DM`, `PETSCVIEWEREXODUSII`, `DMPLEX`, `DMCreate()`
1664: @*/
1665: PetscErrorCode DMPlexCreateExodus(MPI_Comm comm, PetscExodusIIInt exoid, PetscBool interpolate, DM *dm)
1666: {
1667:   PetscMPIInt  num_proc, rank;
1668:   DMLabel      cellSets = NULL, faceSets = NULL, vertSets = NULL;
1669:   PetscSection coordSection;
1670:   Vec          coordinates;
1671:   PetscScalar *coords;
1672:   PetscInt     coordSize, v;
1673:   /* Read from ex_get_init() */
1674:   char title[PETSC_MAX_PATH_LEN + 1];
1675:   int  dim = 0, dimEmbed = 0, numVertices = 0, numCells = 0;
1676:   int  num_cs = 0, num_vs = 0, num_fs = 0;

1678:   PetscFunctionBegin;
1679:   PetscCallMPI(MPI_Comm_rank(comm, &rank));
1680:   PetscCallMPI(MPI_Comm_size(comm, &num_proc));
1681:   PetscCall(DMCreate(comm, dm));
1682:   PetscCall(DMSetType(*dm, DMPLEX));
1683:   /* Open EXODUS II file and read basic information on rank 0, then broadcast to all processors */
1684:   if (rank == 0) {
1685:     PetscCall(PetscMemzero(title, PETSC_MAX_PATH_LEN + 1));
1686:     PetscCallExternal(ex_get_init, exoid, title, &dimEmbed, &numVertices, &numCells, &num_cs, &num_vs, &num_fs);
1687:     PetscCheck(num_cs, PETSC_COMM_SELF, PETSC_ERR_SUP, "Exodus file does not contain any cell set");
1688:   }
1689:   PetscCallMPI(MPI_Bcast(title, PETSC_MAX_PATH_LEN + 1, MPI_CHAR, 0, comm));
1690:   PetscCallMPI(MPI_Bcast(&dim, 1, MPI_INT, 0, comm));
1691:   PetscCall(PetscObjectSetName((PetscObject)*dm, title));
1692:   PetscCall(DMPlexSetChart(*dm, 0, numCells + numVertices));
1693:   /*   We do not want this label automatically computed, instead we compute it here */
1694:   PetscCall(DMCreateLabel(*dm, "celltype"));

1696:   /* Read cell sets information */
1697:   if (rank == 0) {
1698:     PetscInt *cone;
1699:     int       c, cs, ncs, c_loc, v, v_loc;
1700:     /* Read from ex_get_elem_blk_ids() */
1701:     int *cs_id, *cs_order;
1702:     /* Read from ex_get_elem_block() */
1703:     char buffer[PETSC_MAX_PATH_LEN + 1];
1704:     int  num_cell_in_set, num_vertex_per_cell, num_hybrid, num_attr;
1705:     /* Read from ex_get_elem_conn() */
1706:     int *cs_connect;

1708:     /* Get cell sets IDs */
1709:     PetscCall(PetscMalloc2(num_cs, &cs_id, num_cs, &cs_order));
1710:     PetscCallExternal(ex_get_ids, exoid, EX_ELEM_BLOCK, cs_id);
1711:     /* Read the cell set connectivity table and build mesh topology
1712:        EXO standard requires that cells in cell sets be numbered sequentially and be pairwise disjoint. */
1713:     /* Check for a hybrid mesh */
1714:     for (cs = 0, num_hybrid = 0; cs < num_cs; ++cs) {
1715:       DMPolytopeType ct;
1716:       char           elem_type[PETSC_MAX_PATH_LEN];

1718:       PetscCall(PetscArrayzero(elem_type, sizeof(elem_type)));
1719:       PetscCallExternal(ex_get_elem_type, exoid, cs_id[cs], elem_type);
1720:       PetscCall(ExodusGetCellType_Internal(elem_type, &ct));
1721:       dim = PetscMax(dim, DMPolytopeTypeGetDim(ct));
1722:       PetscCallExternal(ex_get_block, exoid, EX_ELEM_BLOCK, cs_id[cs], buffer, &num_cell_in_set, &num_vertex_per_cell, 0, 0, &num_attr);
1723:       switch (ct) {
1724:       case DM_POLYTOPE_TRI_PRISM:
1725:         cs_order[cs] = cs;
1726:         ++num_hybrid;
1727:         break;
1728:       default:
1729:         for (c = cs; c > cs - num_hybrid; --c) cs_order[c] = cs_order[c - 1];
1730:         cs_order[cs - num_hybrid] = cs;
1731:       }
1732:     }
1733:     /* First set sizes */
1734:     for (ncs = 0, c = 0; ncs < num_cs; ++ncs) {
1735:       DMPolytopeType ct;
1736:       char           elem_type[PETSC_MAX_PATH_LEN];
1737:       const PetscInt cs = cs_order[ncs];

1739:       PetscCall(PetscArrayzero(elem_type, sizeof(elem_type)));
1740:       PetscCallExternal(ex_get_elem_type, exoid, cs_id[cs], elem_type);
1741:       PetscCall(ExodusGetCellType_Internal(elem_type, &ct));
1742:       PetscCallExternal(ex_get_block, exoid, EX_ELEM_BLOCK, cs_id[cs], buffer, &num_cell_in_set, &num_vertex_per_cell, 0, 0, &num_attr);
1743:       for (c_loc = 0; c_loc < num_cell_in_set; ++c_loc, ++c) {
1744:         PetscCall(DMPlexSetConeSize(*dm, c, num_vertex_per_cell));
1745:         PetscCall(DMPlexSetCellType(*dm, c, ct));
1746:       }
1747:     }
1748:     for (v = numCells; v < numCells + numVertices; ++v) PetscCall(DMPlexSetCellType(*dm, v, DM_POLYTOPE_POINT));
1749:     PetscCall(DMSetUp(*dm));
1750:     for (ncs = 0, c = 0; ncs < num_cs; ++ncs) {
1751:       const PetscInt cs = cs_order[ncs];
1752:       PetscCallExternal(ex_get_block, exoid, EX_ELEM_BLOCK, cs_id[cs], buffer, &num_cell_in_set, &num_vertex_per_cell, 0, 0, &num_attr);
1753:       PetscCall(PetscMalloc2(num_vertex_per_cell * num_cell_in_set, &cs_connect, num_vertex_per_cell, &cone));
1754:       PetscCallExternal(ex_get_conn, exoid, EX_ELEM_BLOCK, cs_id[cs], cs_connect, NULL, NULL);
1755:       /* EXO uses Fortran-based indexing, DMPlex uses C-style and numbers cell first then vertices. */
1756:       for (c_loc = 0, v = 0; c_loc < num_cell_in_set; ++c_loc, ++c) {
1757:         DMPolytopeType ct;

1759:         for (v_loc = 0; v_loc < num_vertex_per_cell; ++v_loc, ++v) cone[v_loc] = cs_connect[v] + numCells - 1;
1760:         PetscCall(DMPlexGetCellType(*dm, c, &ct));
1761:         PetscCall(DMPlexInvertCell(ct, cone));
1762:         PetscCall(DMPlexSetCone(*dm, c, cone));
1763:         PetscCall(DMSetLabelValue_Fast(*dm, &cellSets, "Cell Sets", c, cs_id[cs]));
1764:       }
1765:       PetscCall(PetscFree2(cs_connect, cone));
1766:     }
1767:     PetscCall(PetscFree2(cs_id, cs_order));
1768:   }
1769:   {
1770:     PetscInt ints[] = {dim, dimEmbed};

1772:     PetscCallMPI(MPI_Bcast(ints, 2, MPIU_INT, 0, comm));
1773:     PetscCall(DMSetDimension(*dm, ints[0]));
1774:     PetscCall(DMSetCoordinateDim(*dm, ints[1]));
1775:     dim      = ints[0];
1776:     dimEmbed = ints[1];
1777:   }
1778:   PetscCall(DMPlexSymmetrize(*dm));
1779:   PetscCall(DMPlexStratify(*dm));
1780:   if (interpolate) {
1781:     DM idm;

1783:     PetscCall(DMPlexInterpolate(*dm, &idm));
1784:     PetscCall(DMDestroy(dm));
1785:     *dm = idm;
1786:   }

1788:   /* Create vertex set label */
1789:   if (rank == 0 && (num_vs > 0)) {
1790:     int vs, v;
1791:     /* Read from ex_get_node_set_ids() */
1792:     int *vs_id;
1793:     /* Read from ex_get_node_set_param() */
1794:     int num_vertex_in_set;
1795:     /* Read from ex_get_node_set() */
1796:     int *vs_vertex_list;

1798:     /* Get vertex set ids */
1799:     PetscCall(PetscMalloc1(num_vs, &vs_id));
1800:     PetscCallExternal(ex_get_ids, exoid, EX_NODE_SET, vs_id);
1801:     for (vs = 0; vs < num_vs; ++vs) {
1802:       PetscCallExternal(ex_get_set_param, exoid, EX_NODE_SET, vs_id[vs], &num_vertex_in_set, NULL);
1803:       PetscCall(PetscMalloc1(num_vertex_in_set, &vs_vertex_list));
1804:       PetscCallExternal(ex_get_set, exoid, EX_NODE_SET, vs_id[vs], vs_vertex_list, NULL);
1805:       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]));
1806:       PetscCall(PetscFree(vs_vertex_list));
1807:     }
1808:     PetscCall(PetscFree(vs_id));
1809:   }
1810:   /* Read coordinates */
1811:   PetscCall(DMGetCoordinateSection(*dm, &coordSection));
1812:   PetscCall(PetscSectionSetNumFields(coordSection, 1));
1813:   PetscCall(PetscSectionSetFieldComponents(coordSection, 0, dimEmbed));
1814:   PetscCall(PetscSectionSetChart(coordSection, numCells, numCells + numVertices));
1815:   for (v = numCells; v < numCells + numVertices; ++v) {
1816:     PetscCall(PetscSectionSetDof(coordSection, v, dimEmbed));
1817:     PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, dimEmbed));
1818:   }
1819:   PetscCall(PetscSectionSetUp(coordSection));
1820:   PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
1821:   PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates));
1822:   PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
1823:   PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
1824:   PetscCall(VecSetBlockSize(coordinates, dimEmbed));
1825:   PetscCall(VecSetType(coordinates, VECSTANDARD));
1826:   PetscCall(VecGetArray(coordinates, &coords));
1827:   if (rank == 0) {
1828:     PetscReal *x, *y, *z;

1830:     PetscCall(PetscMalloc3(numVertices, &x, numVertices, &y, numVertices, &z));
1831:     PetscCallExternal(ex_get_coord, exoid, x, y, z);
1832:     if (dimEmbed > 0) {
1833:       for (v = 0; v < numVertices; ++v) coords[v * dimEmbed + 0] = x[v];
1834:     }
1835:     if (dimEmbed > 1) {
1836:       for (v = 0; v < numVertices; ++v) coords[v * dimEmbed + 1] = y[v];
1837:     }
1838:     if (dimEmbed > 2) {
1839:       for (v = 0; v < numVertices; ++v) coords[v * dimEmbed + 2] = z[v];
1840:     }
1841:     PetscCall(PetscFree3(x, y, z));
1842:   }
1843:   PetscCall(VecRestoreArray(coordinates, &coords));
1844:   PetscCall(DMSetCoordinatesLocal(*dm, coordinates));
1845:   PetscCall(VecDestroy(&coordinates));

1847:   /* Create side set label */
1848:   if (rank == 0 && interpolate && (num_fs > 0)) {
1849:     int fs, f, voff;
1850:     /* Read from ex_get_side_set_ids() */
1851:     int *fs_id;
1852:     /* Read from ex_get_side_set_param() */
1853:     int num_side_in_set;
1854:     /* Read from ex_get_side_set_node_list() */
1855:     int *fs_vertex_count_list, *fs_vertex_list, *fs_side_list;
1856:     /* Read side set labels */
1857:     char   fs_name[MAX_STR_LENGTH + 1];
1858:     size_t fs_name_len;

1860:     /* Get side set ids */
1861:     PetscCall(PetscMalloc1(num_fs, &fs_id));
1862:     PetscCallExternal(ex_get_ids, exoid, EX_SIDE_SET, fs_id);
1863:     // Ids 1 and 2 are reserved by ExodusII for indicating things in 3D
1864:     for (fs = 0; fs < num_fs; ++fs) {
1865:       PetscCallExternal(ex_get_set_param, exoid, EX_SIDE_SET, fs_id[fs], &num_side_in_set, NULL);
1866:       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));
1867:       PetscCallExternal(ex_get_side_set_node_list, exoid, fs_id[fs], fs_vertex_count_list, fs_vertex_list);
1868:       PetscCallExternal(ex_get_set, exoid, EX_SIDE_SET, fs_id[fs], NULL, fs_side_list);

1870:       /* Get the specific name associated with this side set ID. */
1871:       int fs_name_err = ex_get_name(exoid, EX_SIDE_SET, fs_id[fs], fs_name);
1872:       if (!fs_name_err) {
1873:         PetscCall(PetscStrlen(fs_name, &fs_name_len));
1874:         if (fs_name_len == 0) PetscCall(PetscStrncpy(fs_name, "Face Sets", MAX_STR_LENGTH));
1875:       }
1876:       for (f = 0, voff = 0; f < num_side_in_set; ++f) {
1877:         const PetscInt *faces    = NULL;
1878:         PetscInt        faceSize = fs_vertex_count_list[f], numFaces;
1879:         PetscInt        faceVertices[4], v;

1881:         PetscCheck(faceSize <= 4, comm, PETSC_ERR_ARG_WRONG, "ExodusII side cannot have %" PetscInt_FMT " > 4 vertices", faceSize);
1882:         for (v = 0; v < faceSize; ++v, ++voff) faceVertices[v] = fs_vertex_list[voff] + numCells - 1;
1883:         PetscCall(DMPlexGetFullJoin(*dm, faceSize, faceVertices, &numFaces, &faces));
1884:         PetscCheck(numFaces == 1, comm, PETSC_ERR_ARG_WRONG, "Invalid ExodusII side %d in set %d maps to %" PetscInt_FMT " faces", f, fs, numFaces);
1885:         PetscCheck(dim == 1 || faces[0] >= numCells + numVertices, comm, PETSC_ERR_ARG_WRONG, "Invalid ExodusII side %d in set %d maps to point %" PetscInt_FMT " which is not a face", f, fs, faces[0]);
1886:         PetscCall(DMSetLabelValue_Fast(*dm, &faceSets, "Face Sets", faces[0], fs_id[fs]));
1887:         /* Only add the label if one has been detected for this side set. */
1888:         if (!fs_name_err) PetscCall(DMSetLabelValue(*dm, fs_name, faces[0], fs_id[fs]));
1889:         PetscCall(DMPlexRestoreJoin(*dm, faceSize, faceVertices, &numFaces, &faces));
1890:       }
1891:       PetscCall(PetscFree3(fs_vertex_count_list, fs_vertex_list, fs_side_list));
1892:     }
1893:     PetscCall(PetscFree(fs_id));
1894:   }

1896:   { /* Create Cell/Face/Vertex Sets labels at all processes */
1897:     enum {
1898:       n = 3
1899:     };
1900:     PetscBool flag[n];

1902:     flag[0] = cellSets ? PETSC_TRUE : PETSC_FALSE;
1903:     flag[1] = faceSets ? PETSC_TRUE : PETSC_FALSE;
1904:     flag[2] = vertSets ? PETSC_TRUE : PETSC_FALSE;
1905:     PetscCallMPI(MPI_Bcast(flag, n, MPIU_BOOL, 0, comm));
1906:     if (flag[0]) PetscCall(DMCreateLabel(*dm, "Cell Sets"));
1907:     if (flag[1]) PetscCall(DMCreateLabel(*dm, "Face Sets"));
1908:     if (flag[2]) PetscCall(DMCreateLabel(*dm, "Vertex Sets"));
1909:   }
1910:   PetscFunctionReturn(PETSC_SUCCESS);
1911: }