Actual source code: hdf5v.c

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

  3: static PetscErrorCode PetscViewerHDF5Traverse_Internal(PetscViewer, const char[], PetscBool, PetscBool *, H5O_type_t *);
  4: static PetscErrorCode PetscViewerHDF5HasAttribute_Internal(PetscViewer, const char[], const char[], PetscBool *);
  5: static PetscErrorCode PetscViewerHDF5GetGroup_Internal(PetscViewer, const char *[]);

  7: /*@C
  8:   PetscViewerHDF5GetGroup - Get the current HDF5 group name (full path), set with `PetscViewerHDF5PushGroup()`/`PetscViewerHDF5PopGroup()`.

 10:   Not Collective

 12:   Input Parameters:
 13: + viewer - the `PetscViewer` of type `PETSCVIEWERHDF5`
 14: - path   - (Optional) The path relative to the pushed group

 16:   Output Parameter:
 17: . abspath - The absolute HDF5 path (group)

 19:   Level: intermediate

 21:   Notes:
 22:   If path starts with '/', it is taken as an absolute path overriding currently pushed group, else path is relative to the current pushed group.
 23:   `NULL` or empty path means the current pushed group.

 25:   The output abspath is newly allocated so needs to be freed.

 27: .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `PetscViewerHDF5PushGroup()`, `PetscViewerHDF5PopGroup()`, `PetscViewerHDF5OpenGroup()`, `PetscViewerHDF5WriteGroup()`
 28: @*/
 29: PetscErrorCode PetscViewerHDF5GetGroup(PetscViewer viewer, const char path[], const char *abspath[])
 30: {
 31:   size_t      len;
 32:   PetscBool   relative = PETSC_FALSE;
 33:   const char *group;
 34:   char        buf[PETSC_MAX_PATH_LEN] = "";

 36:   PetscFunctionBegin;
 38:   if (path) PetscAssertPointer(path, 2);
 39:   PetscAssertPointer(abspath, 3);
 40:   PetscCall(PetscViewerHDF5GetGroup_Internal(viewer, &group));
 41:   PetscCall(PetscStrlen(path, &len));
 42:   relative = (PetscBool)(!len || path[0] != '/');
 43:   if (relative) {
 44:     PetscCall(PetscStrncpy(buf, group, sizeof(buf)));
 45:     if (!group || len) PetscCall(PetscStrlcat(buf, "/", sizeof(buf)));
 46:     PetscCall(PetscStrlcat(buf, path, sizeof(buf)));
 47:     PetscCall(PetscStrallocpy(buf, (char **)abspath));
 48:   } else {
 49:     PetscCall(PetscStrallocpy(path, (char **)abspath));
 50:   }
 51:   PetscFunctionReturn(PETSC_SUCCESS);
 52: }

 54: static PetscErrorCode PetscViewerHDF5CheckNamedObject_Internal(PetscViewer viewer, PetscObject obj)
 55: {
 56:   PetscBool has;

 58:   PetscFunctionBegin;
 59:   PetscCall(PetscViewerHDF5HasObject(viewer, obj, &has));
 60:   if (!has) {
 61:     const char *group;
 62:     PetscCall(PetscViewerHDF5GetGroup(viewer, NULL, &group));
 63:     SETERRQ(PetscObjectComm((PetscObject)viewer), PETSC_ERR_FILE_UNEXPECTED, "Object (dataset) \"%s\" not stored in group %s", obj->name, group);
 64:   }
 65:   PetscFunctionReturn(PETSC_SUCCESS);
 66: }

 68: static PetscErrorCode PetscViewerSetFromOptions_HDF5(PetscViewer v, PetscOptionItems PetscOptionsObject)
 69: {
 70:   PetscBool         flg  = PETSC_FALSE, set;
 71:   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)v->data;

 73:   PetscFunctionBegin;
 74:   PetscOptionsHeadBegin(PetscOptionsObject, "HDF5 PetscViewer Options");
 75:   PetscCall(PetscOptionsBool("-viewer_hdf5_base_dimension2", "1d Vectors get 2 dimensions in HDF5", "PetscViewerHDF5SetBaseDimension2", hdf5->basedimension2, &hdf5->basedimension2, NULL));
 76:   PetscCall(PetscOptionsBool("-viewer_hdf5_sp_output", "Force data to be written in single precision", "PetscViewerHDF5SetSPOutput", hdf5->spoutput, &hdf5->spoutput, NULL));
 77:   PetscCall(PetscOptionsBool("-viewer_hdf5_collective", "Enable collective transfer mode", "PetscViewerHDF5SetCollective", flg, &flg, &set));
 78:   if (set) PetscCall(PetscViewerHDF5SetCollective(v, flg));
 79:   flg = PETSC_FALSE;
 80:   PetscCall(PetscOptionsBool("-viewer_hdf5_default_timestepping", "Set default timestepping state", "PetscViewerHDF5SetDefaultTimestepping", flg, &flg, &set));
 81:   if (set) PetscCall(PetscViewerHDF5SetDefaultTimestepping(v, flg));
 82:   PetscCall(PetscOptionsBool("-viewer_hdf5_compress", "Enable compression", "PetscViewerHDF5SetCompress", hdf5->compress, &hdf5->compress, NULL));
 83:   PetscOptionsHeadEnd();
 84:   PetscFunctionReturn(PETSC_SUCCESS);
 85: }

 87: static PetscErrorCode PetscViewerView_HDF5(PetscViewer v, PetscViewer viewer)
 88: {
 89:   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)v->data;
 90:   PetscBool         flg;

 92:   PetscFunctionBegin;
 93:   if (hdf5->filename) PetscCall(PetscViewerASCIIPrintf(viewer, "Filename: %s\n", hdf5->filename));
 94:   PetscCall(PetscViewerASCIIPrintf(viewer, "Vectors with blocksize 1 saved as 2D datasets: %s\n", PetscBools[hdf5->basedimension2]));
 95:   PetscCall(PetscViewerASCIIPrintf(viewer, "Enforce single precision storage: %s\n", PetscBools[hdf5->spoutput]));
 96:   PetscCall(PetscViewerHDF5GetCollective(v, &flg));
 97:   PetscCall(PetscViewerASCIIPrintf(viewer, "MPI-IO transfer mode: %s\n", flg ? "collective" : "independent"));
 98:   PetscCall(PetscViewerASCIIPrintf(viewer, "Default timestepping: %s\n", PetscBools[hdf5->defTimestepping]));
 99:   PetscCall(PetscViewerASCIIPrintf(viewer, "Compression: %s\n", PetscBools[hdf5->compress]));
100:   PetscFunctionReturn(PETSC_SUCCESS);
101: }

103: static PetscErrorCode PetscViewerFileClose_HDF5(PetscViewer viewer)
104: {
105:   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;

107:   PetscFunctionBegin;
108:   PetscCall(PetscFree(hdf5->filename));
109:   if (hdf5->file_id) PetscCallHDF5(H5Fclose, (hdf5->file_id));
110:   PetscFunctionReturn(PETSC_SUCCESS);
111: }

113: static PetscErrorCode PetscViewerFlush_HDF5(PetscViewer viewer)
114: {
115:   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;

117:   PetscFunctionBegin;
118:   if (hdf5->file_id) PetscCallHDF5(H5Fflush, (hdf5->file_id, H5F_SCOPE_LOCAL));
119:   PetscFunctionReturn(PETSC_SUCCESS);
120: }

122: static PetscErrorCode PetscViewerDestroy_HDF5(PetscViewer viewer)
123: {
124:   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;

126:   PetscFunctionBegin;
127:   PetscCallHDF5(H5Pclose, (hdf5->dxpl_id));
128:   PetscCall(PetscViewerFileClose_HDF5(viewer));
129:   while (hdf5->groups) {
130:     PetscViewerHDF5GroupList *tmp = hdf5->groups->next;

132:     PetscCall(PetscFree(hdf5->groups->name));
133:     PetscCall(PetscFree(hdf5->groups));
134:     hdf5->groups = tmp;
135:   }
136:   PetscCall(PetscFree(hdf5));
137:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileSetName_C", NULL));
138:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileGetName_C", NULL));
139:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileSetMode_C", NULL));
140:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileGetMode_C", NULL));
141:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerHDF5SetBaseDimension2_C", NULL));
142:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerHDF5SetSPOutput_C", NULL));
143:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerHDF5SetCollective_C", NULL));
144:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerHDF5GetCollective_C", NULL));
145:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerHDF5GetDefaultTimestepping_C", NULL));
146:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerHDF5SetDefaultTimestepping_C", NULL));
147:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerHDF5SetCompress_C", NULL));
148:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerHDF5GetCompress_C", NULL));
149:   PetscFunctionReturn(PETSC_SUCCESS);
150: }

