Date: 2025-03-28T21:10:46+00:00

Actual source code: plexexodusii2.c

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

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

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

     13:   Collective; No Fortran Support

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

     18:   Level: intermediate

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

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

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

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

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

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

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

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

     70: static PetscErrorCode PetscViewerSetUp_ExodusII(PetscViewer viewer)
     71: {
     72:   PetscFunctionBegin;
     73:   PetscFunctionReturn(PETSC_SUCCESS);
     74: }

     76: static PetscErrorCode PetscViewerDestroy_ExodusII(PetscViewer viewer)
     77: {
     78:   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;

     80:   PetscFunctionBegin;
     81:   if (exo->exoid >= 0) PetscCallExternal(ex_close, exo->exoid);
     82:   for (PetscInt i = 0; i < exo->numZonalVariables; i++) PetscCall(PetscFree(exo->zonalVariableNames[i]));
     83:   PetscCall(PetscFree(exo->zonalVariableNames));
     84:   for (PetscInt i = 0; i < exo->numNodalVariables; i++) PetscCall(PetscFree(exo->nodalVariableNames[i]));
     85:   PetscCall(PetscFree(exo->nodalVariableNames));
     86:   PetscCall(PetscFree(exo->filename));
     87:   PetscCall(PetscFree(exo));
     88:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileSetName_C", NULL));
     89:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileGetName_C", NULL));
     90:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileSetMode_C", NULL));
     91:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileGetMode_C", NULL));
     92:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGetId_C", NULL));
     93:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGetOrder_C", NULL));
     94:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerSetOrder_C", NULL));
     95:   PetscFunctionReturn(PETSC_SUCCESS);
     96: }

     98: static PetscErrorCode PetscViewerFileSetName_ExodusII(PetscViewer viewer, const char name[])
     99: {
    100:   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;
    101:   PetscMPIInt           rank;
    102:   PetscExodusIIInt      CPU_word_size, IO_word_size, EXO_mode;
    103:   MPI_Info              mpi_info = MPI_INFO_NULL;
    104:   PetscExodusIIFloat    EXO_version;

    106:   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
    107:   CPU_word_size = sizeof(PetscReal);
    108:   IO_word_size  = sizeof(PetscReal);

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

    144: static PetscErrorCode PetscViewerFileGetName_ExodusII(PetscViewer viewer, const char *name[])
    145: {
    146:   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;

    148:   PetscFunctionBegin;
    149:   *name = exo->filename;
    150:   PetscFunctionReturn(PETSC_SUCCESS);
    151: }

    153: static PetscErrorCode PetscViewerFileSetMode_ExodusII(PetscViewer viewer, PetscFileMode type)
    154: {
    155:   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;

    157:   PetscFunctionBegin;
    158:   exo->btype = type;
    159:   PetscFunctionReturn(PETSC_SUCCESS);
    160: }

    162: static PetscErrorCode PetscViewerFileGetMode_ExodusII(PetscViewer viewer, PetscFileMode *type)
    163: {
    164:   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;

    166:   PetscFunctionBegin;
    167:   *type = exo->btype;
    168:   PetscFunctionReturn(PETSC_SUCCESS);
    169: }

    171: static PetscErrorCode PetscViewerExodusIIGetId_ExodusII(PetscViewer viewer, PetscExodusIIInt *exoid)
    172: {
    173:   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;

    175:   PetscFunctionBegin;
    176:   *exoid = exo->exoid;
    177:   PetscFunctionReturn(PETSC_SUCCESS);
    178: }

    180: static PetscErrorCode PetscViewerExodusIIGetOrder_ExodusII(PetscViewer viewer, PetscInt *order)
    181: {
    182:   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;

    184:   PetscFunctionBegin;
    185:   *order = exo->order;
    186:   PetscFunctionReturn(PETSC_SUCCESS);
    187: }

    189: static PetscErrorCode PetscViewerExodusIISetOrder_ExodusII(PetscViewer viewer, PetscInt order)
    190: {
    191:   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;

    193:   PetscFunctionBegin;
    194:   exo->order = order;
    195:   PetscFunctionReturn(PETSC_SUCCESS);
    196: }

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

    201:   Collective;

    203:   Input Parameters:
    204: + viewer - a `PetscViewer` of type `PETSCVIEWEREXODUSII`
    205: - num    - the number of zonal variables in the exodusII file

    207:   Level: intermediate

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

    213: .seealso: `PETSCVIEWEREXODUSII`, `PetscViewer`, `PetscViewerCreate()`, `PetscViewerDestroy()`, `PetscViewerExodusIIOpen()`, `PetscViewerSetType()`, `PetscViewerType`, `PetscViewerExodusIIGetZonalVariable()`
    214: @*/
    215: PetscErrorCode PetscViewerExodusIISetZonalVariable(PetscViewer viewer, PetscExodusIIInt num)
    216: {
    217:   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;
    218:   MPI_Comm              comm;
    219:   PetscExodusIIInt      exoid = -1;

    221:   PetscFunctionBegin;
    222:   PetscCall(PetscObjectGetComm((PetscObject)viewer, &comm));
    223:   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);
    224:   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");

    226:   exo->numZonalVariables = num;
    227:   PetscCall(PetscMalloc1(num, &exo->zonalVariableNames));
    228:   for (int i = 0; i < num; i++) { exo->zonalVariableNames[i] = NULL; }
    229:   PetscCall(PetscViewerExodusIIGetId(viewer, &exoid));
    230:   PetscCallExternal(ex_put_variable_param, exoid, EX_ELEM_BLOCK, num);
    231:   PetscFunctionReturn(PETSC_SUCCESS);
    232: }

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

    237:   Collective;

    239:   Input Parameters:
    240: + viewer - a `PetscViewer` of type `PETSCVIEWEREXODUSII`
    241: - num    - the number of nodal variables in the exodusII file

    243:   Level: intermediate

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

    249: .seealso: `PETSCVIEWEREXODUSII`, `PetscViewer`, `PetscViewerCreate()`, `PetscViewerDestroy()`, `PetscViewerExodusIIOpen()`, `PetscViewerSetType()`, `PetscViewerType`, `PetscViewerExodusIIGetNodalVariable()`
    250: @*/
    251: PetscErrorCode PetscViewerExodusIISetNodalVariable(PetscViewer viewer, PetscExodusIIInt num)
    252: {
    253:   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;
    254:   MPI_Comm              comm;
    255:   PetscExodusIIInt      exoid = -1;

    257:   PetscFunctionBegin;
    258:   PetscCall(PetscObjectGetComm((PetscObject)viewer, &comm));
    259:   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);
    260:   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");

    262:   exo->numNodalVariables = num;
    263:   PetscCall(PetscMalloc1(num, &exo->nodalVariableNames));
    264:   for (int i = 0; i < num; i++) { exo->nodalVariableNames[i] = NULL; }
    265:   PetscCall(PetscViewerExodusIIGetId(viewer, &exoid));
    266:   PetscCallExternal(ex_put_variable_param, exoid, EX_NODAL, num);
    267:   PetscFunctionReturn(PETSC_SUCCESS);
    268: }

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

    273:   Collective

    275:   Input Parameters:
    276: . viewer - a `PetscViewer` of type `PETSCVIEWEREXODUSII`

    278:   Output Parameters:
    279: . num - the number variables in the exodusII file

    281:   Level: intermediate

    283:   Notes:
    284:   The number of variables in the exodusII file is cached in the viewer

    286: .seealso: `PETSCVIEWEREXODUSII`, `PetscViewer`, `PetscViewerCreate()`, `PetscViewerDestroy()`, `PetscViewerExodusIIOpen()`, `PetscViewerSetType()`, `PetscViewerType`, `PetscViewerExodusIIsetZonalVariable()`
    287: @*/
    288: PetscErrorCode PetscViewerExodusIIGetZonalVariable(PetscViewer viewer, PetscExodusIIInt *num)
    289: {
    290:   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;
    291:   MPI_Comm              comm;
    292:   PetscExodusIIInt      exoid = -1;

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

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

    312:   Collective

    314:   Input Parameters:
    315: . viewer - a `PetscViewer` of type `PETSCVIEWEREXODUSII`

    317:   Output Parameters:
    318: . num - the number variables in the exodusII file

    320:   Level: intermediate

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

    325: .seealso: `PETSCVIEWEREXODUSII`, `PetscViewer`, `PetscViewerCreate()`, `PetscViewerDestroy()`, `PetscViewerExodusIIOpen()`, `PetscViewerSetType()`, `PetscViewerType`, `PetscViewerExodusIISetNodalVariable()`
    326: @*/
    327: PetscErrorCode PetscViewerExodusIIGetNodalVariable(PetscViewer viewer, PetscExodusIIInt *num)
    328: {
    329:   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;
    330:   MPI_Comm              comm;
    331:   PetscExodusIIInt      exoid = -1;

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

    348: /*@
    349:   PetscViewerExodusIISetZonalVariableName - Sets the name of a zonal variable.

    351:   Collective;

    353:   Input Parameters:
    354: + viewer - a `PetscViewer` of type `PETSCVIEWEREXODUSII`
    355: . idx    - the index for which you want to save the name
    356: - name   - string containing the name characters

    358:   Level: intermediate

    360: .seealso: `PETSCVIEWEREXODUSII`, `PetscViewer`, `PetscViewerCreate()`, `PetscViewerDestroy()`, `PetscViewerExodusIIOpen()`, `PetscViewerSetType()`, `PetscViewerType`, `PetscViewerExodusIIGetZonalVariableName()`
    361: @*/
    362: PetscErrorCode PetscViewerExodusIISetZonalVariableName(PetscViewer viewer, PetscExodusIIInt idx, const char name[])
    363: {
    364:   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;

    366:   PetscFunctionBegin;
    367:   PetscCheck((idx >= 0) && (idx < exo->numZonalVariables), PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Variable index out of range. Was PetscViewerExodusIISetZonalVariable called?");
    368:   PetscCall(PetscStrallocpy(name, (char **)&exo->zonalVariableNames[idx]));
    369:   PetscCallExternal(ex_put_variable_name, exo->exoid, EX_ELEM_BLOCK, idx + 1, name);
    370:   PetscFunctionReturn(PETSC_SUCCESS);
    371: }

    373: /*@
    374:   PetscViewerExodusIISetNodalVariableName - Sets the name of a nodal variable.

    376:   Collective;

    378:   Input Parameters:
    379: + viewer - a `PetscViewer` of type `PETSCVIEWEREXODUSII`
    380: . idx    - the index for which you want to save the name
    381: - name   - string containing the name characters

    383:   Level: intermediate

    385: .seealso: `PETSCVIEWEREXODUSII`, `PetscViewer`, `PetscViewerCreate()`, `PetscViewerDestroy()`, `PetscViewerExodusIIOpen()`, `PetscViewerSetType()`, `PetscViewerType`, `PetscViewerExodusIIGetNodalVariableName()`
    386: @*/
    387: PetscErrorCode PetscViewerExodusIISetNodalVariableName(PetscViewer viewer, PetscExodusIIInt idx, const char name[])
    388: {
    389:   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;

    391:   PetscFunctionBegin;
    392:   PetscCheck((idx >= 0) && (idx < exo->numNodalVariables), PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Variable index out of range. Was PetscViewerExodusIISetNodalVariable called?");
    393:   PetscCall(PetscStrallocpy(name, (char **)&exo->nodalVariableNames[idx]));
    394:   PetscCallExternal(ex_put_variable_name, exo->exoid, EX_NODAL, idx + 1, name);
    395:   PetscFunctionReturn(PETSC_SUCCESS);
    396: }

    398: /*@
    399:   PetscViewerExodusIIGetZonalVariableName - Gets the name of a zonal variable.

    401:   Collective;

    403:   Input Parameters:
    404: + viewer - a `PetscViewer` of type `PETSCVIEWEREXODUSII`
    405: - idx    - the index for which you want to get the name

    407:   Output Parameter:
    408: . name - pointer to the string containing the name characters

    410:   Level: intermediate

    412: .seealso: `PETSCVIEWEREXODUSII`, `PetscViewer`, `PetscViewerCreate()`, `PetscViewerDestroy()`, `PetscViewerExodusIIOpen()`, `PetscViewerSetType()`, `PetscViewerType`, `PetscViewerExodusIISetZonalVariableName()`
    413: @*/
    414: PetscErrorCode PetscViewerExodusIIGetZonalVariableName(PetscViewer viewer, PetscExodusIIInt idx, const char *name[])
    415: {
    416:   PetscViewer_ExodusII *exo   = (PetscViewer_ExodusII *)viewer->data;
    417:   PetscExodusIIInt      exoid = -1;
    418:   char                  tmpName[MAX_NAME_LENGTH + 1];

    420:   PetscFunctionBegin;
    421:   PetscCheck(idx >= 0 && idx < exo->numZonalVariables, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Variable index out of range. Was PetscViewerExodusIISetZonalVariable called?");
    422:   if (!exo->zonalVariableNames[idx]) {
    423:     PetscCall(PetscViewerExodusIIGetId(viewer, &exoid));
    424:     PetscCallExternal(ex_get_variable_name, exoid, EX_ELEM_BLOCK, idx + 1, tmpName);
    425:     PetscCall(PetscStrallocpy(tmpName, (char **)&exo->zonalVariableNames[idx]));
    426:   }
    427:   *name = exo->zonalVariableNames[idx];
    428:   PetscFunctionReturn(PETSC_SUCCESS);
    429: }

    431: /*@
    432:   PetscViewerExodusIIGetNodalVariableName - Gets the name of a nodal variable.

    434:   Collective;

    436:   Input Parameters:
    437: + viewer - a `PetscViewer` of type `PETSCVIEWEREXODUSII`
    438: - idx    - the index for which you want to save the name

    440:   Output Parameter:
    441: . name - string array containing name characters

    443:   Level: intermediate

    445: .seealso: `PETSCVIEWEREXODUSII`, `PetscViewer`, `PetscViewerCreate()`, `PetscViewerDestroy()`, `PetscViewerExodusIIOpen()`, `PetscViewerSetType()`, `PetscViewerType`, `PetscViewerExodusIISetNodalVariableName()`
    446: @*/
    447: PetscErrorCode PetscViewerExodusIIGetNodalVariableName(PetscViewer viewer, PetscExodusIIInt idx, const char *name[])
    448: {
    449:   PetscViewer_ExodusII *exo   = (PetscViewer_ExodusII *)viewer->data;
    450:   PetscExodusIIInt      exoid = -1;
    451:   char                  tmpName[MAX_NAME_LENGTH + 1];

    453:   PetscFunctionBegin;
    454:   PetscCheck((idx >= 0) && (idx < exo->numNodalVariables), PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Variable index out of range. Was PetscViewerExodusIISetNodalVariable called?");
    455:   if (!exo->nodalVariableNames[idx]) {
    456:     PetscCall(PetscViewerExodusIIGetId(viewer, &exoid));
    457:     PetscCallExternal(ex_get_variable_name, exoid, EX_NODAL, idx + 1, tmpName);
    458:     PetscCall(PetscStrallocpy(tmpName, (char **)&exo->nodalVariableNames[idx]));
    459:   }
    460:   *name = exo->nodalVariableNames[idx];
    461:   PetscFunctionReturn(PETSC_SUCCESS);
    462: }

    464: /*@C
    465:   PetscViewerExodusIISetZonalVariableNames - Sets the names of all nodal variables

    467:   Collective; No Fortran Support

    469:   Input Parameters:
    470: + viewer - a `PetscViewer` of type `PETSCVIEWEREXODUSII`
    471: - names  - 2D array containing the array of string names to be set

    473:   Level: intermediate

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

    478: .seealso: `PETSCVIEWEREXODUSII`, `PetscViewer`, `PetscViewerCreate()`, `PetscViewerDestroy()`, `PetscViewerExodusIIOpen()`, `PetscViewerSetType()`, `PetscViewerType`, `PetscViewerExodusIIGetZonalVariableNames()`
    479: @*/
    480: PetscErrorCode PetscViewerExodusIISetZonalVariableNames(PetscViewer viewer, const char *names[])
    481: {
    482:   PetscExodusIIInt      numNames;
    483:   PetscExodusIIInt      exoid = -1;
    484:   PetscViewer_ExodusII *exo   = (PetscViewer_ExodusII *)viewer->data;

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

    490:   PetscCall(PetscViewerExodusIIGetId(viewer, &exoid));
    491:   for (int i = 0; i < numNames; i++) {
    492:     PetscCall(PetscStrallocpy(names[i], &exo->zonalVariableNames[i]));
    493:     PetscCallExternal(ex_put_variable_name, exoid, EX_ELEM_BLOCK, i + 1, names[i]);
    494:   }
    495:   PetscFunctionReturn(PETSC_SUCCESS);
    496: }

    498: /*@C
    499:   PetscViewerExodusIISetNodalVariableNames - Sets the names of all nodal variables.

    501:   Collective; No Fortran Support

    503:   Input Parameters:
    504: + viewer - a `PetscViewer` of type `PETSCVIEWEREXODUSII`
    505: - names  - 2D array containing the array of string names to be set

    507:   Level: intermediate

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

    512: .seealso: `PETSCVIEWEREXODUSII`, `PetscViewer`, `PetscViewerCreate()`, `PetscViewerDestroy()`, `PetscViewerExodusIIOpen()`, `PetscViewerSetType()`, `PetscViewerType`, `PetscViewerExodusIIGetNodalVariableNames()`
    513: @*/
    514: PetscErrorCode PetscViewerExodusIISetNodalVariableNames(PetscViewer viewer, const char *names[])
    515: {
    516:   PetscExodusIIInt      numNames;
    517:   PetscExodusIIInt      exoid = -1;
    518:   PetscViewer_ExodusII *exo   = (PetscViewer_ExodusII *)viewer->data;

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

    524:   PetscCall(PetscViewerExodusIIGetId(viewer, &exoid));
    525:   for (int i = 0; i < numNames; i++) {
    526:     PetscCall(PetscStrallocpy(names[i], &exo->nodalVariableNames[i]));
    527:     PetscCallExternal(ex_put_variable_name, exoid, EX_NODAL, i + 1, names[i]);
    528:   }
    529:   PetscFunctionReturn(PETSC_SUCCESS);
    530: }

    532: /*@C
    533:   PetscViewerExodusIIGetZonalVariableNames - Gets the names of all zonal variables.

    535:   Collective; No Fortran Support

    537:   Input Parameters:
    538: + viewer  - a `PetscViewer` of type `PETSCVIEWEREXODUSII`
    539: - numVars - the number of zonal variable names to retrieve

    541:   Output Parameters:
    542: . varNames - pointer to a 2D array where the zonal variable names will be saved

    544:   Level: intermediate

    546:   Notes:
    547:   This function returns a borrowed pointer which should not be freed.

    549: .seealso: `PETSCVIEWEREXODUSII`, `PetscViewer`, `PetscViewerCreate()`, `PetscViewerDestroy()`, `PetscViewerExodusIIOpen()`, `PetscViewerSetType()`, `PetscViewerType`, `PetscViewerExodusIISetZonalVariableNames()`
    550: @*/
    551: PetscErrorCode PetscViewerExodusIIGetZonalVariableNames(PetscViewer viewer, PetscExodusIIInt *numVars, char ***varNames)
    552: {
    553:   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;
    554:   PetscExodusIIInt      idx;
    555:   char                  tmpName[MAX_NAME_LENGTH + 1];
    556:   PetscExodusIIInt      exoid = -1;

    558:   PetscFunctionBegin;
    559:   PetscCall(PetscViewerExodusIIGetZonalVariable(viewer, numVars));
    560:   /*
    561:     Cache variable names if necessary
    562:   */
    563:   for (idx = 0; idx < *numVars; idx++) {
    564:     if (!exo->zonalVariableNames[idx]) {
    565:       PetscCall(PetscViewerExodusIIGetId(viewer, &exoid));
    566:       PetscCallExternal(ex_get_variable_name, exoid, EX_ELEM_BLOCK, idx + 1, tmpName);
    567:       PetscCall(PetscStrallocpy(tmpName, (char **)&exo->zonalVariableNames[idx]));
    568:     }
    569:   }
    570:   *varNames = (char **)exo->zonalVariableNames;
    571:   PetscFunctionReturn(PETSC_SUCCESS);
    572: }

    574: /*@C
    575:   PetscViewerExodusIIGetNodalVariableNames - Gets the names of all nodal variables.

    577:   Collective; No Fortran Support

    579:   Input Parameters:
    580: + viewer  - a `PetscViewer` of type `PETSCVIEWEREXODUSII`
    581: - numVars - the number of nodal variable names to retrieve

    583:   Output Parameters:
    584: . varNames - 2D array where the nodal variable names will be saved

    586:   Level: intermediate

    588:   Notes:
    589:   This function returns a borrowed pointer which should not be freed.

    591: .seealso: `PETSCVIEWEREXODUSII`, `PetscViewer`, `PetscViewerCreate()`, `PetscViewerDestroy()`, `PetscViewerExodusIIOpen()`, `PetscViewerSetType()`, `PetscViewerType`, `PetscViewerExodusIISetNodalVariableNames()`
    592: @*/
    593: PetscErrorCode PetscViewerExodusIIGetNodalVariableNames(PetscViewer viewer, PetscExodusIIInt *numVars, char ***varNames)
    594: {
    595:   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;
    596:   PetscExodusIIInt      idx;
    597:   char                  tmpName[MAX_NAME_LENGTH + 1];
    598:   PetscExodusIIInt      exoid = -1;

    600:   PetscFunctionBegin;
    601:   PetscCall(PetscViewerExodusIIGetNodalVariable(viewer, numVars));
    602:   /*
    603:     Cache variable names if necessary
    604:   */
    605:   for (idx = 0; idx < *numVars; idx++) {
    606:     if (!exo->nodalVariableNames[idx]) {
    607:       PetscCall(PetscViewerExodusIIGetId(viewer, &exoid));
    608:       PetscCallExternal(ex_get_variable_name, exoid, EX_NODAL, idx + 1, tmpName);
    609:       PetscCall(PetscStrallocpy(tmpName, (char **)&exo->nodalVariableNames[idx]));
    610:     }
    611:   }
    612:   *varNames = (char **)exo->nodalVariableNames;
    613:   PetscFunctionReturn(PETSC_SUCCESS);
    614: }

    616: /*MC
    617:    PETSCVIEWEREXODUSII - A viewer that writes to an Exodus II file

    619:   Level: beginner

    621: .seealso: `PetscViewerExodusIIOpen()`, `PetscViewerCreate()`, `PETSCVIEWERBINARY`, `PETSCVIEWERHDF5`, `DMView()`,
    622:           `PetscViewerFileSetName()`, `PetscViewerFileSetMode()`, `PetscViewerFormat`, `PetscViewerType`, `PetscViewerSetType()`
    623: M*/
    624: PETSC_EXTERN PetscErrorCode PetscViewerCreate_ExodusII(PetscViewer v)
    625: {
    626:   PetscViewer_ExodusII *exo;

    628:   PetscFunctionBegin;
    629:   PetscCall(PetscNew(&exo));

    631:   v->data                 = (void *)exo;
    632:   v->ops->destroy         = PetscViewerDestroy_ExodusII;
    633:   v->ops->setfromoptions  = PetscViewerSetFromOptions_ExodusII;
    634:   v->ops->setup           = PetscViewerSetUp_ExodusII;
    635:   v->ops->view            = PetscViewerView_ExodusII;
    636:   v->ops->flush           = PetscViewerFlush_ExodusII;
    637:   exo->btype              = FILE_MODE_UNDEFINED;
    638:   exo->filename           = 0;
    639:   exo->exoid              = -1;
    640:   exo->numNodalVariables  = -1;
    641:   exo->numZonalVariables  = -1;
    642:   exo->nodalVariableNames = NULL;
    643:   exo->zonalVariableNames = NULL;

    645:   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerFileSetName_C", PetscViewerFileSetName_ExodusII));
    646:   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerFileGetName_C", PetscViewerFileGetName_ExodusII));
    647:   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerFileSetMode_C", PetscViewerFileSetMode_ExodusII));
    648:   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerFileGetMode_C", PetscViewerFileGetMode_ExodusII));
    649:   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerGetId_C", PetscViewerExodusIIGetId_ExodusII));
    650:   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerSetOrder_C", PetscViewerExodusIISetOrder_ExodusII));
    651:   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerGetOrder_C", PetscViewerExodusIIGetOrder_ExodusII));
    652:   PetscFunctionReturn(PETSC_SUCCESS);
    653: }

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

    658:   Collective

    660:   Input Parameters:
    661: + viewer - a `PetscViewer` of type `PETSCVIEWEREXODUSII`
    662: - name   - the name of the result

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

    667:   Level: beginner

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

    675: .seealso: `PetscViewerExodusIISetNodalVariable()`, `PetscViewerExodusIIGetNodalVariable()`, `PetscViewerExodusIISetNodalVariableName()`, `PetscViewerExodusIISetNodalVariableNames()`, `PetscViewerExodusIIGetNodalVariableName()`, `PetscViewerExodusIIGetNodalVariableNames()`
    676: @*/
    677: PetscErrorCode PetscViewerExodusIIGetNodalVariableIndex(PetscViewer viewer, const char name[], PetscExodusIIInt *varIndex)
    678: {
    679:   PetscExodusIIInt num_vars = 0, i, j;
    680:   char             ext_name[MAX_STR_LENGTH + 1];
    681:   char           **var_names;
    682:   const int        num_suffix = 5;
    683:   char            *suffix[5];
    684:   PetscBool        flg;

    686:   PetscFunctionBegin;
    687:   suffix[0] = (char *)"";
    688:   suffix[1] = (char *)"_X";
    689:   suffix[2] = (char *)"_XX";
    690:   suffix[3] = (char *)"_1";
    691:   suffix[4] = (char *)"_11";
    692:   *varIndex = -1;

    694:   PetscCall(PetscViewerExodusIIGetNodalVariableNames(viewer, &num_vars, &var_names));
    695:   for (i = 0; i < num_vars; ++i) {
    696:     for (j = 0; j < num_suffix; ++j) {
    697:       PetscCall(PetscStrncpy(ext_name, name, MAX_STR_LENGTH));
    698:       PetscCall(PetscStrlcat(ext_name, suffix[j], MAX_STR_LENGTH));
    699:       PetscCall(PetscStrcasecmp(ext_name, var_names[i], &flg));
    700:       if (flg) *varIndex = i;
    701:     }
    702:     if (flg) break;
    703:   }
    704:   PetscFunctionReturn(PETSC_SUCCESS);
    705: }

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

    710:   Collective

    712:   Input Parameters:
    713: + viewer - a `PetscViewer` of type `PETSCVIEWEREXODUSII`
    714: - name   - the name of the result

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

    719:   Level: beginner

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

    727: .seealso: `PetscViewerExodusIISetNodalVariable()`, `PetscViewerExodusIIGetNodalVariable()`, `PetscViewerExodusIISetNodalVariableName()`, `PetscViewerExodusIISetNodalVariableNames()`, `PetscViewerExodusIIGetNodalVariableName()`, `PetscViewerExodusIIGetNodalVariableNames()`
    728: @*/
    729: PetscErrorCode PetscViewerExodusIIGetZonalVariableIndex(PetscViewer viewer, const char name[], int *varIndex)
    730: {
    731:   PetscExodusIIInt num_vars = 0, i, j;
    732:   char             ext_name[MAX_STR_LENGTH + 1];
    733:   char           **var_names;
    734:   const int        num_suffix = 5;
    735:   char            *suffix[5];
    736:   PetscBool        flg;

    738:   PetscFunctionBegin;
    739:   suffix[0] = (char *)"";
    740:   suffix[1] = (char *)"_X";
    741:   suffix[2] = (char *)"_XX";
    742:   suffix[3] = (char *)"_1";
    743:   suffix[4] = (char *)"_11";
    744:   *varIndex = -1;

    746:   PetscCall(PetscViewerExodusIIGetZonalVariableNames(viewer, &num_vars, &var_names));
    747:   for (i = 0; i < num_vars; ++i) {
    748:     for (j = 0; j < num_suffix; ++j) {
    749:       PetscCall(PetscStrncpy(ext_name, name, MAX_STR_LENGTH));
    750:       PetscCall(PetscStrlcat(ext_name, suffix[j], MAX_STR_LENGTH));
    751:       PetscCall(PetscStrcasecmp(ext_name, var_names[i], &flg));
    752:       if (flg) *varIndex = i;
    753:     }
    754:     if (flg) break;
    755:   }
    756:   PetscFunctionReturn(PETSC_SUCCESS);
    757: }

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

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

    804:   PetscFunctionBegin;
    805:   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
    806:   PetscCallMPI(MPI_Comm_rank(comm, &rank));
    807:   PetscCallMPI(MPI_Comm_size(comm, &size));

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

    844:       break;
    845:     default:
    846:       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ORDER, "Must call PetscViewerFileSetMode() before PetscViewerFileSetName()");
    847:     }

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

    874:     PetscCall(PetscViewerExodusIIGetOrder(viewer, &degree));
    875:     if (degree == 2) numNodes += numEdges;
    876:     cellsNotInConnectivity = numCells;
    877:     for (cs = 0; cs < num_cs; ++cs) {
    878:       IS              stratumIS;
    879:       const PetscInt *cells;
    880:       PetscScalar    *xyz = NULL;
    881:       PetscInt        csSize, closureSize;

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

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

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

    986:           PetscCall(DMPlexGetConeSize(dm, cells[0], &facesInClosure));
    987:           PetscCall(DMPlexGetTransitiveClosure(dm, cells[0], PETSC_TRUE, &closureSize, &closure));
    988:           edgesInClosure = closureSize - facesInClosure - 1 - verticesInClosure;
    989:           PetscCall(DMPlexRestoreTransitiveClosure(dm, cells[0], PETSC_TRUE, &closureSize, &closure));
    990:         }
    991:       }
    992:       /* Get connectivity for each cell */
    993:       for (c = 0; c < csSize; ++c) {
    994:         PetscInt *closure = NULL;
    995:         PetscInt  temp, i;

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

    1049:             temp              = connect[12 + off];
    1050:             connect[12 + off] = connect[16 + off];
    1051:             connect[16 + off] = temp;
    1052:             temp              = connect[13 + off];
    1053:             connect[13 + off] = connect[17 + off];
    1054:             connect[17 + off] = temp;
    1055:             temp              = connect[14 + off];
    1056:             connect[14 + off] = connect[18 + off];
    1057:             connect[18 + off] = temp;
    1058:             temp              = connect[15 + off];
    1059:             connect[15 + off] = connect[19 + off];
    1060:             connect[19 + off] = temp;

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

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

    1115:       /* There can't be more than 24 values in the closure of a point for the coord coordSection */
    1116:       PetscCall(PetscCalloc3(numNodes * 3, &coords, dim, &cval, 24, &closure));
    1117:       PetscCall(DMGetCoordinatesLocalNoncollective(dm, &coord));
    1118:       PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
    1119:       for (p = pStart; p < pEnd; ++p) {
    1120:         PetscCall(PetscSectionGetDof(coordSection, p, &hasDof));
    1121:         if (hasDof) {
    1122:           PetscInt closureSize = 24, j;

    1124:           PetscCall(DMPlexVecGetClosure(cdm, NULL, coord, p, &closureSize, &closure));
    1125:           for (d = 0; d < dim; ++d) {
    1126:             cval[d] = 0.0;
    1127:             for (j = 0; j < closureSize / dim; j++) cval[d] += closure[j * dim + d];
    1128:             coords[d * numNodes + n] = PetscRealPart(cval[d]) * dim / closureSize;
    1129:           }
    1130:           ++n;
    1131:         }
    1132:       }
    1133:       PetscCallExternal(ex_put_coord, exo->exoid, &coords[0 * numNodes], &coords[1 * numNodes], &coords[2 * numNodes]);
    1134:       PetscCall(PetscFree3(coords, cval, closure));
    1135:       PetscCallExternal(ex_put_coord_names, exo->exoid, (char **)coordNames);
    1136:     }

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

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

    1197:           for (i = 0; i < fsSize; ++i) {
    1198:             /* Element List */
    1199:             points = NULL;
    1200:             PetscCall(DMPlexGetTransitiveClosure(dm, faces[i], PETSC_FALSE, &numPoints, &points));
    1201:             elem_list[elem_ind[fs] + i] = points[2] + 1;
    1202:             PetscCall(DMPlexRestoreTransitiveClosure(dm, faces[i], PETSC_FALSE, &numPoints, &points));

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

    1240:         /* Put side sets */
    1241:         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]]);
    1242:         PetscCall(PetscFree3(elem_ind, elem_list, side_list));
    1243:       }
    1244:     }
    1245:     /*
    1246:       close the exodus file
    1247:     */
    1248:     ex_close(exo->exoid);
    1249:     exo->exoid = -1;
    1250:   }
    1251:   PetscCall(PetscSectionDestroy(&coordSection));

    1253:   /*
    1254:     reopen the file in parallel
    1255:   */
    1256:   EXO_mode = EX_WRITE;
    1257: #if defined(PETSC_USE_64BIT_INDICES)
    1258:   EXO_mode += EX_ALL_INT64_API;
    1259: #endif
    1260:   CPU_word_size = sizeof(PetscReal);
    1261:   IO_word_size  = sizeof(PetscReal);
    1262:   exo->exoid    = ex_open_par(exo->filename, EXO_mode, &CPU_word_size, &IO_word_size, &EXO_version, comm, MPI_INFO_NULL);
    1263:   PetscCheck(exo->exoid >= 0, PETSC_COMM_SELF, PETSC_ERR_LIB, "ex_open_par failed for %s", exo->filename);
    1264:   PetscFunctionReturn(PETSC_SUCCESS);
    1265: }

    1267: static PetscErrorCode VecViewPlex_ExodusII_Nodal_Internal(Vec v, PetscExodusIIInt exoid, PetscExodusIIInt step, PetscExodusIIInt offset);
    1268: static PetscErrorCode VecViewPlex_ExodusII_Zonal_Internal(Vec v, PetscExodusIIInt exoid, PetscExodusIIInt step, PetscExodusIIInt offset);
    1269: static PetscErrorCode VecLoadPlex_ExodusII_Nodal_Internal(Vec v, PetscExodusIIInt exoid, PetscExodusIIInt step, PetscExodusIIInt offset);
    1270: static PetscErrorCode VecLoadPlex_ExodusII_Zonal_Internal(Vec v, PetscExodusIIInt exoid, PetscExodusIIInt step, PetscExodusIIInt offset);

    1272: PetscErrorCode VecView_PlexExodusII_Internal(Vec v, PetscViewer viewer)
    1273: {
    1274:   DM          dm;
    1275:   MPI_Comm    comm;
    1276:   PetscMPIInt rank;

    1278:   PetscExodusIIInt exoid, offsetN = -1, offsetZ = -1;
    1279:   const char      *vecname;
    1280:   PetscInt         step;

    1282:   PetscFunctionBegin;
    1283:   PetscCall(PetscObjectGetComm((PetscObject)v, &comm));
    1284:   PetscCallMPI(MPI_Comm_rank(comm, &rank));
    1285:   PetscCall(PetscViewerExodusIIGetId(viewer, &exoid));
    1286:   PetscCall(VecGetDM(v, &dm));
    1287:   PetscCall(PetscObjectGetName((PetscObject)v, &vecname));

    1289:   PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
    1290:   PetscCall(PetscViewerExodusIIGetNodalVariableIndex(viewer, vecname, &offsetN));
    1291:   PetscCall(PetscViewerExodusIIGetZonalVariableIndex(viewer, vecname, &offsetZ));
    1292:   PetscCheck(!(offsetN >= 0 && offsetZ >= 0), comm, PETSC_ERR_FILE_UNEXPECTED, "Found both nodal and zonal variable %s in exodus file. ", vecname);
    1293:   if (offsetN >= 0) {
    1294:     PetscCall(VecViewPlex_ExodusII_Nodal_Internal(v, exoid, (PetscExodusIIInt)step + 1, offsetN + 1));
    1295:   } else if (offsetZ >= 0) {
    1296:     PetscCall(VecViewPlex_ExodusII_Zonal_Internal(v, exoid, (PetscExodusIIInt)step + 1, offsetZ + 1));
    1297:   } else SETERRQ(comm, PETSC_ERR_FILE_UNEXPECTED, "Could not find nodal or zonal variable %s in exodus file. ", vecname);
    1298:   PetscFunctionReturn(PETSC_SUCCESS);
    1299: }

    1301: PetscErrorCode VecLoad_PlexExodusII_Internal(Vec v, PetscViewer viewer)
    1302: {
    1303:   DM          dm;
    1304:   MPI_Comm    comm;
    1305:   PetscMPIInt rank;

    1307:   PetscExodusIIInt exoid, offsetN = 0, offsetZ = 0;
    1308:   const char      *vecname;
    1309:   PetscInt         step;

    1311:   PetscFunctionBegin;
    1312:   PetscCall(PetscObjectGetComm((PetscObject)v, &comm));
    1313:   PetscCallMPI(MPI_Comm_rank(comm, &rank));
    1314:   PetscCall(PetscViewerExodusIIGetId(viewer, &exoid));
    1315:   PetscCall(VecGetDM(v, &dm));
    1316:   PetscCall(PetscObjectGetName((PetscObject)v, &vecname));

    1318:   PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
    1319:   PetscCall(PetscViewerExodusIIGetNodalVariableIndex(viewer, vecname, &offsetN));
    1320:   PetscCall(PetscViewerExodusIIGetZonalVariableIndex(viewer, vecname, &offsetZ));
    1321:   PetscCheck(!(offsetN >= 0 && offsetZ >= 0), comm, PETSC_ERR_FILE_UNEXPECTED, "Found both nodal and zonal variable %s in exodus file. ", vecname);
    1322:   if (offsetN >= 0) PetscCall(VecLoadPlex_ExodusII_Nodal_Internal(v, exoid, (PetscExodusIIInt)step + 1, offsetN + 1));
    1323:   else if (offsetZ >= 0) PetscCall(VecLoadPlex_ExodusII_Zonal_Internal(v, exoid, (PetscExodusIIInt)step + 1, offsetZ + 1));
    1324:   else SETERRQ(comm, PETSC_ERR_FILE_UNEXPECTED, "Could not find nodal or zonal variable %s in exodus file. ", vecname);
    1325:   PetscFunctionReturn(PETSC_SUCCESS);
    1326: }

    1328: static PetscErrorCode VecViewPlex_ExodusII_Nodal_Internal(Vec v, PetscExodusIIInt exoid, PetscExodusIIInt step, PetscExodusIIInt offset)
    1329: {
    1330:   MPI_Comm           comm;
    1331:   PetscMPIInt        size;
    1332:   DM                 dm;
    1333:   Vec                vNatural, vComp;
    1334:   const PetscScalar *varray;
    1335:   PetscInt           xs, xe, bs;
    1336:   PetscBool          useNatural;

    1338:   PetscFunctionBegin;
    1339:   PetscCall(PetscObjectGetComm((PetscObject)v, &comm));
    1340:   PetscCallMPI(MPI_Comm_size(comm, &size));
    1341:   PetscCall(VecGetDM(v, &dm));
    1342:   PetscCall(DMGetUseNatural(dm, &useNatural));
    1343:   useNatural = useNatural && size > 1 ? PETSC_TRUE : PETSC_FALSE;
    1344:   if (useNatural) {
    1345:     PetscCall(DMPlexCreateNaturalVector(dm, &vNatural));
    1346:     PetscCall(DMPlexGlobalToNaturalBegin(dm, v, vNatural));
    1347:     PetscCall(DMPlexGlobalToNaturalEnd(dm, v, vNatural));
    1348:   } else {
    1349:     vNatural = v;
    1350:   }

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

    1365:     PetscCall(ISCreateStride(comm, (xe - xs) / bs, xs, bs, &compIS));
    1366:     for (c = 0; c < bs; ++c) {
    1367:       PetscCall(ISStrideSetStride(compIS, (xe - xs) / bs, xs + c, bs));
    1368:       PetscCall(VecGetSubVector(vNatural, compIS, &vComp));
    1369:       PetscCall(VecGetArrayRead(vComp, &varray));
    1370:       PetscCallExternal(ex_put_partial_var, exoid, step, EX_NODAL, offset + c, 1, xs / bs + 1, (xe - xs) / bs, varray);
    1371:       PetscCall(VecRestoreArrayRead(vComp, &varray));
    1372:       PetscCall(VecRestoreSubVector(vNatural, compIS, &vComp));
    1373:     }
    1374:     PetscCall(ISDestroy(&compIS));
    1375:   }
    1376:   if (useNatural) PetscCall(VecDestroy(&vNatural));
    1377:   PetscFunctionReturn(PETSC_SUCCESS);
    1378: }

    1380: static PetscErrorCode VecLoadPlex_ExodusII_Nodal_Internal(Vec v, PetscExodusIIInt exoid, PetscExodusIIInt step, PetscExodusIIInt offset)
    1381: {
    1382:   MPI_Comm     comm;
    1383:   PetscMPIInt  size;
    1384:   DM           dm;
    1385:   Vec          vNatural, vComp;
    1386:   PetscScalar *varray;
    1387:   PetscInt     xs, xe, bs;
    1388:   PetscBool    useNatural;

    1390:   PetscFunctionBegin;
    1391:   PetscCall(PetscObjectGetComm((PetscObject)v, &comm));
    1392:   PetscCallMPI(MPI_Comm_size(comm, &size));
    1393:   PetscCall(VecGetDM(v, &dm));
    1394:   PetscCall(DMGetUseNatural(dm, &useNatural));
    1395:   useNatural = useNatural && size > 1 ? PETSC_TRUE : PETSC_FALSE;
    1396:   if (useNatural) PetscCall(DMPlexCreateNaturalVector(dm, &vNatural));
    1397:   else vNatural = v;

    1399:   /* Read local chunk from the file */
    1400:   PetscCall(VecGetOwnershipRange(vNatural, &xs, &xe));
    1401:   PetscCall(VecGetBlockSize(vNatural, &bs));
    1402:   if (bs == 1) {
    1403:     PetscCall(VecGetArray(vNatural, &varray));
    1404:     PetscCallExternal(ex_get_partial_var, exoid, step, EX_NODAL, offset, 1, xs + 1, xe - xs, varray);
    1405:     PetscCall(VecRestoreArray(vNatural, &varray));
    1406:   } else {
    1407:     IS       compIS;
    1408:     PetscInt c;

    1410:     PetscCall(ISCreateStride(comm, (xe - xs) / bs, xs, bs, &compIS));
    1411:     for (c = 0; c < bs; ++c) {
    1412:       PetscCall(ISStrideSetStride(compIS, (xe - xs) / bs, xs + c, bs));
    1413:       PetscCall(VecGetSubVector(vNatural, compIS, &vComp));
    1414:       PetscCall(VecGetArray(vComp, &varray));
    1415:       PetscCallExternal(ex_get_partial_var, exoid, step, EX_NODAL, offset + c, 1, xs / bs + 1, (xe - xs) / bs, varray);
    1416:       PetscCall(VecRestoreArray(vComp, &varray));
    1417:       PetscCall(VecRestoreSubVector(vNatural, compIS, &vComp));
    1418:     }
    1419:     PetscCall(ISDestroy(&compIS));
    1420:   }
    1421:   if (useNatural) {
    1422:     PetscCall(DMPlexNaturalToGlobalBegin(dm, vNatural, v));
    1423:     PetscCall(DMPlexNaturalToGlobalEnd(dm, vNatural, v));
    1424:     PetscCall(VecDestroy(&vNatural));
    1425:   }
    1426:   PetscFunctionReturn(PETSC_SUCCESS);
    1427: }

    1429: static PetscErrorCode VecViewPlex_ExodusII_Zonal_Internal(Vec v, PetscExodusIIInt exoid, PetscExodusIIInt step, PetscExodusIIInt offset)
    1430: {
    1431:   MPI_Comm           comm;
    1432:   PetscMPIInt        size;
    1433:   DM                 dm;
    1434:   Vec                vNatural, vComp;
    1435:   const PetscScalar *varray;
    1436:   PetscInt           xs, xe, bs;
    1437:   PetscBool          useNatural;
    1438:   IS                 compIS;
    1439:   PetscInt          *csSize, *csID;
    1440:   PetscExodusIIInt   numCS, set, csxs = 0;

    1442:   PetscFunctionBegin;
    1443:   PetscCall(PetscObjectGetComm((PetscObject)v, &comm));
    1444:   PetscCallMPI(MPI_Comm_size(comm, &size));
    1445:   PetscCall(VecGetDM(v, &dm));
    1446:   PetscCall(DMGetUseNatural(dm, &useNatural));
    1447:   useNatural = useNatural && size > 1 ? PETSC_TRUE : PETSC_FALSE;
    1448:   if (useNatural) {
    1449:     PetscCall(DMPlexCreateNaturalVector(dm, &vNatural));
    1450:     PetscCall(DMPlexGlobalToNaturalBegin(dm, v, vNatural));
    1451:     PetscCall(DMPlexGlobalToNaturalEnd(dm, v, vNatural));
    1452:   } else {
    1453:     vNatural = v;
    1454:   }

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

    1468:     block.id   = csID[set];
    1469:     block.type = EX_ELEM_BLOCK;
    1470:     PetscCallExternal(ex_get_block_param, exoid, &block);
    1471:     csSize[set] = block.num_entry;
    1472:   }
    1473:   PetscCall(VecGetOwnershipRange(vNatural, &xs, &xe));
    1474:   PetscCall(VecGetBlockSize(vNatural, &bs));
    1475:   if (bs > 1) PetscCall(ISCreateStride(comm, (xe - xs) / bs, xs, bs, &compIS));
    1476:   for (set = 0; set < numCS; set++) {
    1477:     PetscInt csLocalSize, c;

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

    1505: static PetscErrorCode VecLoadPlex_ExodusII_Zonal_Internal(Vec v, PetscExodusIIInt exoid, PetscExodusIIInt step, PetscExodusIIInt offset)
    1506: {
    1507:   MPI_Comm         comm;
    1508:   PetscMPIInt      size;
    1509:   DM               dm;
    1510:   Vec              vNatural, vComp;
    1511:   PetscScalar     *varray;
    1512:   PetscInt         xs, xe, bs;
    1513:   PetscBool        useNatural;
    1514:   IS               compIS;
    1515:   PetscInt        *csSize, *csID;
    1516:   PetscExodusIIInt numCS, set, csxs = 0;

    1518:   PetscFunctionBegin;
    1519:   PetscCall(PetscObjectGetComm((PetscObject)v, &comm));
    1520:   PetscCallMPI(MPI_Comm_size(comm, &size));
    1521:   PetscCall(VecGetDM(v, &dm));
    1522:   PetscCall(DMGetUseNatural(dm, &useNatural));
    1523:   useNatural = useNatural && size > 1 ? PETSC_TRUE : PETSC_FALSE;
    1524:   if (useNatural) PetscCall(DMPlexCreateNaturalVector(dm, &vNatural));
    1525:   else vNatural = v;

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

    1539:     block.id   = csID[set];
    1540:     block.type = EX_ELEM_BLOCK;
    1541:     PetscCallExternal(ex_get_block_param, exoid, &block);
    1542:     csSize[set] = block.num_entry;
    1543:   }
    1544:   PetscCall(VecGetOwnershipRange(vNatural, &xs, &xe));
    1545:   PetscCall(VecGetBlockSize(vNatural, &bs));
    1546:   if (bs > 1) PetscCall(ISCreateStride(comm, (xe - xs) / bs, xs, bs, &compIS));
    1547:   for (set = 0; set < numCS; ++set) {
    1548:     PetscInt csLocalSize, c;

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

    1580: /*@
    1581:   PetscViewerExodusIIGetId - Get the file id of the `PETSCVIEWEREXODUSII` file

    1583:   Logically Collective

    1585:   Input Parameter:
    1586: . viewer - the `PetscViewer`

    1588:   Output Parameter:
    1589: . exoid - The ExodusII file id

    1591:   Level: intermediate

    1593: .seealso: `PETSCVIEWEREXODUSII`, `PetscViewer`, `PetscViewerFileSetMode()`, `PetscViewerCreate()`, `PetscViewerSetType()`, `PetscViewerBinaryOpen()`
    1594: @*/
    1595: PetscErrorCode PetscViewerExodusIIGetId(PetscViewer viewer, PetscExodusIIInt *exoid)
    1596: {
    1597:   PetscFunctionBegin;
    1599:   PetscTryMethod(viewer, "PetscViewerGetId_C", (PetscViewer, PetscExodusIIInt *), (viewer, exoid));
    1600:   PetscFunctionReturn(PETSC_SUCCESS);
    1601: }

    1603: /*@
    1604:   PetscViewerExodusIISetOrder - Set the elements order in the exodusII file.

    1606:   Collective

    1608:   Input Parameters:
    1609: + viewer - the `PETSCVIEWEREXODUSII` viewer
    1610: - order  - elements order

    1612:   Output Parameter:

    1614:   Level: beginner

    1616: .seealso: `PETSCVIEWEREXODUSII`, `PetscViewer`, `PetscViewerExodusIIGetId()`, `PetscViewerExodusIIGetOrder()`
    1617: @*/
    1618: PetscErrorCode PetscViewerExodusIISetOrder(PetscViewer viewer, PetscInt order)
    1619: {
    1620:   PetscFunctionBegin;
    1622:   PetscTryMethod(viewer, "PetscViewerSetOrder_C", (PetscViewer, PetscInt), (viewer, order));
    1623:   PetscFunctionReturn(PETSC_SUCCESS);
    1624: }

    1626: /*@
    1627:   PetscViewerExodusIIGetOrder - Get the elements order in the exodusII file.

    1629:   Collective

    1631:   Input Parameters:
    1632: + viewer - the `PETSCVIEWEREXODUSII` viewer
    1633: - order  - elements order

    1635:   Output Parameter:

    1637:   Level: beginner

    1639: .seealso: `PETSCVIEWEREXODUSII`, `PetscViewer`, `PetscViewerExodusIIGetId()`, `PetscViewerExodusIISetOrder()`
    1640: @*/
    1641: PetscErrorCode PetscViewerExodusIIGetOrder(PetscViewer viewer, PetscInt *order)
    1642: {
    1643:   PetscFunctionBegin;
    1645:   PetscTryMethod(viewer, "PetscViewerGetOrder_C", (PetscViewer, PetscInt *), (viewer, order));
    1646:   PetscFunctionReturn(PETSC_SUCCESS);
    1647: }

    1649: /*@
    1650:   PetscViewerExodusIIOpen - Opens a file for ExodusII input/output.

    1652:   Collective

    1654:   Input Parameters:
    1655: + comm - MPI communicator
    1656: . name - name of file
    1657: - mode - access mode
    1658: .vb
    1659:     FILE_MODE_WRITE - create new file for binary output
    1660:     FILE_MODE_READ - open existing file for binary input
    1661:     FILE_MODE_APPEND - open existing file for binary output
    1662: .ve

    1664:   Output Parameter:
    1665: . exo - `PETSCVIEWEREXODUSII` `PetscViewer` for Exodus II input/output to use with the specified file

    1667:   Level: beginner

    1669: .seealso: `PETSCVIEWEREXODUSII`, `PetscViewer`, `PetscViewerPushFormat()`, `PetscViewerDestroy()`,
    1670:           `DMLoad()`, `PetscFileMode`, `PetscViewerSetType()`, `PetscViewerFileSetMode()`, `PetscViewerFileSetName()`
    1671: @*/
    1672: PetscErrorCode PetscViewerExodusIIOpen(MPI_Comm comm, const char name[], PetscFileMode mode, PetscViewer *exo)
    1673: {
    1674:   PetscFunctionBegin;
    1675:   PetscCall(PetscViewerCreate(comm, exo));
    1676:   PetscCall(PetscViewerSetType(*exo, PETSCVIEWEREXODUSII));
    1677:   PetscCall(PetscViewerFileSetMode(*exo, mode));
    1678:   PetscCall(PetscViewerFileSetName(*exo, name));
    1679:   PetscCall(PetscViewerSetFromOptions(*exo));
    1680:   PetscFunctionReturn(PETSC_SUCCESS);
    1681: }

    1683: static PetscErrorCode ExodusGetCellType_Internal(const char *elem_type, DMPolytopeType *ct)
    1684: {
    1685:   PetscBool flg;

    1687:   PetscFunctionBegin;
    1688:   *ct = DM_POLYTOPE_UNKNOWN;
    1689:   PetscCall(PetscStrcmp(elem_type, "BAR2", &flg));
    1690:   if (flg) {
    1691:     *ct = DM_POLYTOPE_SEGMENT;
    1692:     goto done;
    1693:   }
    1694:   PetscCall(PetscStrcmp(elem_type, "BAR3", &flg));
    1695:   if (flg) {
    1696:     *ct = DM_POLYTOPE_SEGMENT;
    1697:     goto done;
    1698:   }
    1699:   PetscCall(PetscStrcmp(elem_type, "TRI", &flg));
    1700:   if (flg) {
    1701:     *ct = DM_POLYTOPE_TRIANGLE;
    1702:     goto done;
    1703:   }
    1704:   PetscCall(PetscStrcmp(elem_type, "TRI3", &flg));
    1705:   if (flg) {
    1706:     *ct = DM_POLYTOPE_TRIANGLE;
    1707:     goto done;
    1708:   }
    1709:   PetscCall(PetscStrcmp(elem_type, "QUAD", &flg));
    1710:   if (flg) {
    1711:     *ct = DM_POLYTOPE_QUADRILATERAL;
    1712:     goto done;
    1713:   }
    1714:   PetscCall(PetscStrcmp(elem_type, "QUAD4", &flg));
    1715:   if (flg) {
    1716:     *ct = DM_POLYTOPE_QUADRILATERAL;
    1717:     goto done;
    1718:   }
    1719:   PetscCall(PetscStrcmp(elem_type, "SHELL4", &flg));
    1720:   if (flg) {
    1721:     *ct = DM_POLYTOPE_QUADRILATERAL;
    1722:     goto done;
    1723:   }
    1724:   PetscCall(PetscStrcmp(elem_type, "TETRA", &flg));
    1725:   if (flg) {
    1726:     *ct = DM_POLYTOPE_TETRAHEDRON;
    1727:     goto done;
    1728:   }
    1729:   PetscCall(PetscStrcmp(elem_type, "TET4", &flg));
    1730:   if (flg) {
    1731:     *ct = DM_POLYTOPE_TETRAHEDRON;
    1732:     goto done;
    1733:   }
    1734:   PetscCall(PetscStrcmp(elem_type, "WEDGE", &flg));
    1735:   if (flg) {
    1736:     *ct = DM_POLYTOPE_TRI_PRISM;
    1737:     goto done;
    1738:   }
    1739:   PetscCall(PetscStrcmp(elem_type, "HEX", &flg));
    1740:   if (flg) {
    1741:     *ct = DM_POLYTOPE_HEXAHEDRON;
    1742:     goto done;
    1743:   }
    1744:   PetscCall(PetscStrcmp(elem_type, "HEX8", &flg));
    1745:   if (flg) {
    1746:     *ct = DM_POLYTOPE_HEXAHEDRON;
    1747:     goto done;
    1748:   }
    1749:   PetscCall(PetscStrcmp(elem_type, "HEXAHEDRON", &flg));
    1750:   if (flg) {
    1751:     *ct = DM_POLYTOPE_HEXAHEDRON;
    1752:     goto done;
    1753:   }
    1754:   SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unrecognized element type %s", elem_type);
    1755: done:
    1756:   PetscFunctionReturn(PETSC_SUCCESS);
    1757: }

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

    1762:   Collective

    1764:   Input Parameters:
    1765: + comm        - The MPI communicator
    1766: . exoid       - The ExodusII id associated with a exodus file and obtained using ex_open
    1767: - interpolate - Create faces and edges in the mesh

    1769:   Output Parameter:
    1770: . dm - The `DM` object representing the mesh

    1772:   Level: beginner

    1774: .seealso: [](ch_unstructured), `DM`, `PETSCVIEWEREXODUSII`, `DMPLEX`, `DMCreate()`
    1775: @*/
    1776: PetscErrorCode DMPlexCreateExodus(MPI_Comm comm, PetscExodusIIInt exoid, PetscBool interpolate, DM *dm)
    1777: {
    1778:   PetscMPIInt  num_proc, rank;
    1779:   DMLabel      cellSets = NULL, faceSets = NULL, vertSets = NULL;
    1780:   PetscSection coordSection;
    1781:   Vec          coordinates;
    1782:   PetscScalar *coords;
    1783:   PetscInt     coordSize, v;
    1784:   /* Read from ex_get_init() */
    1785:   char title[PETSC_MAX_PATH_LEN + 1];
    1786:   int  dim = 0, dimEmbed = 0, numVertices = 0, numCells = 0;
    1787:   int  num_cs = 0, num_vs = 0, num_fs = 0;

    1789:   PetscFunctionBegin;
    1790:   PetscCallMPI(MPI_Comm_rank(comm, &rank));
    1791:   PetscCallMPI(MPI_Comm_size(comm, &num_proc));
    1792:   PetscCall(DMCreate(comm, dm));
    1793:   PetscCall(DMSetType(*dm, DMPLEX));
    1794:   /* Open EXODUS II file and read basic information on rank 0, then broadcast to all processors */
    1795:   if (rank == 0) {
    1796:     PetscCall(PetscMemzero(title, PETSC_MAX_PATH_LEN + 1));
    1797:     PetscCallExternal(ex_get_init, exoid, title, &dimEmbed, &numVertices, &numCells, &num_cs, &num_vs, &num_fs);
    1798:     PetscCheck(num_cs, PETSC_COMM_SELF, PETSC_ERR_SUP, "Exodus file does not contain any cell set");
    1799:   }
    1800:   PetscCallMPI(MPI_Bcast(title, PETSC_MAX_PATH_LEN + 1, MPI_CHAR, 0, comm));
    1801:   PetscCallMPI(MPI_Bcast(&dim, 1, MPI_INT, 0, comm));
    1802:   PetscCall(PetscObjectSetName((PetscObject)*dm, title));
    1803:   PetscCall(DMPlexSetChart(*dm, 0, numCells + numVertices));
    1804:   /*   We do not want this label automatically computed, instead we compute it here */
    1805:   PetscCall(DMCreateLabel(*dm, "celltype"));

    1807:   /* Read cell sets information */
    1808:   if (rank == 0) {
    1809:     PetscInt *cone;
    1810:     int       c, cs, ncs, c_loc, v, v_loc;
    1811:     /* Read from ex_get_elem_blk_ids() */
    1812:     int *cs_id, *cs_order;
    1813:     /* Read from ex_get_elem_block() */
    1814:     char buffer[PETSC_MAX_PATH_LEN + 1];
    1815:     int  num_cell_in_set, num_vertex_per_cell, num_hybrid, num_attr;
    1816:     /* Read from ex_get_elem_conn() */
    1817:     int *cs_connect;

    1819:     /* Get cell sets IDs */
    1820:     PetscCall(PetscMalloc2(num_cs, &cs_id, num_cs, &cs_order));
    1821:     PetscCallExternal(ex_get_ids, exoid, EX_ELEM_BLOCK, cs_id);
    1822:     /* Read the cell set connectivity table and build mesh topology
    1823:        EXO standard requires that cells in cell sets be numbered sequentially and be pairwise disjoint. */
    1824:     /* Check for a hybrid mesh */
    1825:     for (cs = 0, num_hybrid = 0; cs < num_cs; ++cs) {
    1826:       DMPolytopeType ct;
    1827:       char           elem_type[PETSC_MAX_PATH_LEN];

    1829:       PetscCall(PetscArrayzero(elem_type, sizeof(elem_type)));
    1830:       PetscCallExternal(ex_get_elem_type, exoid, cs_id[cs], elem_type);
    1831:       PetscCall(ExodusGetCellType_Internal(elem_type, &ct));
    1832:       dim = PetscMax(dim, DMPolytopeTypeGetDim(ct));
    1833:       PetscCallExternal(ex_get_block, exoid, EX_ELEM_BLOCK, cs_id[cs], buffer, &num_cell_in_set, &num_vertex_per_cell, 0, 0, &num_attr);
    1834:       switch (ct) {
    1835:       case DM_POLYTOPE_TRI_PRISM:
    1836:         cs_order[cs] = cs;
    1837:         ++num_hybrid;
    1838:         break;
    1839:       default:
    1840:         for (c = cs; c > cs - num_hybrid; --c) cs_order[c] = cs_order[c - 1];
    1841:         cs_order[cs - num_hybrid] = cs;
    1842:       }
    1843:     }
    1844:     /* First set sizes */
    1845:     for (ncs = 0, c = 0; ncs < num_cs; ++ncs) {
    1846:       DMPolytopeType ct;
    1847:       char           elem_type[PETSC_MAX_PATH_LEN];
    1848:       const PetscInt cs = cs_order[ncs];

    1850:       PetscCall(PetscArrayzero(elem_type, sizeof(elem_type)));
    1851:       PetscCallExternal(ex_get_elem_type, exoid, cs_id[cs], elem_type);
    1852:       PetscCall(ExodusGetCellType_Internal(elem_type, &ct));
    1853:       PetscCallExternal(ex_get_block, exoid, EX_ELEM_BLOCK, cs_id[cs], buffer, &num_cell_in_set, &num_vertex_per_cell, 0, 0, &num_attr);
    1854:       for (c_loc = 0; c_loc < num_cell_in_set; ++c_loc, ++c) {
    1855:         PetscCall(DMPlexSetConeSize(*dm, c, num_vertex_per_cell));
    1856:         PetscCall(DMPlexSetCellType(*dm, c, ct));
    1857:       }
    1858:     }
    1859:     for (v = numCells; v < numCells + numVertices; ++v) PetscCall(DMPlexSetCellType(*dm, v, DM_POLYTOPE_POINT));
    1860:     PetscCall(DMSetUp(*dm));
    1861:     for (ncs = 0, c = 0; ncs < num_cs; ++ncs) {
    1862:       const PetscInt cs = cs_order[ncs];
    1863:       PetscCallExternal(ex_get_block, exoid, EX_ELEM_BLOCK, cs_id[cs], buffer, &num_cell_in_set, &num_vertex_per_cell, 0, 0, &num_attr);
    1864:       PetscCall(PetscMalloc2(num_vertex_per_cell * num_cell_in_set, &cs_connect, num_vertex_per_cell, &cone));
    1865:       PetscCallExternal(ex_get_conn, exoid, EX_ELEM_BLOCK, cs_id[cs], cs_connect, NULL, NULL);
    1866:       /* EXO uses Fortran-based indexing, DMPlex uses C-style and numbers cell first then vertices. */
    1867:       for (c_loc = 0, v = 0; c_loc < num_cell_in_set; ++c_loc, ++c) {
    1868:         DMPolytopeType ct;

    1870:         for (v_loc = 0; v_loc < num_vertex_per_cell; ++v_loc, ++v) cone[v_loc] = cs_connect[v] + numCells - 1;
    1871:         PetscCall(DMPlexGetCellType(*dm, c, &ct));
    1872:         PetscCall(DMPlexInvertCell(ct, cone));
    1873:         PetscCall(DMPlexSetCone(*dm, c, cone));
    1874:         PetscCall(DMSetLabelValue_Fast(*dm, &cellSets, "Cell Sets", c, cs_id[cs]));
    1875:       }
    1876:       PetscCall(PetscFree2(cs_connect, cone));
    1877:     }
    1878:     PetscCall(PetscFree2(cs_id, cs_order));
    1879:   }
    1880:   {
    1881:     PetscInt ints[] = {dim, dimEmbed};

    1883:     PetscCallMPI(MPI_Bcast(ints, 2, MPIU_INT, 0, comm));
    1884:     PetscCall(DMSetDimension(*dm, ints[0]));
    1885:     PetscCall(DMSetCoordinateDim(*dm, ints[1]));
    1886:     dim      = ints[0];
    1887:     dimEmbed = ints[1];
    1888:   }
    1889:   PetscCall(DMPlexSymmetrize(*dm));
    1890:   PetscCall(DMPlexStratify(*dm));
    1891:   if (interpolate) {
    1892:     DM idm;

    1894:     PetscCall(DMPlexInterpolate(*dm, &idm));
    1895:     PetscCall(DMDestroy(dm));
    1896:     *dm = idm;
    1897:   }

    1899:   /* Create vertex set label */
    1900:   if (rank == 0 && (num_vs > 0)) {
    1901:     int vs, v;
    1902:     /* Read from ex_get_node_set_ids() */
    1903:     int *vs_id;
    1904:     /* Read from ex_get_node_set_param() */
    1905:     int num_vertex_in_set;
    1906:     /* Read from ex_get_node_set() */
    1907:     int *vs_vertex_list;

    1909:     /* Get vertex set ids */
    1910:     PetscCall(PetscMalloc1(num_vs, &vs_id));
    1911:     PetscCallExternal(ex_get_ids, exoid, EX_NODE_SET, vs_id);
    1912:     for (vs = 0; vs < num_vs; ++vs) {
    1913:       PetscCallExternal(ex_get_set_param, exoid, EX_NODE_SET, vs_id[vs], &num_vertex_in_set, NULL);
    1914:       PetscCall(PetscMalloc1(num_vertex_in_set, &vs_vertex_list));
    1915:       PetscCallExternal(ex_get_set, exoid, EX_NODE_SET, vs_id[vs], vs_vertex_list, NULL);
    1916:       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]));
    1917:       PetscCall(PetscFree(vs_vertex_list));
    1918:     }
    1919:     PetscCall(PetscFree(vs_id));
    1920:   }
    1921:   /* Read coordinates */
    1922:   PetscCall(DMGetCoordinateSection(*dm, &coordSection));
    1923:   PetscCall(PetscSectionSetNumFields(coordSection, 1));
    1924:   PetscCall(PetscSectionSetFieldComponents(coordSection, 0, dimEmbed));
    1925:   PetscCall(PetscSectionSetChart(coordSection, numCells, numCells + numVertices));
    1926:   for (v = numCells; v < numCells + numVertices; ++v) {
    1927:     PetscCall(PetscSectionSetDof(coordSection, v, dimEmbed));
    1928:     PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, dimEmbed));
    1929:   }
    1930:   PetscCall(PetscSectionSetUp(coordSection));
    1931:   PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
    1932:   PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates));
    1933:   PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
    1934:   PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
    1935:   PetscCall(VecSetBlockSize(coordinates, dimEmbed));
    1936:   PetscCall(VecSetType(coordinates, VECSTANDARD));
    1937:   PetscCall(VecGetArray(coordinates, &coords));
    1938:   if (rank == 0) {
    1939:     PetscReal *x, *y, *z;

    1941:     PetscCall(PetscMalloc3(numVertices, &x, numVertices, &y, numVertices, &z));
    1942:     PetscCallExternal(ex_get_coord, exoid, x, y, z);
    1943:     if (dimEmbed > 0) {
    1944:       for (v = 0; v < numVertices; ++v) coords[v * dimEmbed + 0] = x[v];
    1945:     }
    1946:     if (dimEmbed > 1) {
    1947:       for (v = 0; v < numVertices; ++v) coords[v * dimEmbed + 1] = y[v];
    1948:     }
    1949:     if (dimEmbed > 2) {
    1950:       for (v = 0; v < numVertices; ++v) coords[v * dimEmbed + 2] = z[v];
    1951:     }
    1952:     PetscCall(PetscFree3(x, y, z));
    1953:   }
    1954:   PetscCall(VecRestoreArray(coordinates, &coords));
    1955:   PetscCall(DMSetCoordinatesLocal(*dm, coordinates));
    1956:   PetscCall(VecDestroy(&coordinates));

    1958:   /* Create side set label */
    1959:   if (rank == 0 && interpolate && (num_fs > 0)) {
    1960:     int fs, f, voff;
    1961:     /* Read from ex_get_side_set_ids() */
    1962:     int *fs_id;
    1963:     /* Read from ex_get_side_set_param() */
    1964:     int num_side_in_set;
    1965:     /* Read from ex_get_side_set_node_list() */
    1966:     int *fs_vertex_count_list, *fs_vertex_list, *fs_side_list;
    1967:     /* Read side set labels */
    1968:     char   fs_name[MAX_STR_LENGTH + 1];
    1969:     size_t fs_name_len;

    1971:     /* Get side set ids */
    1972:     PetscCall(PetscMalloc1(num_fs, &fs_id));
    1973:     PetscCallExternal(ex_get_ids, exoid, EX_SIDE_SET, fs_id);
    1974:     // Ids 1 and 2 are reserved by ExodusII for indicating things in 3D
    1975:     for (fs = 0; fs < num_fs; ++fs) {
    1976:       PetscCallExternal(ex_get_set_param, exoid, EX_SIDE_SET, fs_id[fs], &num_side_in_set, NULL);
    1977:       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));
    1978:       PetscCallExternal(ex_get_side_set_node_list, exoid, fs_id[fs], fs_vertex_count_list, fs_vertex_list);
    1979:       PetscCallExternal(ex_get_set, exoid, EX_SIDE_SET, fs_id[fs], NULL, fs_side_list);

    1981:       /* Get the specific name associated with this side set ID. */
    1982:       int fs_name_err = ex_get_name(exoid, EX_SIDE_SET, fs_id[fs], fs_name);
    1983:       if (!fs_name_err) {
    1984:         PetscCall(PetscStrlen(fs_name, &fs_name_len));
    1985:         if (fs_name_len == 0) PetscCall(PetscStrncpy(fs_name, "Face Sets", MAX_STR_LENGTH));
    1986:       }
    1987:       for (f = 0, voff = 0; f < num_side_in_set; ++f) {
    1988:         const PetscInt *faces    = NULL;
    1989:         PetscInt        faceSize = fs_vertex_count_list[f], numFaces;
    1990:         PetscInt        faceVertices[4], v;

    1992:         PetscCheck(faceSize <= 4, comm, PETSC_ERR_ARG_WRONG, "ExodusII side cannot have %" PetscInt_FMT " > 4 vertices", faceSize);
    1993:         for (v = 0; v < faceSize; ++v, ++voff) faceVertices[v] = fs_vertex_list[voff] + numCells - 1;
    1994:         PetscCall(DMPlexGetFullJoin(*dm, faceSize, faceVertices, &numFaces, &faces));
    1995:         PetscCheck(numFaces == 1, comm, PETSC_ERR_ARG_WRONG, "Invalid ExodusII side %d in set %d maps to %" PetscInt_FMT " faces", f, fs, numFaces);
    1996:         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]);
    1997:         PetscCall(DMSetLabelValue_Fast(*dm, &faceSets, "Face Sets", faces[0], fs_id[fs]));
    1998:         /* Only add the label if one has been detected for this side set. */
    1999:         if (!fs_name_err) PetscCall(DMSetLabelValue(*dm, fs_name, faces[0], fs_id[fs]));
    2000:         PetscCall(DMPlexRestoreJoin(*dm, faceSize, faceVertices, &numFaces, &faces));
    2001:       }
    2002:       PetscCall(PetscFree3(fs_vertex_count_list, fs_vertex_list, fs_side_list));
    2003:     }
    2004:     PetscCall(PetscFree(fs_id));
    2005:   }

    2007:   { /* Create Cell/Face/Vertex Sets labels at all processes */
    2008:     enum {
    2009:       n = 3
    2010:     };
    2011:     PetscBool flag[n];

    2013:     flag[0] = cellSets ? PETSC_TRUE : PETSC_FALSE;
    2014:     flag[1] = faceSets ? PETSC_TRUE : PETSC_FALSE;
    2015:     flag[2] = vertSets ? PETSC_TRUE : PETSC_FALSE;
    2016:     PetscCallMPI(MPI_Bcast(flag, n, MPIU_BOOL, 0, comm));
    2017:     if (flag[0]) PetscCall(DMCreateLabel(*dm, "Cell Sets"));
    2018:     if (flag[1]) PetscCall(DMCreateLabel(*dm, "Face Sets"));
    2019:     if (flag[2]) PetscCall(DMCreateLabel(*dm, "Vertex Sets"));
    2020:   }
    2021:   PetscFunctionReturn(PETSC_SUCCESS);
    2022: }