Actual source code: plexexodusii2.c

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

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

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

     11:   Collective; No Fortran Support

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

     16:   Level: intermediate

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

     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 PetscViewerDestroy_ExodusII(PetscViewer viewer)
     71: {
     72:   PetscViewer_ExodusII *exo = (PetscViewer_ExodusII *)viewer->data;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    195:   Collective;

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

    201:   Level: intermediate

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

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

    215:   PetscFunctionBegin;
    216:   PetscCall(PetscObjectGetComm((PetscObject)viewer, &comm));
    217:   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);
    218:   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");

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

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

    231:   Collective;

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

    237:   Level: intermediate

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

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

    251:   PetscFunctionBegin;
    252:   PetscCall(PetscObjectGetComm((PetscObject)viewer, &comm));
    253:   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);
    254:   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");

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

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

    267:   Collective

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

    272:   Output Parameter:
    273: . num - the number variables in the ExodusII file

    275:   Level: intermediate

    277:   Notes:
    278:   The number of variables in the ExodusII file is cached in the viewer

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

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

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

    306:   Collective

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

    311:   Output Parameter:
    312: . num - the number variables in the ExodusII file

    314:   Level: intermediate

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

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

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

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

    345:   Collective;

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

    352:   Level: intermediate

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

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

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

    370:   Collective;

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

    377:   Level: intermediate

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

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

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

    395:   Collective;

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

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

    404:   Level: intermediate

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

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

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

    428:   Collective;

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

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

    437:   Level: intermediate

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

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

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

    461:   Collective; No Fortran Support

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

    467:   Level: intermediate

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

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

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

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

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

    495:   Collective; No Fortran Support

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

    501:   Level: intermediate

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

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

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

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

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

    529:   Collective; No Fortran Support

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

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

    538:   Level: intermediate

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

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

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

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

    571:   Collective; No Fortran Support

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

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

    580:   Level: intermediate

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

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

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

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

    613:   Level: beginner

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

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

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

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

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

    651:   Collective

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

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

    660:   Level: beginner

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

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

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

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

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

    703:   Collective

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

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

    712:   Level: beginner

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    1232:         /* Put side sets */
    1233:         for (fs = 0; fs < num_fs; ++fs) PetscCallExternal(ex_put_set, exo->exoid, EX_SIDE_SET, fsIdx[fs], &elem_list[elem_ind[fs]], &side_list[elem_ind[fs]]);
    1234:         PetscCall(PetscFree3(elem_ind, elem_list, side_list));
    1235:       }
    1236:     }
    1237:     /*
    1238:       close the exodus file
    1239:     */
    1240:     ex_close(exo->exoid);
    1241:     exo->exoid = -1;
    1242:   }
    1243:   PetscCall(PetscSectionDestroy(&coordSection));

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    1651:   Collective

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

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

    1661:   Level: beginner

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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