152: static PetscErrorCode PetscViewerFileSetMode_HDF5(PetscViewer viewer, PetscFileMode type)
153: {
154:   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;

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

161: static PetscErrorCode PetscViewerFileGetMode_HDF5(PetscViewer viewer, PetscFileMode *type)
162: {
163:   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;

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

170: static PetscErrorCode PetscViewerHDF5SetBaseDimension2_HDF5(PetscViewer viewer, PetscBool flg)
171: {
172:   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;

174:   PetscFunctionBegin;
175:   hdf5->basedimension2 = flg;
176:   PetscFunctionReturn(PETSC_SUCCESS);
177: }

179: /*@
180:   PetscViewerHDF5SetBaseDimension2 - Vectors of 1 dimension (i.e. bs/dof is 1) will be saved in the HDF5 file with a
181:   dimension of 2.

183:   Logically Collective

185:   Input Parameters:
186: + viewer - the `PetscViewer`; if it is a `PETSCVIEWERHDF5` then this command is ignored
187: - flg    - if `PETSC_TRUE` the vector will always have at least a dimension of 2 even if that first dimension is of size 1

189:   Options Database Key:
190: . -viewer_hdf5_base_dimension2 - turns on (true) or off (false) using a dimension of 2 in the HDF5 file even if the bs/dof of the vector is 1

192:   Level: intermediate

194:   Note:
195:   Setting this option allegedly makes code that reads the HDF5 in easier since they do not have a "special case" of a bs/dof
196:   of one when the dimension is lower. Others think the option is crazy.

198: .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerFileSetMode()`, `PetscViewerCreate()`, `PetscViewerSetType()`, `PetscViewerBinaryOpen()`
199: @*/
200: PetscErrorCode PetscViewerHDF5SetBaseDimension2(PetscViewer viewer, PetscBool flg)
201: {
202:   PetscFunctionBegin;
204:   PetscTryMethod(viewer, "PetscViewerHDF5SetBaseDimension2_C", (PetscViewer, PetscBool), (viewer, flg));
205:   PetscFunctionReturn(PETSC_SUCCESS);
206: }

208: /*@
209:   PetscViewerHDF5GetBaseDimension2 - Vectors of 1 dimension (i.e. bs/dof is 1) will be saved in the HDF5 file with a
210:   dimension of 2.

212:   Logically Collective

214:   Input Parameter:
215: . viewer - the `PetscViewer`, must be `PETSCVIEWERHDF5`

217:   Output Parameter:
218: . flg - if `PETSC_TRUE` the vector will always have at least a dimension of 2 even if that first dimension is of size 1

220:   Level: intermediate

222:   Note:
223:   Setting this option allegedly makes code that reads the HDF5 in easier since they do not have a "special case" of a bs/dof
224:   of one when the dimension is lower. Others think the option is crazy.

226: .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerFileSetMode()`, `PetscViewerCreate()`, `PetscViewerSetType()`, `PetscViewerBinaryOpen()`
227: @*/
228: PetscErrorCode PetscViewerHDF5GetBaseDimension2(PetscViewer viewer, PetscBool *flg)
229: {
230:   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;

232:   PetscFunctionBegin;
234:   *flg = hdf5->basedimension2;
235:   PetscFunctionReturn(PETSC_SUCCESS);
236: }

238: static PetscErrorCode PetscViewerHDF5SetSPOutput_HDF5(PetscViewer viewer, PetscBool flg)
239: {
240:   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;

242:   PetscFunctionBegin;
243:   hdf5->spoutput = flg;
244:   PetscFunctionReturn(PETSC_SUCCESS);
245: }

247: /*@
248:   PetscViewerHDF5SetSPOutput - Data is written to disk in single precision even if PETSc is
249:   compiled with double precision `PetscReal`.

251:   Logically Collective

253:   Input Parameters:
254: + viewer - the PetscViewer; if it is a `PETSCVIEWERHDF5` then this command is ignored
255: - flg    - if `PETSC_TRUE` the data will be written to disk with single precision

257:   Options Database Key:
258: . -viewer_hdf5_sp_output - turns on (true) or off (false) output in single precision

260:   Level: intermediate

262:   Note:
263:   Setting this option does not make any difference if PETSc is compiled with single precision
264:   in the first place. It does not affect reading datasets (HDF5 handle this internally).

266: .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerFileSetMode()`, `PetscViewerCreate()`, `PetscViewerSetType()`, `PetscViewerBinaryOpen()`,
267:           `PetscReal`, `PetscViewerHDF5GetSPOutput()`
268: @*/
269: PetscErrorCode PetscViewerHDF5SetSPOutput(PetscViewer viewer, PetscBool flg)
270: {
271:   PetscFunctionBegin;
273:   PetscTryMethod(viewer, "PetscViewerHDF5SetSPOutput_C", (PetscViewer, PetscBool), (viewer, flg));
274:   PetscFunctionReturn(PETSC_SUCCESS);
275: }

277: /*@
278:   PetscViewerHDF5GetSPOutput - Data is written to disk in single precision even if PETSc is
279:   compiled with double precision `PetscReal`.

281:   Logically Collective

283:   Input Parameter:
284: . viewer - the PetscViewer, must be of type `PETSCVIEWERHDF5`

286:   Output Parameter:
287: . flg - if `PETSC_TRUE` the data will be written to disk with single precision

289:   Level: intermediate

291: .seealso: [](sec_viewers), `PetscViewerFileSetMode()`, `PetscViewerCreate()`, `PetscViewerSetType()`, `PetscViewerBinaryOpen()`,
292:           `PetscReal`, `PetscViewerHDF5SetSPOutput()`
293: @*/
294: PetscErrorCode PetscViewerHDF5GetSPOutput(PetscViewer viewer, PetscBool *flg)
295: {
296:   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;

298:   PetscFunctionBegin;
300:   *flg = hdf5->spoutput;
301:   PetscFunctionReturn(PETSC_SUCCESS);
302: }

304: static PetscErrorCode PetscViewerHDF5SetCollective_HDF5(PetscViewer viewer, PetscBool flg)
305: {
306:   PetscFunctionBegin;
307:   /* H5FD_MPIO_COLLECTIVE is wrong in hdf5 1.10.2, and is the same as H5FD_MPIO_INDEPENDENT in earlier versions
308:      - see e.g. https://gitlab.cosma.dur.ac.uk/swift/swiftsim/issues/431 */
309: #if H5_VERSION_GE(1, 10, 3) && defined(H5_HAVE_PARALLEL)
310:   {
311:     PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;
312:     PetscCallHDF5(H5Pset_dxpl_mpio, (hdf5->dxpl_id, flg ? H5FD_MPIO_COLLECTIVE : H5FD_MPIO_INDEPENDENT));
313:   }
314: #else
315:   if (flg) PetscCall(PetscPrintf(PetscObjectComm((PetscObject)viewer), "Warning: PetscViewerHDF5SetCollective(viewer,PETSC_TRUE) is ignored for HDF5 versions prior to 1.10.3 or if built without MPI support\n"));
316: #endif
317:   PetscFunctionReturn(PETSC_SUCCESS);
318: }

320: /*@
321:   PetscViewerHDF5SetCollective - Use collective MPI-IO transfer mode for HDF5 reads and writes.

323:   Logically Collective; flg must contain common value

325:   Input Parameters:
326: + viewer - the `PetscViewer`; if it is not `PETSCVIEWERHDF5` then this command is ignored
327: - flg    - `PETSC_TRUE` for collective mode; `PETSC_FALSE` for independent mode (default)

329:   Options Database Key:
330: . -viewer_hdf5_collective - turns on (true) or off (false) collective transfers

332:   Level: intermediate

334:   Note:
335:   Collective mode gives the MPI-IO layer underneath HDF5 a chance to do some additional collective optimizations and hence can perform better.
336:   However, this works correctly only since HDF5 1.10.3 and if HDF5 is installed for MPI; hence, we ignore this setting for older versions.

338:   Developer Notes:
339:   In the HDF5 layer, `PETSC_TRUE` / `PETSC_FALSE` means `H5Pset_dxpl_mpio()` is called with `H5FD_MPIO_COLLECTIVE` / `H5FD_MPIO_INDEPENDENT`, respectively.
340:   This in turn means use of MPI_File_{read,write}_all /  MPI_File_{read,write} in the MPI-IO layer, respectively.
341:   See HDF5 documentation and MPI-IO documentation for details.

343: .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5GetCollective()`, `PetscViewerCreate()`, `PetscViewerSetType()`, `PetscViewerHDF5Open()`
344: @*/
345: PetscErrorCode PetscViewerHDF5SetCollective(PetscViewer viewer, PetscBool flg)
346: {
347:   PetscFunctionBegin;
350:   PetscTryMethod(viewer, "PetscViewerHDF5SetCollective_C", (PetscViewer, PetscBool), (viewer, flg));
351:   PetscFunctionReturn(PETSC_SUCCESS);
352: }

354: static PetscErrorCode PetscViewerHDF5GetCollective_HDF5(PetscViewer viewer, PetscBool *flg)
355: {
356: #if defined(H5_HAVE_PARALLEL)
357:   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;
358:   H5FD_mpio_xfer_t  mode;
359: #endif

361:   PetscFunctionBegin;
362: #if !defined(H5_HAVE_PARALLEL)
363:   *flg = PETSC_FALSE;
364: #else
365:   PetscCallHDF5(H5Pget_dxpl_mpio, (hdf5->dxpl_id, &mode));
366:   *flg = (mode == H5FD_MPIO_COLLECTIVE) ? PETSC_TRUE : PETSC_FALSE;
367: #endif
368:   PetscFunctionReturn(PETSC_SUCCESS);
369: }

371: /*@
372:   PetscViewerHDF5GetCollective - Return flag whether collective MPI-IO transfer mode is used for HDF5 reads and writes.

374:   Not Collective

376:   Input Parameter:
377: . viewer - the `PETSCVIEWERHDF5` `PetscViewer`

379:   Output Parameter:
380: . flg - the flag

382:   Level: intermediate

384:   Note:
385:   This setting works correctly only since HDF5 1.10.3 and if HDF5 was installed for MPI. For older versions, `PETSC_FALSE` will be always returned.
386:   For more details, see `PetscViewerHDF5SetCollective()`.

388: .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5SetCollective()`, `PetscViewerCreate()`, `PetscViewerSetType()`, `PetscViewerHDF5Open()`
389: @*/
390: PetscErrorCode PetscViewerHDF5GetCollective(PetscViewer viewer, PetscBool *flg)
391: {
392:   PetscFunctionBegin;
394:   PetscAssertPointer(flg, 2);

396:   PetscUseMethod(viewer, "PetscViewerHDF5GetCollective_C", (PetscViewer, PetscBool *), (viewer, flg));
397:   PetscFunctionReturn(PETSC_SUCCESS);
398: }

400: static PetscErrorCode PetscViewerFileSetName_HDF5(PetscViewer viewer, const char name[])
401: {
402:   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;
403:   hid_t             plist_id;

405:   PetscFunctionBegin;
406:   if (hdf5->file_id) PetscCallHDF5(H5Fclose, (hdf5->file_id));
407:   if (hdf5->filename) PetscCall(PetscFree(hdf5->filename));
408:   PetscCall(PetscStrallocpy(name, &hdf5->filename));
409:   /* Set up file access property list with parallel I/O access */
410:   PetscCallHDF5Return(plist_id, H5Pcreate, (H5P_FILE_ACCESS));
411: #if defined(H5_HAVE_PARALLEL)
412:   PetscCallHDF5(H5Pset_fapl_mpio, (plist_id, PetscObjectComm((PetscObject)viewer), MPI_INFO_NULL));
413: #endif
414:   /* Create or open the file collectively */
415:   switch (hdf5->btype) {
416:   case FILE_MODE_READ:
417:     if (PetscDefined(USE_DEBUG)) {
418:       PetscMPIInt rank;
419:       PetscBool   flg;

421:       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
422:       if (rank == 0) {
423:         PetscCall(PetscTestFile(hdf5->filename, 'r', &flg));
424:         PetscCheck(flg, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "File %s requested for reading does not exist", hdf5->filename);
425:       }
426:       PetscCallMPI(MPI_Barrier(PetscObjectComm((PetscObject)viewer)));
427:     }
428:     PetscCallHDF5Return(hdf5->file_id, H5Fopen, (name, H5F_ACC_RDONLY, plist_id));
429:     break;
430:   case FILE_MODE_APPEND:
431:   case FILE_MODE_UPDATE: {
432:     PetscBool flg;
433:     PetscCall(PetscTestFile(hdf5->filename, 'r', &flg));
434:     if (flg) PetscCallHDF5Return(hdf5->file_id, H5Fopen, (name, H5F_ACC_RDWR, plist_id));
435:     else PetscCallHDF5Return(hdf5->file_id, H5Fcreate, (name, H5F_ACC_EXCL, H5P_DEFAULT, plist_id));
436:     break;
437:   }
438:   case FILE_MODE_WRITE:
439:     PetscCallHDF5Return(hdf5->file_id, H5Fcreate, (name, H5F_ACC_TRUNC, H5P_DEFAULT, plist_id));
440:     break;
441:   case FILE_MODE_UNDEFINED:
442:     SETERRQ(PetscObjectComm((PetscObject)viewer), PETSC_ERR_ORDER, "Must call PetscViewerFileSetMode() before PetscViewerFileSetName()");
443:   default:
444:     SETERRQ(PetscObjectComm((PetscObject)viewer), PETSC_ERR_SUP, "Unsupported file mode %s", PetscFileModes[hdf5->btype]);
445:   }
446:   PetscCheck(hdf5->file_id >= 0, PETSC_COMM_SELF, PETSC_ERR_LIB, "H5Fcreate failed for %s", name);
447:   PetscCallHDF5(H5Pclose, (plist_id));
448:   PetscCall(PetscViewerHDF5ResetAttachedDMPlexStorageVersion(viewer));
449:   PetscFunctionReturn(PETSC_SUCCESS);
450: }

452: static PetscErrorCode PetscViewerFileGetName_HDF5(PetscViewer viewer, const char **name)
453: {
454:   PetscViewer_HDF5 *vhdf5 = (PetscViewer_HDF5 *)viewer->data;

456:   PetscFunctionBegin;
457:   *name = vhdf5->filename;
458:   PetscFunctionReturn(PETSC_SUCCESS);
459: }

461: static PetscErrorCode PetscViewerSetUp_HDF5(PetscViewer viewer)
462: {
463:   /*
464:   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5*) viewer->data;
465:   */

467:   PetscFunctionBegin;
468:   PetscFunctionReturn(PETSC_SUCCESS);
469: }

471: static PetscErrorCode PetscViewerHDF5SetDefaultTimestepping_HDF5(PetscViewer viewer, PetscBool flg)
472: {
473:   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;

475:   PetscFunctionBegin;
476:   hdf5->defTimestepping = flg;
477:   PetscFunctionReturn(PETSC_SUCCESS);
478: }

480: static PetscErrorCode PetscViewerHDF5GetDefaultTimestepping_HDF5(PetscViewer viewer, PetscBool *flg)
481: {
482:   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;

484:   PetscFunctionBegin;
485:   *flg = hdf5->defTimestepping;
486:   PetscFunctionReturn(PETSC_SUCCESS);
487: }

489: /*@
490:   PetscViewerHDF5SetDefaultTimestepping - Set the flag for default timestepping

492:   Logically Collective

494:   Input Parameters:
495: + viewer - the `PetscViewer`; if it is not `PETSCVIEWERHDF5` then this command is ignored
496: - flg    - if `PETSC_TRUE` we will assume that timestepping is on

498:   Options Database Key:
499: . -viewer_hdf5_default_timestepping - turns on (true) or off (false) default timestepping

501:   Level: intermediate

503:   Note:
504:   If the timestepping attribute is not found for an object, then the default timestepping is used

506: .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5GetDefaultTimestepping()`, `PetscViewerHDF5PushTimestepping()`, `PetscViewerHDF5GetTimestep()`
507: @*/
508: PetscErrorCode PetscViewerHDF5SetDefaultTimestepping(PetscViewer viewer, PetscBool flg)
509: {
510:   PetscFunctionBegin;
512:   PetscTryMethod(viewer, "PetscViewerHDF5SetDefaultTimestepping_C", (PetscViewer, PetscBool), (viewer, flg));
513:   PetscFunctionReturn(PETSC_SUCCESS);
514: }

516: /*@
517:   PetscViewerHDF5GetDefaultTimestepping - Get the flag for default timestepping

519:   Not Collective

521:   Input Parameter:
522: . viewer - the `PetscViewer` of type `PETSCVIEWERHDF5`

524:   Output Parameter:
525: . flg - if `PETSC_TRUE` we will assume that timestepping is on

527:   Level: intermediate

529: .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5SetDefaultTimestepping()`, `PetscViewerHDF5PushTimestepping()`, `PetscViewerHDF5GetTimestep()`
530: @*/
531: PetscErrorCode PetscViewerHDF5GetDefaultTimestepping(PetscViewer viewer, PetscBool *flg)
532: {
533:   PetscFunctionBegin;
535:   PetscUseMethod(viewer, "PetscViewerHDF5GetDefaultTimestepping_C", (PetscViewer, PetscBool *), (viewer, flg));
536:   PetscFunctionReturn(PETSC_SUCCESS);
537: }

539: static PetscErrorCode PetscViewerHDF5SetCompress_HDF5(PetscViewer viewer, PetscBool flg)
540: {
541:   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;

543:   PetscFunctionBegin;
544:   hdf5->compress = flg;
545:   PetscFunctionReturn(PETSC_SUCCESS);
546: }

548: static PetscErrorCode PetscViewerHDF5GetCompress_HDF5(PetscViewer viewer, PetscBool *flg)
549: {
550:   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;

552:   PetscFunctionBegin;
553:   *flg = hdf5->compress;
554:   PetscFunctionReturn(PETSC_SUCCESS);
555: }

557: /*@
558:   PetscViewerHDF5SetCompress - Set the flag for compression

560:   Logically Collective

562:   Input Parameters:
563: + viewer - the `PetscViewer`; if it is not `PETSCVIEWERHDF5` then this command is ignored
564: - flg    - if `PETSC_TRUE` we will turn on compression

566:   Options Database Key:
567: . -viewer_hdf5_compress - turns on (true) or off (false) compression

569:   Level: intermediate

571: .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5GetCompress()`
572: @*/
573: PetscErrorCode PetscViewerHDF5SetCompress(PetscViewer viewer, PetscBool flg)
574: {
575:   PetscFunctionBegin;
577:   PetscTryMethod(viewer, "PetscViewerHDF5SetCompress_C", (PetscViewer, PetscBool), (viewer, flg));
578:   PetscFunctionReturn(PETSC_SUCCESS);
579: }

581: /*@
582:   PetscViewerHDF5GetCompress - Get the flag for compression

584:   Not Collective

586:   Input Parameter:
587: . viewer - the `PetscViewer` of type `PETSCVIEWERHDF5`

589:   Output Parameter:
590: . flg - if `PETSC_TRUE` we will turn on compression

592:   Level: intermediate

594: .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5SetCompress()`
595: @*/
596: PetscErrorCode PetscViewerHDF5GetCompress(PetscViewer viewer, PetscBool *flg)
597: {
598:   PetscFunctionBegin;
600:   PetscUseMethod(viewer, "PetscViewerHDF5GetCompress_C", (PetscViewer, PetscBool *), (viewer, flg));
601:   PetscFunctionReturn(PETSC_SUCCESS);
602: }

604: /*MC
605:    PETSCVIEWERHDF5 - A viewer that writes to an HDF5 file

607:   Level: beginner

609: .seealso: [](sec_viewers), `PetscViewerHDF5Open()`, `PetscViewerStringSPrintf()`, `PetscViewerSocketOpen()`, `PetscViewerDrawOpen()`, `PETSCVIEWERSOCKET`,
610:           `PetscViewerCreate()`, `PetscViewerASCIIOpen()`, `PetscViewerBinaryOpen()`, `PETSCVIEWERBINARY`, `PETSCVIEWERDRAW`, `PETSCVIEWERSTRING`,
611:           `PetscViewerMatlabOpen()`, `VecView()`, `DMView()`, `PetscViewerMatlabPutArray()`, `PETSCVIEWERASCII`, `PETSCVIEWERMATLAB`,
612:           `PetscViewerFileSetName()`, `PetscViewerFileSetMode()`, `PetscViewerFormat`, `PetscViewerType`, `PetscViewerSetType()`
613: M*/

615: PETSC_EXTERN PetscErrorCode PetscViewerCreate_HDF5(PetscViewer v)
616: {
617:   PetscViewer_HDF5 *hdf5;

619:   PetscFunctionBegin;
620: #if !defined(H5_HAVE_PARALLEL)
621:   {
622:     PetscMPIInt size;
623:     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)v), &size));
624:     PetscCheck(size <= 1, PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot use parallel HDF5 viewer since the given HDF5 does not support parallel I/O (H5_HAVE_PARALLEL is unset)");
625:   }
626: #endif

628:   PetscCall(PetscNew(&hdf5));

630:   v->data                = (void *)hdf5;
631:   v->ops->destroy        = PetscViewerDestroy_HDF5;
632:   v->ops->setfromoptions = PetscViewerSetFromOptions_HDF5;
633:   v->ops->setup          = PetscViewerSetUp_HDF5;
634:   v->ops->view           = PetscViewerView_HDF5;
635:   v->ops->flush          = PetscViewerFlush_HDF5;
636:   hdf5->btype            = FILE_MODE_UNDEFINED;
637:   hdf5->filename         = NULL;
638:   hdf5->timestep         = -1;
639:   hdf5->groups           = NULL;

641:   PetscCallHDF5Return(hdf5->dxpl_id, H5Pcreate, (H5P_DATASET_XFER));

643:   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerFileSetName_C", PetscViewerFileSetName_HDF5));
644:   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerFileGetName_C", PetscViewerFileGetName_HDF5));
645:   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerFileSetMode_C", PetscViewerFileSetMode_HDF5));
646:   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerFileGetMode_C", PetscViewerFileGetMode_HDF5));
647:   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerHDF5SetBaseDimension2_C", PetscViewerHDF5SetBaseDimension2_HDF5));
648:   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerHDF5SetSPOutput_C", PetscViewerHDF5SetSPOutput_HDF5));
649:   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerHDF5SetCollective_C", PetscViewerHDF5SetCollective_HDF5));
650:   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerHDF5GetCollective_C", PetscViewerHDF5GetCollective_HDF5));
651:   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerHDF5GetDefaultTimestepping_C", PetscViewerHDF5GetDefaultTimestepping_HDF5));
652:   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerHDF5SetDefaultTimestepping_C", PetscViewerHDF5SetDefaultTimestepping_HDF5));
653:   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerHDF5GetCompress_C", PetscViewerHDF5GetCompress_HDF5));
654:   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerHDF5SetCompress_C", PetscViewerHDF5SetCompress_HDF5));
655:   PetscFunctionReturn(PETSC_SUCCESS);
656: }

658: /*@C
659:   PetscViewerHDF5Open - Opens a file for HDF5 input/output as a `PETSCVIEWERHDF5` `PetscViewer`

661:   Collective

663:   Input Parameters:
664: + comm - MPI communicator
665: . name - name of file
666: - type - type of file

668:   Output Parameter:
669: . hdf5v - `PetscViewer` for HDF5 input/output to use with the specified file

671:   Options Database Keys:
672: + -viewer_hdf5_base_dimension2 - turns on (true) or off (false) using a dimension of 2 in the HDF5 file even if the bs/dof of the vector is 1
673: - -viewer_hdf5_sp_output       - forces (if true) the viewer to write data in single precision independent on the precision of PetscReal

675:   Level: beginner

677:   Notes:
678:   Reading is always available, regardless of the mode. Available modes are
679: .vb
680:   FILE_MODE_READ - open existing HDF5 file for read only access, fail if file does not exist [H5Fopen() with H5F_ACC_RDONLY]
681:   FILE_MODE_WRITE - if file exists, fully overwrite it, else create new HDF5 file [H5FcreateH5Fcreate() with H5F_ACC_TRUNC]
682:   FILE_MODE_APPEND - if file exists, keep existing contents [H5Fopen() with H5F_ACC_RDWR], else create new HDF5 file [H5FcreateH5Fcreate() with H5F_ACC_EXCL]
683:   FILE_MODE_UPDATE - same as FILE_MODE_APPEND
684: .ve

686:   In case of `FILE_MODE_APPEND` / `FILE_MODE_UPDATE`, any stored object (dataset, attribute) can be selectively overwritten if the same fully qualified name (/group/path/to/object) is specified.

688:   This PetscViewer should be destroyed with PetscViewerDestroy().

690: .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerASCIIOpen()`, `PetscViewerPushFormat()`, `PetscViewerDestroy()`, `PetscViewerHDF5SetBaseDimension2()`,
691:           `PetscViewerHDF5SetSPOutput()`, `PetscViewerHDF5GetBaseDimension2()`, `VecView()`, `MatView()`, `VecLoad()`,
692:           `MatLoad()`, `PetscFileMode`, `PetscViewer`, `PetscViewerSetType()`, `PetscViewerFileSetMode()`, `PetscViewerFileSetName()`
693: @*/
694: PetscErrorCode PetscViewerHDF5Open(MPI_Comm comm, const char name[], PetscFileMode type, PetscViewer *hdf5v)
695: {
696:   PetscFunctionBegin;
697:   PetscCall(PetscViewerCreate(comm, hdf5v));
698:   PetscCall(PetscViewerSetType(*hdf5v, PETSCVIEWERHDF5));
699:   PetscCall(PetscViewerFileSetMode(*hdf5v, type));
700:   PetscCall(PetscViewerFileSetName(*hdf5v, name));
701:   PetscCall(PetscViewerSetFromOptions(*hdf5v));
702:   PetscFunctionReturn(PETSC_SUCCESS);
703: }

705: /*@C
706:   PetscViewerHDF5GetFileId - Retrieve the file id, this file ID then can be used in direct HDF5 calls

708:   Not Collective

710:   Input Parameter:
711: . viewer - the `PetscViewer`

713:   Output Parameter:
714: . file_id - The file id

716:   Level: intermediate

718: .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`
719: @*/
720: PetscErrorCode PetscViewerHDF5GetFileId(PetscViewer viewer, hid_t *file_id)
721: {
722:   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;

724:   PetscFunctionBegin;
726:   if (file_id) *file_id = hdf5->file_id;
727:   PetscFunctionReturn(PETSC_SUCCESS);
728: }

730: /*@C
731:   PetscViewerHDF5PushGroup - Set the current HDF5 group for output

733:   Not Collective

735:   Input Parameters:
736: + viewer - the `PetscViewer` of type `PETSCVIEWERHDF5`
737: - name   - The group name

739:   Level: intermediate

741:   Notes:
742:   This is designed to mnemonically resemble the Unix cd command.
743: .vb
744:   If name begins with '/', it is interpreted as an absolute path fully replacing current group, otherwise it is taken as relative to the current group.
745:   `NULL`, empty string, or any sequence of all slashes (e.g. "///") is interpreted as the root group "/".
746:   "." means the current group is pushed again.
747: .ve

749:   Example:
750:   Suppose the current group is "/a".
751: .vb
752:   If name is `NULL`, empty string, or a sequence of all slashes (e.g. "///"), then the new group will be "/".
753:   If name is ".", then the new group will be "/a".
754:   If name is "b", then the new group will be "/a/b".
755:   If name is "/b", then the new group will be "/b".
756: .ve

758:   Developer Notes:
759:   The root group "/" is internally stored as `NULL`.

761: .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `PetscViewerHDF5PopGroup()`, `PetscViewerHDF5GetGroup()`, `PetscViewerHDF5OpenGroup()`, `PetscViewerHDF5WriteGroup()`
762: @*/
763: PetscErrorCode PetscViewerHDF5PushGroup(PetscViewer viewer, const char name[])
764: {
765:   PetscViewer_HDF5         *hdf5 = (PetscViewer_HDF5 *)viewer->data;
766:   PetscViewerHDF5GroupList *groupNode;
767:   size_t                    i, len;
768:   char                      buf[PETSC_MAX_PATH_LEN];
769:   const char               *gname;

771:   PetscFunctionBegin;
773:   if (name) PetscAssertPointer(name, 2);
774:   PetscCall(PetscStrlen(name, &len));
775:   gname = NULL;
776:   if (len) {
777:     if (len == 1 && name[0] == '.') {
778:       /* use current name */
779:       gname = (hdf5->groups && hdf5->groups->name) ? hdf5->groups->name : NULL;
780:     } else if (name[0] == '/') {
781:       /* absolute */
782:       for (i = 1; i < len; i++) {
783:         if (name[i] != '/') {
784:           gname = name;
785:           break;
786:         }
787:       }
788:     } else {
789:       /* relative */
790:       const char *parent = (hdf5->groups && hdf5->groups->name) ? hdf5->groups->name : "";
791:       PetscCall(PetscSNPrintf(buf, sizeof(buf), "%s/%s", parent, name));
792:       gname = buf;
793:     }
794:   }
795:   PetscCall(PetscNew(&groupNode));
796:   PetscCall(PetscStrallocpy(gname, (char **)&groupNode->name));
797:   groupNode->next = hdf5->groups;
798:   hdf5->groups    = groupNode;
799:   PetscFunctionReturn(PETSC_SUCCESS);
800: }

802: /*@
803:   PetscViewerHDF5PopGroup - Return the current HDF5 group for output to the previous value

805:   Not Collective

807:   Input Parameter:
808: . viewer - the `PetscViewer` of type `PETSCVIEWERHDF5`

810:   Level: intermediate

812: .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `PetscViewerHDF5PushGroup()`, `PetscViewerHDF5GetGroup()`, `PetscViewerHDF5OpenGroup()`, `PetscViewerHDF5WriteGroup()`
813: @*/
814: PetscErrorCode PetscViewerHDF5PopGroup(PetscViewer viewer)
815: {
816:   PetscViewer_HDF5         *hdf5 = (PetscViewer_HDF5 *)viewer->data;
817:   PetscViewerHDF5GroupList *groupNode;

819:   PetscFunctionBegin;
821:   PetscCheck(hdf5->groups, PetscObjectComm((PetscObject)viewer), PETSC_ERR_ARG_WRONGSTATE, "HDF5 group stack is empty, cannot pop");
822:   groupNode    = hdf5->groups;
823:   hdf5->groups = hdf5->groups->next;
824:   PetscCall(PetscFree(groupNode->name));
825:   PetscCall(PetscFree(groupNode));
826:   PetscFunctionReturn(PETSC_SUCCESS);
827: }

829: static PetscErrorCode PetscViewerHDF5GetGroup_Internal(PetscViewer viewer, const char *name[])
830: {
831:   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;

833:   PetscFunctionBegin;
835:   PetscAssertPointer(name, 2);
836:   if (hdf5->groups) *name = hdf5->groups->name;
837:   else *name = NULL;
838:   PetscFunctionReturn(PETSC_SUCCESS);
839: }

841: /*@C
842:   PetscViewerHDF5OpenGroup - Open the HDF5 group with the name (full path) returned by `PetscViewerHDF5GetGroup()`,
843:   and return this group's ID and file ID.
844:   If `PetscViewerHDF5GetGroup()` yields NULL, then group ID is file ID.

846:   Not Collective

848:   Input Parameters:
849: + viewer - the `PetscViewer` of type `PETSCVIEWERHDF5`
850: - path   - (Optional) The path relative to the pushed group

852:   Output Parameters:
853: + fileId  - The HDF5 file ID
854: - groupId - The HDF5 group ID

856:   Level: intermediate

858:   Note:
859:   If path starts with '/', it is taken as an absolute path overriding currently pushed group, else path is relative to the current pushed group.
860:   `NULL` or empty path means the current pushed group.

862:   If the viewer is writable, the group is created if it doesn't exist yet.

864: .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `PetscViewerHDF5PushGroup()`, `PetscViewerHDF5PopGroup()`, `PetscViewerHDF5GetGroup()`, `PetscViewerHDF5WriteGroup()`
865: @*/
866: PetscErrorCode PetscViewerHDF5OpenGroup(PetscViewer viewer, const char path[], hid_t *fileId, hid_t *groupId)
867: {
868:   hid_t       file_id;
869:   H5O_type_t  type;
870:   const char *fileName  = NULL;
871:   const char *groupName = NULL;
872:   PetscBool   writable, has;

874:   PetscFunctionBegin;
875:   PetscCall(PetscViewerWritable(viewer, &writable));
876:   PetscCall(PetscViewerHDF5GetFileId(viewer, &file_id));
877:   PetscCall(PetscViewerFileGetName(viewer, &fileName));
878:   PetscCall(PetscViewerHDF5GetGroup(viewer, path, &groupName));
879:   PetscCall(PetscViewerHDF5Traverse_Internal(viewer, groupName, writable, &has, &type));
880:   if (!has) {
881:     PetscCheck(writable, PetscObjectComm((PetscObject)viewer), PETSC_ERR_FILE_UNEXPECTED, "Group %s does not exist and file %s is not open for writing", groupName, fileName);
882:     SETERRQ(PetscObjectComm((PetscObject)viewer), PETSC_ERR_LIB, "HDF5 failed to create group %s although file %s is open for writing", groupName, fileName);
883:   }
884:   PetscCheck(type == H5O_TYPE_GROUP, PetscObjectComm((PetscObject)viewer), PETSC_ERR_FILE_UNEXPECTED, "Path %s in file %s resolves to something which is not a group", groupName, fileName);
885:   PetscCallHDF5Return(*groupId, H5Gopen2, (file_id, groupName, H5P_DEFAULT));
886:   PetscCall(PetscFree(groupName));
887:   *fileId = file_id;
888:   PetscFunctionReturn(PETSC_SUCCESS);
889: }

891: /*@C
892:   PetscViewerHDF5WriteGroup - Ensure the HDF5 group exists in the HDF5 file

894:   Not Collective

896:   Input Parameters:
897: + viewer - the `PetscViewer` of type `PETSCVIEWERHDF5`
898: - path   - (Optional) The path relative to the pushed group

900:   Level: intermediate

902:   Note:
903:   If path starts with '/', it is taken as an absolute path overriding currently pushed group, else path is relative to the current pushed group.
904:   `NULL` or empty path means the current pushed group.

906:   This will fail if the viewer is not writable.

908: .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `PetscViewerHDF5PushGroup()`, `PetscViewerHDF5PopGroup()`, `PetscViewerHDF5GetGroup()`, `PetscViewerHDF5OpenGroup()`
909: @*/
910: PetscErrorCode PetscViewerHDF5WriteGroup(PetscViewer viewer, const char path[])
911: {
912:   hid_t fileId, groupId;

914:   PetscFunctionBegin;
915:   PetscCall(PetscViewerHDF5OpenGroup(viewer, path, &fileId, &groupId)); // make sure group is actually created
916:   PetscCallHDF5(H5Gclose, (groupId));
917:   PetscFunctionReturn(PETSC_SUCCESS);
918: }

920: /*@
921:   PetscViewerHDF5PushTimestepping - Activate timestepping mode for subsequent HDF5 reading and writing.

923:   Not Collective

925:   Input Parameter:
926: . viewer - the `PetscViewer` of type `PETSCVIEWERHDF5`

928:   Level: intermediate

930:   Notes:
931:   On first `PetscViewerHDF5PushTimestepping()`, the initial time step is set to 0.
932:   Next timesteps can then be set using `PetscViewerHDF5IncrementTimestep()` or `PetscViewerHDF5SetTimestep()`.
933:   Current timestep value determines which timestep is read from or written to any dataset on the next HDF5 I/O operation [e.g. `VecView()`].
934:   Use `PetscViewerHDF5PopTimestepping()` to deactivate timestepping mode; calling it by the end of the program is NOT mandatory.
935:   Current timestep is remembered between `PetscViewerHDF5PopTimestepping()` and the next `PetscViewerHDF5PushTimestepping()`.

937:   If a dataset was stored with timestepping, it can be loaded only in the timestepping mode again.
938:   Loading a timestepped dataset with timestepping disabled, or vice-versa results in an error.

940:   Developer Notes:
941:   Timestepped HDF5 dataset has an extra dimension and attribute "timestepping" set to true.

943: .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `PetscViewerHDF5PopTimestepping()`, `PetscViewerHDF5IsTimestepping()`, `PetscViewerHDF5SetTimestep()`, `PetscViewerHDF5IncrementTimestep()`, `PetscViewerHDF5GetTimestep()`
944: @*/
945: PetscErrorCode PetscViewerHDF5PushTimestepping(PetscViewer viewer)
946: {
947:   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;

949:   PetscFunctionBegin;
951:   PetscCheck(!hdf5->timestepping, PetscObjectComm((PetscObject)viewer), PETSC_ERR_ARG_WRONGSTATE, "Timestepping is already pushed");
952:   hdf5->timestepping = PETSC_TRUE;
953:   if (hdf5->timestep < 0) hdf5->timestep = 0;
954:   PetscFunctionReturn(PETSC_SUCCESS);
955: }

957: /*@
958:   PetscViewerHDF5PopTimestepping - Deactivate timestepping mode for subsequent HDF5 reading and writing.

960:   Not Collective

962:   Input Parameter:
963: . viewer - the `PetscViewer` of type `PETSCVIEWERHDF5`

965:   Level: intermediate

967:   Note:
968:   See `PetscViewerHDF5PushTimestepping()` for details.

970: .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `PetscViewerHDF5PushTimestepping()`, `PetscViewerHDF5IsTimestepping()`, `PetscViewerHDF5SetTimestep()`, `PetscViewerHDF5IncrementTimestep()`, `PetscViewerHDF5GetTimestep()`
971: @*/
972: PetscErrorCode PetscViewerHDF5PopTimestepping(PetscViewer viewer)
973: {
974:   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;

976:   PetscFunctionBegin;
978:   PetscCheck(hdf5->timestepping, PetscObjectComm((PetscObject)viewer), PETSC_ERR_ARG_WRONGSTATE, "Timestepping has not been pushed yet. Call PetscViewerHDF5PushTimestepping() first");
979:   hdf5->timestepping = PETSC_FALSE;
980:   PetscFunctionReturn(PETSC_SUCCESS);
981: }

983: /*@
984:   PetscViewerHDF5IsTimestepping - Ask the viewer whether it is in timestepping mode currently.

986:   Not Collective

988:   Input Parameter:
989: . viewer - the `PetscViewer` of type `PETSCVIEWERHDF5`

991:   Output Parameter:
992: . flg - is timestepping active?

994:   Level: intermediate

996:   Note:
997:   See `PetscViewerHDF5PushTimestepping()` for details.

999: .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `PetscViewerHDF5PushTimestepping()`, `PetscViewerHDF5PopTimestepping()`, `PetscViewerHDF5SetTimestep()`, `PetscViewerHDF5IncrementTimestep()`, `PetscViewerHDF5GetTimestep()`
1000: @*/
1001: PetscErrorCode PetscViewerHDF5IsTimestepping(PetscViewer viewer, PetscBool *flg)
1002: {
1003:   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;

1005:   PetscFunctionBegin;
1007:   *flg = hdf5->timestepping;
1008:   PetscFunctionReturn(PETSC_SUCCESS);
1009: }

1011: /*@
1012:   PetscViewerHDF5IncrementTimestep - Increments current timestep for the HDF5 output. Fields are stacked in time.

1014:   Not Collective

1016:   Input Parameter:
1017: . viewer - the `PetscViewer` of type `PETSCVIEWERHDF5`

1019:   Level: intermediate

1021:   Note:
1022:   This can be called only if the viewer is in timestepping mode. See `PetscViewerHDF5PushTimestepping()` for details.

1024: .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `PetscViewerHDF5PushTimestepping()`, `PetscViewerHDF5SetTimestep()`, `PetscViewerHDF5GetTimestep()`
1025: @*/
1026: PetscErrorCode PetscViewerHDF5IncrementTimestep(PetscViewer viewer)
1027: {
1028:   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;

1030:   PetscFunctionBegin;
1032:   PetscCheck(hdf5->timestepping, PetscObjectComm((PetscObject)viewer), PETSC_ERR_ARG_WRONGSTATE, "Timestepping has not been pushed yet. Call PetscViewerHDF5PushTimestepping() first");
1033:   ++hdf5->timestep;
1034:   PetscFunctionReturn(PETSC_SUCCESS);
1035: }

1037: /*@
1038:   PetscViewerHDF5SetTimestep - Set the current timestep for the HDF5 output. Fields are stacked in time.

1040:   Logically Collective

1042:   Input Parameters:
1043: + viewer   - the `PetscViewer` of type `PETSCVIEWERHDF5`
1044: - timestep - The timestep

1046:   Level: intermediate

1048:   Note:
1049:   This can be called only if the viewer is in timestepping mode. See `PetscViewerHDF5PushTimestepping()` for details.

1051: .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `PetscViewerHDF5PushTimestepping()`, `PetscViewerHDF5IncrementTimestep()`, `PetscViewerHDF5GetTimestep()`
1052: @*/
1053: PetscErrorCode PetscViewerHDF5SetTimestep(PetscViewer viewer, PetscInt timestep)
1054: {
1055:   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;

1057:   PetscFunctionBegin;
1060:   PetscCheck(timestep >= 0, PetscObjectComm((PetscObject)viewer), PETSC_ERR_ARG_WRONGSTATE, "Timestep %" PetscInt_FMT " is negative", timestep);
1061:   PetscCheck(hdf5->timestepping, PetscObjectComm((PetscObject)viewer), PETSC_ERR_ARG_WRONGSTATE, "Timestepping has not been pushed yet. Call PetscViewerHDF5PushTimestepping() first");
1062:   hdf5->timestep = timestep;
1063:   PetscFunctionReturn(PETSC_SUCCESS);
1064: }

1066: /*@
1067:   PetscViewerHDF5GetTimestep - Get the current timestep for the HDF5 output. Fields are stacked in time.

1069:   Not Collective

1071:   Input Parameter:
1072: . viewer - the `PetscViewer` of type `PETSCVIEWERHDF5`

1074:   Output Parameter:
1075: . timestep - The timestep

1077:   Level: intermediate

1079:   Note:
1080:   This can be called only if the viewer is in the timestepping mode. See `PetscViewerHDF5PushTimestepping()` for details.

1082: .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `PetscViewerHDF5PushTimestepping()`, `PetscViewerHDF5IncrementTimestep()`, `PetscViewerHDF5SetTimestep()`
1083: @*/
1084: PetscErrorCode PetscViewerHDF5GetTimestep(PetscViewer viewer, PetscInt *timestep)
1085: {
1086:   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;

1088:   PetscFunctionBegin;
1090:   PetscAssertPointer(timestep, 2);
1091:   PetscCheck(hdf5->timestepping, PetscObjectComm((PetscObject)viewer), PETSC_ERR_ARG_WRONGSTATE, "Timestepping has not been pushed yet. Call PetscViewerHDF5PushTimestepping() first");
1092:   *timestep = hdf5->timestep;
1093:   PetscFunctionReturn(PETSC_SUCCESS);
1094: }

1096: /*@C
1097:   PetscDataTypeToHDF5DataType - Converts the PETSc name of a datatype to its HDF5 name.

1099:   Not Collective

1101:   Input Parameter:
1102: . ptype - the PETSc datatype name (for example `PETSC_DOUBLE`)

1104:   Output Parameter:
1105: . htype - the HDF5  datatype

1107:   Level: advanced

1109: .seealso: [](sec_viewers), `PetscDataType`, `PetscHDF5DataTypeToPetscDataType()`
1110: @*/
1111: PetscErrorCode PetscDataTypeToHDF5DataType(PetscDataType ptype, hid_t *htype)
1112: {
1113:   PetscFunctionBegin;
1114:   if (ptype == PETSC_INT)
1115: #if defined(PETSC_USE_64BIT_INDICES)
1116:     *htype = H5T_NATIVE_LLONG;
1117: #else
1118:     *htype = H5T_NATIVE_INT;
1119: #endif
1120:   else if (ptype == PETSC_DOUBLE) *htype = H5T_NATIVE_DOUBLE;
1121:   else if (ptype == PETSC_LONG) *htype = H5T_NATIVE_LONG;
1122:   else if (ptype == PETSC_SHORT) *htype = H5T_NATIVE_SHORT;
1123:   else if (ptype == PETSC_ENUM) *htype = H5T_NATIVE_INT;
1124:   else if (ptype == PETSC_BOOL) *htype = H5T_NATIVE_HBOOL;
1125:   else if (ptype == PETSC_FLOAT) *htype = H5T_NATIVE_FLOAT;
1126:   else if (ptype == PETSC_CHAR) *htype = H5T_NATIVE_CHAR;
1127:   else if (ptype == PETSC_BIT_LOGICAL) *htype = H5T_NATIVE_UCHAR;
1128:   else if (ptype == PETSC_STRING) *htype = H5Tcopy(H5T_C_S1);
1129:   else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Unsupported PETSc datatype");
1130:   PetscFunctionReturn(PETSC_SUCCESS);
1131: }

1133: /*@C
1134:   PetscHDF5DataTypeToPetscDataType - Finds the PETSc name of a datatype from its HDF5 name

1136:   Not Collective

1138:   Input Parameter:
1139: . htype - the HDF5 datatype (for example `H5T_NATIVE_DOUBLE`, ...)

1141:   Output Parameter:
1142: . ptype - the PETSc datatype name (for example `PETSC_DOUBLE`)

1144:   Level: advanced

1146: .seealso: [](sec_viewers), `PetscDataType`
1147: @*/
1148: PetscErrorCode PetscHDF5DataTypeToPetscDataType(hid_t htype, PetscDataType *ptype)
1149: {
1150:   PetscFunctionBegin;
1151: #if defined(PETSC_USE_64BIT_INDICES)
1152:   if (htype == H5T_NATIVE_INT) *ptype = PETSC_LONG;
1153:   else if (htype == H5T_NATIVE_LLONG) *ptype = PETSC_INT;
1154: #else
1155:   if (htype == H5T_NATIVE_INT) *ptype = PETSC_INT;
1156: #endif
1157:   else if (htype == H5T_NATIVE_DOUBLE) *ptype = PETSC_DOUBLE;
1158:   else if (htype == H5T_NATIVE_LONG) *ptype = PETSC_LONG;
1159:   else if (htype == H5T_NATIVE_SHORT) *ptype = PETSC_SHORT;
1160:   else if (htype == H5T_NATIVE_HBOOL) *ptype = PETSC_BOOL;
1161:   else if (htype == H5T_NATIVE_FLOAT) *ptype = PETSC_FLOAT;
1162:   else if (htype == H5T_NATIVE_CHAR) *ptype = PETSC_CHAR;
1163:   else if (htype == H5T_NATIVE_UCHAR) *ptype = PETSC_CHAR;
1164:   else if (htype == H5T_C_S1) *ptype = PETSC_STRING;
1165:   else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Unsupported HDF5 datatype");
1166:   PetscFunctionReturn(PETSC_SUCCESS);
1167: }

1169: /*@C
1170:   PetscViewerHDF5WriteAttribute - Write an attribute

1172:   Collective

1174:   Input Parameters:
1175: + viewer   - The `PETSCVIEWERHDF5` viewer
1176: . parent   - The parent dataset/group name
1177: . name     - The attribute name
1178: . datatype - The attribute type
1179: - value    - The attribute value

1181:   Level: advanced

1183:   Note:
1184:   If parent starts with '/', it is taken as an absolute path overriding currently pushed group, else parent is relative to the current pushed group. NULL means the current pushed group.

1186: .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `PetscViewerHDF5WriteObjectAttribute()`, `PetscViewerHDF5ReadAttribute()`, `PetscViewerHDF5HasAttribute()`,
1187:           `PetscViewerHDF5PushGroup()`, `PetscViewerHDF5PopGroup()`, `PetscViewerHDF5GetGroup()`
1188: @*/
1189: PetscErrorCode PetscViewerHDF5WriteAttribute(PetscViewer viewer, const char parent[], const char name[], PetscDataType datatype, const void *value)
1190: {
1191:   const char *parentAbsPath;
1192:   hid_t       h5, dataspace, obj, attribute, dtype;
1193:   PetscBool   has;

1195:   PetscFunctionBegin;
1197:   if (parent) PetscAssertPointer(parent, 2);
1198:   PetscAssertPointer(name, 3);
1200:   PetscAssertPointer(value, 5);
1201:   PetscCall(PetscViewerHDF5GetGroup(viewer, parent, &parentAbsPath));
1202:   PetscCall(PetscViewerHDF5Traverse_Internal(viewer, parentAbsPath, PETSC_TRUE, NULL, NULL));
1203:   PetscCall(PetscViewerHDF5HasAttribute_Internal(viewer, parentAbsPath, name, &has));
1204:   PetscCall(PetscDataTypeToHDF5DataType(datatype, &dtype));
1205:   if (datatype == PETSC_STRING) {
1206:     size_t len;
1207:     PetscCall(PetscStrlen((const char *)value, &len));
1208:     PetscCallHDF5(H5Tset_size, (dtype, len + 1));
1209:   }
1210:   PetscCall(PetscViewerHDF5GetFileId(viewer, &h5));
1211:   PetscCallHDF5Return(dataspace, H5Screate, (H5S_SCALAR));
1212:   PetscCallHDF5Return(obj, H5Oopen, (h5, parentAbsPath, H5P_DEFAULT));
1213:   if (has) {
1214:     PetscCallHDF5Return(attribute, H5Aopen_name, (obj, name));
1215:   } else {
1216:     PetscCallHDF5Return(attribute, H5Acreate2, (obj, name, dtype, dataspace, H5P_DEFAULT, H5P_DEFAULT));
1217:   }
1218:   PetscCallHDF5(H5Awrite, (attribute, dtype, value));
1219:   if (datatype == PETSC_STRING) PetscCallHDF5(H5Tclose, (dtype));
1220:   PetscCallHDF5(H5Aclose, (attribute));
1221:   PetscCallHDF5(H5Oclose, (obj));
1222:   PetscCallHDF5(H5Sclose, (dataspace));
1223:   PetscCall(PetscFree(parentAbsPath));
1224:   PetscFunctionReturn(PETSC_SUCCESS);
1225: }

1227: /*@C
1228:   PetscViewerHDF5WriteObjectAttribute - Write an attribute to the dataset matching the given `PetscObject` by name

1230:   Collective

1232:   Input Parameters:
1233: + viewer   - The `PETSCVIEWERHDF5` viewer
1234: . obj      - The object whose name is used to lookup the parent dataset, relative to the current group.
1235: . name     - The attribute name
1236: . datatype - The attribute type
1237: - value    - The attribute value

1239:   Level: advanced

1241:   Note:
1242:   This fails if the path current_group/object_name doesn't resolve to a dataset (the path doesn't exist or is not a dataset).
1243:   You might want to check first if it does using `PetscViewerHDF5HasObject()`.

1245: .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `PetscViewerHDF5WriteAttribute()`, `PetscViewerHDF5ReadObjectAttribute()`, `PetscViewerHDF5HasObjectAttribute()`,
1246:           `PetscViewerHDF5HasObject()`, `PetscViewerHDF5PushGroup()`, `PetscViewerHDF5PopGroup()`, `PetscViewerHDF5GetGroup()`
1247: @*/
1248: PetscErrorCode PetscViewerHDF5WriteObjectAttribute(PetscViewer viewer, PetscObject obj, const char name[], PetscDataType datatype, const void *value)
1249: {
1250:   PetscFunctionBegin;
1253:   PetscAssertPointer(name, 3);
1254:   PetscAssertPointer(value, 5);
1255:   PetscCall(PetscViewerHDF5CheckNamedObject_Internal(viewer, obj));
1256:   PetscCall(PetscViewerHDF5WriteAttribute(viewer, obj->name, name, datatype, value));
1257:   PetscFunctionReturn(PETSC_SUCCESS);
1258: }

1260: /*@C
1261:   PetscViewerHDF5ReadAttribute - Read an attribute

1263:   Collective

1265:   Input Parameters:
1266: + viewer       - The `PETSCVIEWERHDF5` viewer
1267: . parent       - The parent dataset/group name
1268: . name         - The attribute name
1269: . datatype     - The attribute type
1270: - defaultValue - The pointer to the default value

1272:   Output Parameter:
1273: . value - The pointer to the read HDF5 attribute value

1275:   Level: advanced

1277:   Notes:
1278:   If defaultValue is `NULL` and the attribute is not found, an error occurs.

1280:   If defaultValue is not `NULL` and the attribute is not found, `defaultValue` is copied to value.

1282:   The pointers `defaultValue` and value can be the same; for instance
1283: .vb
1284:   flg = PETSC_FALSE;
1285:   PetscCall(`PetscViewerHDF5ReadAttribute`(viewer,name,"attr",PETSC_BOOL,&flg,&flg));
1286: .ve
1287:   is valid, but make sure the default value is initialized.

1289:   If the datatype is `PETSC_STRING`, the output string is newly allocated so one must `PetscFree()` it when no longer needed.

1291:   If parent starts with '/', it is taken as an absolute path overriding currently pushed group, else parent is relative to the current pushed group. `NULL` means the current pushed group.

1293: .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `PetscViewerHDF5ReadObjectAttribute()`, `PetscViewerHDF5WriteAttribute()`, `PetscViewerHDF5HasAttribute()`, `PetscViewerHDF5HasObject()`, `PetscViewerHDF5PushGroup()`, `PetscViewerHDF5PopGroup()`, `PetscViewerHDF5GetGroup()`
1294: @*/
1295: PetscErrorCode PetscViewerHDF5ReadAttribute(PetscViewer viewer, const char parent[], const char name[], PetscDataType datatype, const void *defaultValue, void *value)
1296: {
1297:   const char *parentAbsPath;
1298:   hid_t       h5, obj, attribute, dtype;
1299:   PetscBool   has;

1301:   PetscFunctionBegin;
1303:   if (parent) PetscAssertPointer(parent, 2);
1304:   PetscAssertPointer(name, 3);
1305:   if (defaultValue) PetscAssertPointer(defaultValue, 5);
1306:   PetscAssertPointer(value, 6);
1307:   PetscCall(PetscDataTypeToHDF5DataType(datatype, &dtype));
1308:   PetscCall(PetscViewerHDF5GetGroup(viewer, parent, &parentAbsPath));
1309:   PetscCall(PetscViewerHDF5Traverse_Internal(viewer, parentAbsPath, PETSC_FALSE, &has, NULL));
1310:   if (has) PetscCall(PetscViewerHDF5HasAttribute_Internal(viewer, parentAbsPath, name, &has));
1311:   if (!has) {
1312:     if (defaultValue) {
1313:       if (defaultValue != value) {
1314:         if (datatype == PETSC_STRING) {
1315:           PetscCall(PetscStrallocpy(*(char **)defaultValue, (char **)value));
1316:         } else {
1317:           size_t len;
1318:           PetscCallHDF5ReturnNoCheck(len, H5Tget_size, (dtype));
1319:           PetscCall(PetscMemcpy(value, defaultValue, len));
1320:         }
1321:       }
1322:       PetscCall(PetscFree(parentAbsPath));
1323:       PetscFunctionReturn(PETSC_SUCCESS);
1324:     } else SETERRQ(PetscObjectComm((PetscObject)viewer), PETSC_ERR_FILE_UNEXPECTED, "Attribute %s/%s does not exist and default value not provided", parentAbsPath, name);
1325:   }
1326:   PetscCall(PetscViewerHDF5GetFileId(viewer, &h5));
1327:   PetscCallHDF5Return(obj, H5Oopen, (h5, parentAbsPath, H5P_DEFAULT));
1328:   PetscCallHDF5Return(attribute, H5Aopen_name, (obj, name));
1329:   if (datatype == PETSC_STRING) {
1330:     size_t len;
1331:     hid_t  atype;
1332:     PetscCallHDF5Return(atype, H5Aget_type, (attribute));
1333:     PetscCallHDF5ReturnNoCheck(len, H5Tget_size, (atype));
1334:     PetscCall(PetscMalloc((len + 1) * sizeof(char), value));
1335:     PetscCallHDF5(H5Tset_size, (dtype, len + 1));
1336:     PetscCallHDF5(H5Aread, (attribute, dtype, *(char **)value));
1337:   } else {
1338:     PetscCallHDF5(H5Aread, (attribute, dtype, value));
1339:   }
1340:   PetscCallHDF5(H5Aclose, (attribute));
1341:   /* H5Oclose can be used to close groups, datasets, or committed datatypes */
1342:   PetscCallHDF5(H5Oclose, (obj));
1343:   PetscCall(PetscFree(parentAbsPath));
1344:   PetscFunctionReturn(PETSC_SUCCESS);
1345: }

1347: /*@C
1348:   PetscViewerHDF5ReadObjectAttribute - Read an attribute from the dataset matching the given `PetscObject` by name

1350:   Collective

1352:   Input Parameters:
1353: + viewer       - The `PETSCVIEWERHDF5` viewer
1354: . obj          - The object whose name is used to lookup the parent dataset, relative to the current group.
1355: . name         - The attribute name
1356: . datatype     - The attribute type
1357: - defaultValue - The default attribute value

1359:   Output Parameter:
1360: . value - The attribute value

1362:   Level: advanced

1364:   Note:
1365:   This fails if current_group/object_name doesn't resolve to a dataset (the path doesn't exist or is not a dataset).
1366:   You might want to check first if it does using `PetscViewerHDF5HasObject()`.

1368: .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `PetscViewerHDF5ReadAttribute()` `PetscViewerHDF5WriteObjectAttribute()`, `PetscViewerHDF5HasObjectAttribute()`, `PetscViewerHDF5PushGroup()`, `PetscViewerHDF5PopGroup()`, `PetscViewerHDF5GetGroup()`
1369: @*/
1370: PetscErrorCode PetscViewerHDF5ReadObjectAttribute(PetscViewer viewer, PetscObject obj, const char name[], PetscDataType datatype, void *defaultValue, void *value)
1371: {
1372:   PetscFunctionBegin;
1375:   PetscAssertPointer(name, 3);
1376:   PetscAssertPointer(value, 6);
1377:   PetscCall(PetscViewerHDF5CheckNamedObject_Internal(viewer, obj));
1378:   PetscCall(PetscViewerHDF5ReadAttribute(viewer, obj->name, name, datatype, defaultValue, value));
1379:   PetscFunctionReturn(PETSC_SUCCESS);
1380: }

1382: static PetscErrorCode PetscViewerHDF5Traverse_Inner_Internal(hid_t h5, const char name[], PetscBool createGroup, PetscBool *exists_)
1383: {
1384:   htri_t exists;
1385:   hid_t  group;

1387:   PetscFunctionBegin;
1388:   PetscCallHDF5Return(exists, H5Lexists, (h5, name, H5P_DEFAULT));
1389:   if (exists) PetscCallHDF5Return(exists, H5Oexists_by_name, (h5, name, H5P_DEFAULT));
1390:   if (!exists && createGroup) {
1391:     PetscCallHDF5Return(group, H5Gcreate2, (h5, name, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT));
1392:     PetscCallHDF5(H5Gclose, (group));
1393:     exists = PETSC_TRUE;
1394:   }
1395:   *exists_ = (PetscBool)exists;
1396:   PetscFunctionReturn(PETSC_SUCCESS);
1397: }

1399: static PetscErrorCode PetscViewerHDF5Traverse_Internal(PetscViewer viewer, const char name[], PetscBool createGroup, PetscBool *has, H5O_type_t *otype)
1400: {
1401:   const char rootGroupName[] = "/";
1402:   hid_t      h5;
1403:   PetscBool  exists = PETSC_FALSE;
1404:   PetscInt   i;
1405:   int        n;
1406:   char     **hierarchy;
1407:   char       buf[PETSC_MAX_PATH_LEN] = "";

1409:   PetscFunctionBegin;
1411:   if (name) PetscAssertPointer(name, 2);
1412:   else name = rootGroupName;
1413:   if (has) {
1414:     PetscAssertPointer(has, 4);
1415:     *has = PETSC_FALSE;
1416:   }
1417:   if (otype) {
1418:     PetscAssertPointer(otype, 5);
1419:     *otype = H5O_TYPE_UNKNOWN;
1420:   }
1421:   PetscCall(PetscViewerHDF5GetFileId(viewer, &h5));

1423:   /*
1424:      Unfortunately, H5Oexists_by_name() fails if any object in hierarchy is missing.
1425:      Hence, each of them needs to be tested separately:
1426:      1) whether it's a valid link
1427:      2) whether this link resolves to an object
1428:      See H5Oexists_by_name() documentation.
1429:   */
1430:   PetscCall(PetscStrToArray(name, '/', &n, &hierarchy));
1431:   if (!n) {
1432:     /*  Assume group "/" always exists in accordance with HDF5 >= 1.10.0. See H5Lexists() documentation. */
1433:     if (has) *has = PETSC_TRUE;
1434:     if (otype) *otype = H5O_TYPE_GROUP;
1435:     PetscCall(PetscStrToArrayDestroy(n, hierarchy));
1436:     PetscFunctionReturn(PETSC_SUCCESS);
1437:   }
1438:   for (i = 0; i < n; i++) {
1439:     PetscCall(PetscStrlcat(buf, "/", sizeof(buf)));
1440:     PetscCall(PetscStrlcat(buf, hierarchy[i], sizeof(buf)));
1441:     PetscCall(PetscViewerHDF5Traverse_Inner_Internal(h5, buf, createGroup, &exists));
1442:     if (!exists) break;
1443:   }
1444:   PetscCall(PetscStrToArrayDestroy(n, hierarchy));

1446:   /* If the object exists, get its type */
1447:   if (exists && otype) {
1448:     H5O_info_t info;

1450:     /* We could use H5Iget_type() here but that would require opening the object. This way we only need its name. */
1451:     PetscCallHDF5(H5Oget_info_by_name, (h5, name, &info, H5P_DEFAULT));
1452:     *otype = info.type;
1453:   }
1454:   if (has) *has = exists;
1455:   PetscFunctionReturn(PETSC_SUCCESS);
1456: }

1458: /*@C
1459:   PetscViewerHDF5HasGroup - Check whether the current (pushed) group exists in the HDF5 file

1461:   Collective

1463:   Input Parameters:
1464: + viewer - The `PETSCVIEWERHDF5` viewer
1465: - path   - (Optional) The path relative to the pushed group

1467:   Output Parameter:
1468: . has - Flag for group existence

1470:   Level: advanced

1472:   Notes:
1473:   If path starts with '/', it is taken as an absolute path overriding currently pushed group, else path is relative to the current pushed group.
1474:   `NULL` or empty path means the current pushed group.

1476:   If path exists but is not a group, `PETSC_FALSE` is returned.

1478: .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5HasAttribute()`, `PetscViewerHDF5HasDataset()`, `PetscViewerHDF5PushGroup()`, `PetscViewerHDF5PopGroup()`, `PetscViewerHDF5GetGroup()`, `PetscViewerHDF5OpenGroup()`
1479: @*/
1480: PetscErrorCode PetscViewerHDF5HasGroup(PetscViewer viewer, const char path[], PetscBool *has)
1481: {
1482:   H5O_type_t  type;
1483:   const char *abspath;

1485:   PetscFunctionBegin;
1487:   if (path) PetscAssertPointer(path, 2);
1488:   PetscAssertPointer(has, 3);
1489:   PetscCall(PetscViewerHDF5GetGroup(viewer, path, &abspath));
1490:   PetscCall(PetscViewerHDF5Traverse_Internal(viewer, abspath, PETSC_FALSE, NULL, &type));
1491:   *has = (PetscBool)(type == H5O_TYPE_GROUP);
1492:   PetscCall(PetscFree(abspath));
1493:   PetscFunctionReturn(PETSC_SUCCESS);
1494: }

1496: /*@C
1497:   PetscViewerHDF5HasDataset - Check whether a given dataset exists in the HDF5 file

1499:   Collective

1501:   Input Parameters:
1502: + viewer - The `PETSCVIEWERHDF5` viewer
1503: - path   - The dataset path

1505:   Output Parameter:
1506: . has - Flag whether dataset exists

1508:   Level: advanced

1510:   Notes:
1511:   If path starts with '/', it is taken as an absolute path overriding currently pushed group, else path is relative to the current pushed group.

1513:   If `path` is `NULL` or empty, has is set to `PETSC_FALSE`.

1515:   If `path` exists but is not a dataset, has is set to `PETSC_FALSE` as well.

1517: .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5HasObject()`, `PetscViewerHDF5HasAttribute()`, `PetscViewerHDF5HasGroup()`, `PetscViewerHDF5PushGroup()`, `PetscViewerHDF5PopGroup()`, `PetscViewerHDF5GetGroup()`
1518: @*/
1519: PetscErrorCode PetscViewerHDF5HasDataset(PetscViewer viewer, const char path[], PetscBool *has)
1520: {
1521:   H5O_type_t  type;
1522:   const char *abspath;

1524:   PetscFunctionBegin;
1526:   if (path) PetscAssertPointer(path, 2);
1527:   PetscAssertPointer(has, 3);
1528:   PetscCall(PetscViewerHDF5GetGroup(viewer, path, &abspath));
1529:   PetscCall(PetscViewerHDF5Traverse_Internal(viewer, abspath, PETSC_FALSE, NULL, &type));
1530:   *has = (PetscBool)(type == H5O_TYPE_DATASET);
1531:   PetscCall(PetscFree(abspath));
1532:   PetscFunctionReturn(PETSC_SUCCESS);
1533: }

1535: /*@
1536:   PetscViewerHDF5HasObject - Check whether a dataset with the same name as given object exists in the HDF5 file under current group

1538:   Collective

1540:   Input Parameters:
1541: + viewer - The `PETSCVIEWERHDF5` viewer
1542: - obj    - The named object

1544:   Output Parameter:
1545: . has - Flag for dataset existence

1547:   Level: advanced

1549:   Notes:
1550:   If the object is unnamed, an error occurs.

1552:   If the path current_group/object_name exists but is not a dataset, has is set to `PETSC_FALSE` as well.

1554: .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `PetscViewerHDF5HasDataset()`, `PetscViewerHDF5HasAttribute()`, `PetscViewerHDF5PushGroup()`, `PetscViewerHDF5PopGroup()`, `PetscViewerHDF5GetGroup()`
1555: @*/
1556: PetscErrorCode PetscViewerHDF5HasObject(PetscViewer viewer, PetscObject obj, PetscBool *has)
1557: {
1558:   size_t len;

1560:   PetscFunctionBegin;
1563:   PetscAssertPointer(has, 3);
1564:   PetscCall(PetscStrlen(obj->name, &len));
1565:   PetscCheck(len, PetscObjectComm((PetscObject)viewer), PETSC_ERR_ARG_WRONG, "Object must be named");
1566:   PetscCall(PetscViewerHDF5HasDataset(viewer, obj->name, has));
1567:   PetscFunctionReturn(PETSC_SUCCESS);
1568: }

1570: /*@C
1571:   PetscViewerHDF5HasAttribute - Check whether an attribute exists

1573:   Collective

1575:   Input Parameters:
1576: + viewer - The `PETSCVIEWERHDF5` viewer
1577: . parent - The parent dataset/group name
1578: - name   - The attribute name

1580:   Output Parameter:
1581: . has - Flag for attribute existence

1583:   Level: advanced

1585:   Note:
1586:   If parent starts with '/', it is taken as an absolute path overriding currently pushed group, else parent is relative to the current pushed group. `NULL` means the current pushed group.

1588: .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `PetscViewerHDF5HasObjectAttribute()`, `PetscViewerHDF5WriteAttribute()`, `PetscViewerHDF5ReadAttribute()`, `PetscViewerHDF5PushGroup()`, `PetscViewerHDF5PopGroup()`, `PetscViewerHDF5GetGroup()`
1589: @*/
1590: PetscErrorCode PetscViewerHDF5HasAttribute(PetscViewer viewer, const char parent[], const char name[], PetscBool *has)
1591: {
1592:   const char *parentAbsPath;

1594:   PetscFunctionBegin;
1596:   if (parent) PetscAssertPointer(parent, 2);
1597:   PetscAssertPointer(name, 3);
1598:   PetscAssertPointer(has, 4);
1599:   PetscCall(PetscViewerHDF5GetGroup(viewer, parent, &parentAbsPath));
1600:   PetscCall(PetscViewerHDF5Traverse_Internal(viewer, parentAbsPath, PETSC_FALSE, has, NULL));
1601:   if (*has) PetscCall(PetscViewerHDF5HasAttribute_Internal(viewer, parentAbsPath, name, has));
1602:   PetscCall(PetscFree(parentAbsPath));
1603:   PetscFunctionReturn(PETSC_SUCCESS);
1604: }

1606: /*@C
1607:   PetscViewerHDF5HasObjectAttribute - Check whether an attribute is attached to the dataset matching the given `PetscObject` by name

1609:   Collective

1611:   Input Parameters:
1612: + viewer - The `PETSCVIEWERHDF5` viewer
1613: . obj    - The object whose name is used to lookup the parent dataset, relative to the current group.
1614: - name   - The attribute name

1616:   Output Parameter:
1617: . has - Flag for attribute existence

1619:   Level: advanced

1621:   Note:
1622:   This fails if current_group/object_name doesn't resolve to a dataset (the path doesn't exist or is not a dataset).
1623:   You might want to check first if it does using `PetscViewerHDF5HasObject()`.

1625: .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `PetscViewerHDF5HasAttribute()`, `PetscViewerHDF5WriteObjectAttribute()`, `PetscViewerHDF5ReadObjectAttribute()`, `PetscViewerHDF5HasObject()`, `PetscViewerHDF5PushGroup()`, `PetscViewerHDF5PopGroup()`, `PetscViewerHDF5GetGroup()`
1626: @*/
1627: PetscErrorCode PetscViewerHDF5HasObjectAttribute(PetscViewer viewer, PetscObject obj, const char name[], PetscBool *has)
1628: {
1629:   PetscFunctionBegin;
1632:   PetscAssertPointer(name, 3);
1633:   PetscAssertPointer(has, 4);
1634:   PetscCall(PetscViewerHDF5CheckNamedObject_Internal(viewer, obj));
1635:   PetscCall(PetscViewerHDF5HasAttribute(viewer, obj->name, name, has));
1636:   PetscFunctionReturn(PETSC_SUCCESS);
1637: }

1639: static PetscErrorCode PetscViewerHDF5HasAttribute_Internal(PetscViewer viewer, const char parent[], const char name[], PetscBool *has)
1640: {
1641:   hid_t  h5;
1642:   htri_t hhas;

1644:   PetscFunctionBegin;
1645:   PetscCall(PetscViewerHDF5GetFileId(viewer, &h5));
1646:   PetscCallHDF5Return(hhas, H5Aexists_by_name, (h5, parent, name, H5P_DEFAULT));
1647:   *has = hhas ? PETSC_TRUE : PETSC_FALSE;
1648:   PetscFunctionReturn(PETSC_SUCCESS);
1649: }

1651: /*
1652:   The variable Petsc_Viewer_HDF5_keyval is used to indicate an MPI attribute that
1653:   is attached to a communicator, in this case the attribute is a PetscViewer.
1654: */
1655: PetscMPIInt Petsc_Viewer_HDF5_keyval = MPI_KEYVAL_INVALID;

1657: /*@C
1658:   PETSC_VIEWER_HDF5_ - Creates an `PETSCVIEWERHDF5` `PetscViewer` shared by all processors in a communicator.

1660:   Collective

1662:   Input Parameter:
1663: . comm - the MPI communicator to share the `PETSCVIEWERHDF5` `PetscViewer`

1665:   Options Database Key:
1666: . -viewer_hdf5_filename <name> - name of the HDF5 file

1668:   Environmental variable:
1669: . `PETSC_VIEWER_HDF5_FILENAME` - name of the HDF5 file

1671:   Level: intermediate

1673:   Note:
1674:   Unlike almost all other PETSc routines, `PETSC_VIEWER_HDF5_()` does not return
1675:   an error code.  The HDF5 `PetscViewer` is usually used in the form
1676: .vb
1677:   XXXView(XXX object, PETSC_VIEWER_HDF5_(comm));
1678: .ve

1680: .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `PetscViewerCreate()`, `PetscViewerDestroy()`
1681: @*/
1682: PetscViewer PETSC_VIEWER_HDF5_(MPI_Comm comm)
1683: {
1684:   PetscErrorCode ierr;
1685:   PetscMPIInt    mpi_ierr;
1686:   PetscBool      flg;
1687:   PetscMPIInt    iflg;
1688:   PetscViewer    viewer;
1689:   char           fname[PETSC_MAX_PATH_LEN];
1690:   MPI_Comm       ncomm;

1692:   PetscFunctionBegin;
1693:   ierr = PetscCommDuplicate(comm, &ncomm, NULL);
1694:   if (ierr) {
1695:     ierr = PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_HDF5_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
1696:     PetscFunctionReturn(NULL);
1697:   }
1698:   if (Petsc_Viewer_HDF5_keyval == MPI_KEYVAL_INVALID) {
1699:     mpi_ierr = MPI_Comm_create_keyval(MPI_COMM_NULL_COPY_FN, MPI_COMM_NULL_DELETE_FN, &Petsc_Viewer_HDF5_keyval, NULL);
1700:     if (mpi_ierr) {
1701:       ierr = PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_HDF5_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
1702:       PetscFunctionReturn(NULL);
1703:     }
1704:   }
1705:   mpi_ierr = MPI_Comm_get_attr(ncomm, Petsc_Viewer_HDF5_keyval, (void **)&viewer, &iflg);
1706:   if (mpi_ierr) {
1707:     ierr = PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_HDF5_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
1708:     PetscFunctionReturn(NULL);
1709:   }
1710:   if (!iflg) { /* PetscViewer not yet created */
1711:     ierr = PetscOptionsGetenv(ncomm, "PETSC_VIEWER_HDF5_FILENAME", fname, PETSC_MAX_PATH_LEN, &flg);
1712:     if (ierr) {
1713:       ierr = PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_HDF5_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_REPEAT, " ");
1714:       PetscFunctionReturn(NULL);
1715:     }
1716:     if (!flg) {
1717:       ierr = PetscStrncpy(fname, "output.h5", sizeof(fname));
1718:       if (ierr) {
1719:         ierr = PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_HDF5_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_REPEAT, " ");
1720:         PetscFunctionReturn(NULL);
1721:       }
1722:     }
1723:     ierr = PetscViewerHDF5Open(ncomm, fname, FILE_MODE_WRITE, &viewer);
1724:     if (ierr) {
1725:       ierr = PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_HDF5_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_REPEAT, " ");
1726:       PetscFunctionReturn(NULL);
1727:     }
1728:     ierr = PetscObjectRegisterDestroy((PetscObject)viewer);
1729:     if (ierr) {
1730:       ierr = PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_HDF5_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_REPEAT, " ");
1731:       PetscFunctionReturn(NULL);
1732:     }
1733:     mpi_ierr = MPI_Comm_set_attr(ncomm, Petsc_Viewer_HDF5_keyval, (void *)viewer);
1734:     if (mpi_ierr) {
1735:       ierr = PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_HDF5_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
1736:       PetscFunctionReturn(NULL);
1737:     }
1738:   }
1739:   ierr = PetscCommDestroy(&ncomm);
1740:   if (ierr) {
1741:     ierr = PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_HDF5_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_REPEAT, " ");
1742:     PetscFunctionReturn(NULL);
1743:   }
1744:   PetscFunctionReturn(viewer);
1745: }