xref: /petsc/src/sys/classes/viewer/impls/hdf5/hdf5v.c (revision 69f65dfb176f3d3e756fc44d2300fd6791726976)
1 #include <petsc/private/viewerhdf5impl.h> /*I "petscviewerhdf5.h" I*/
2 
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 
6 /*@C
7   PetscViewerHDF5GetGroup - Get the current HDF5 group name (full path), set with `PetscViewerHDF5PushGroup()`/`PetscViewerHDF5PopGroup()`.
8 
9   Not collective
10 
11   Input Parameters:
12 + viewer - the `PetscViewer` of type `PETSCVIEWERHDF5`
13 - path - (Optional) The path relative to the pushed group
14 
15   Output Parameter:
16 . abspath - The absolute HDF5 path (group)
17 
18   Level: intermediate
19 
20   Notes:
21   If path starts with '/', it is taken as an absolute path overriding currently pushed group, else path is relative to the current pushed group.
22   `NULL` or empty path means the current pushed group.
23 
24   The output abspath is newly allocated so needs to be freed.
25 
26 .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `PetscViewerHDF5PushGroup()`, `PetscViewerHDF5PopGroup()`, `PetscViewerHDF5OpenGroup()`, `PetscViewerHDF5WriteGroup()`
27 @*/
28 PetscErrorCode PetscViewerHDF5GetGroup(PetscViewer viewer, const char path[], char *abspath[])
29 {
30   size_t      len;
31   PetscBool   relative = PETSC_FALSE;
32   const char *group;
33   char        buf[PETSC_MAX_PATH_LEN] = "";
34 
35   PetscFunctionBegin;
36   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);
37   if (path) PetscValidCharPointer(path, 2);
38   PetscValidPointer(abspath, 3);
39   PetscCall(PetscViewerHDF5GetGroup_Internal(viewer, &group));
40   PetscCall(PetscStrlen(path, &len));
41   relative = (PetscBool)(!len || path[0] != '/');
42   if (relative) {
43     PetscCall(PetscStrncpy(buf, group, sizeof(buf)));
44     if (!group || len) PetscCall(PetscStrlcat(buf, "/", sizeof(buf)));
45     PetscCall(PetscStrlcat(buf, path, sizeof(buf)));
46     PetscCall(PetscStrallocpy(buf, abspath));
47   } else {
48     PetscCall(PetscStrallocpy(path, abspath));
49   }
50   PetscFunctionReturn(PETSC_SUCCESS);
51 }
52 
53 static PetscErrorCode PetscViewerHDF5CheckNamedObject_Internal(PetscViewer viewer, PetscObject obj)
54 {
55   PetscBool has;
56 
57   PetscFunctionBegin;
58   PetscCall(PetscViewerHDF5HasObject(viewer, obj, &has));
59   if (!has) {
60     char *group;
61     PetscCall(PetscViewerHDF5GetGroup(viewer, NULL, &group));
62     SETERRQ(PetscObjectComm((PetscObject)viewer), PETSC_ERR_FILE_UNEXPECTED, "Object (dataset) \"%s\" not stored in group %s", obj->name, group);
63   }
64   PetscFunctionReturn(PETSC_SUCCESS);
65 }
66 
67 static PetscErrorCode PetscViewerSetFromOptions_HDF5(PetscViewer v, PetscOptionItems *PetscOptionsObject)
68 {
69   PetscBool         flg  = PETSC_FALSE, set;
70   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)v->data;
71 
72   PetscFunctionBegin;
73   PetscOptionsHeadBegin(PetscOptionsObject, "HDF5 PetscViewer Options");
74   PetscCall(PetscOptionsBool("-viewer_hdf5_base_dimension2", "1d Vectors get 2 dimensions in HDF5", "PetscViewerHDF5SetBaseDimension2", hdf5->basedimension2, &hdf5->basedimension2, NULL));
75   PetscCall(PetscOptionsBool("-viewer_hdf5_sp_output", "Force data to be written in single precision", "PetscViewerHDF5SetSPOutput", hdf5->spoutput, &hdf5->spoutput, NULL));
76   PetscCall(PetscOptionsBool("-viewer_hdf5_collective", "Enable collective transfer mode", "PetscViewerHDF5SetCollective", flg, &flg, &set));
77   if (set) PetscCall(PetscViewerHDF5SetCollective(v, flg));
78   flg = PETSC_FALSE;
79   PetscCall(PetscOptionsBool("-viewer_hdf5_default_timestepping", "Set default timestepping state", "PetscViewerHDF5SetDefaultTimestepping", flg, &flg, &set));
80   if (set) PetscCall(PetscViewerHDF5SetDefaultTimestepping(v, flg));
81   PetscOptionsHeadEnd();
82   PetscFunctionReturn(PETSC_SUCCESS);
83 }
84 
85 static PetscErrorCode PetscViewerView_HDF5(PetscViewer v, PetscViewer viewer)
86 {
87   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)v->data;
88   PetscBool         flg;
89 
90   PetscFunctionBegin;
91   if (hdf5->filename) PetscCall(PetscViewerASCIIPrintf(viewer, "Filename: %s\n", hdf5->filename));
92   PetscCall(PetscViewerASCIIPrintf(viewer, "Vectors with blocksize 1 saved as 2D datasets: %s\n", PetscBools[hdf5->basedimension2]));
93   PetscCall(PetscViewerASCIIPrintf(viewer, "Enforce single precision storage: %s\n", PetscBools[hdf5->spoutput]));
94   PetscCall(PetscViewerHDF5GetCollective(v, &flg));
95   PetscCall(PetscViewerASCIIPrintf(viewer, "MPI-IO transfer mode: %s\n", flg ? "collective" : "independent"));
96   PetscCall(PetscViewerASCIIPrintf(viewer, "Default timestepping: %s\n", PetscBools[hdf5->defTimestepping]));
97   PetscFunctionReturn(PETSC_SUCCESS);
98 }
99 
100 static PetscErrorCode PetscViewerFileClose_HDF5(PetscViewer viewer)
101 {
102   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;
103 
104   PetscFunctionBegin;
105   PetscCall(PetscFree(hdf5->filename));
106   if (hdf5->file_id) PetscCallHDF5(H5Fclose, (hdf5->file_id));
107   PetscFunctionReturn(PETSC_SUCCESS);
108 }
109 
110 static PetscErrorCode PetscViewerFlush_HDF5(PetscViewer viewer)
111 {
112   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;
113 
114   PetscFunctionBegin;
115   if (hdf5->file_id) PetscCallHDF5(H5Fflush, (hdf5->file_id, H5F_SCOPE_LOCAL));
116   PetscFunctionReturn(PETSC_SUCCESS);
117 }
118 
119 static PetscErrorCode PetscViewerDestroy_HDF5(PetscViewer viewer)
120 {
121   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;
122 
123   PetscFunctionBegin;
124   PetscCallHDF5(H5Pclose, (hdf5->dxpl_id));
125   PetscCall(PetscViewerFileClose_HDF5(viewer));
126   while (hdf5->groups) {
127     PetscViewerHDF5GroupList *tmp = hdf5->groups->next;
128 
129     PetscCall(PetscFree(hdf5->groups->name));
130     PetscCall(PetscFree(hdf5->groups));
131     hdf5->groups = tmp;
132   }
133   PetscCall(PetscFree(hdf5));
134   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileSetName_C", NULL));
135   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileGetName_C", NULL));
136   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileSetMode_C", NULL));
137   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileGetMode_C", NULL));
138   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerHDF5SetBaseDimension2_C", NULL));
139   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerHDF5SetSPOutput_C", NULL));
140   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerHDF5SetCollective_C", NULL));
141   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerHDF5GetCollective_C", NULL));
142   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerHDF5GetDefaultTimestepping_C", NULL));
143   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerHDF5SetDefaultTimestepping_C", NULL));
144   PetscFunctionReturn(PETSC_SUCCESS);
145 }
146 
147 static PetscErrorCode PetscViewerFileSetMode_HDF5(PetscViewer viewer, PetscFileMode type)
148 {
149   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;
150 
151   PetscFunctionBegin;
152   hdf5->btype = type;
153   PetscFunctionReturn(PETSC_SUCCESS);
154 }
155 
156 static PetscErrorCode PetscViewerFileGetMode_HDF5(PetscViewer viewer, PetscFileMode *type)
157 {
158   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;
159 
160   PetscFunctionBegin;
161   *type = hdf5->btype;
162   PetscFunctionReturn(PETSC_SUCCESS);
163 }
164 
165 static PetscErrorCode PetscViewerHDF5SetBaseDimension2_HDF5(PetscViewer viewer, PetscBool flg)
166 {
167   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;
168 
169   PetscFunctionBegin;
170   hdf5->basedimension2 = flg;
171   PetscFunctionReturn(PETSC_SUCCESS);
172 }
173 
174 /*@
175      PetscViewerHDF5SetBaseDimension2 - Vectors of 1 dimension (i.e. bs/dof is 1) will be saved in the HDF5 file with a
176        dimension of 2.
177 
178     Logically Collective
179 
180   Input Parameters:
181 +  viewer - the `PetscViewer`; if it is a `PETSCVIEWERHDF5` then this command is ignored
182 -  flg - if `PETSC_TRUE` the vector will always have at least a dimension of 2 even if that first dimension is of size 1
183 
184   Options Database Key:
185 .  -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
186 
187   Level: intermediate
188 
189   Note:
190   Setting this option allegedly makes code that reads the HDF5 in easier since they do not have a "special case" of a bs/dof
191   of one when the dimension is lower. Others think the option is crazy.
192 
193 .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, PetscViewerFileSetMode()`, `PetscViewerCreate()`, `PetscViewerSetType()`, `PetscViewerBinaryOpen()`
194 @*/
195 PetscErrorCode PetscViewerHDF5SetBaseDimension2(PetscViewer viewer, PetscBool flg)
196 {
197   PetscFunctionBegin;
198   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);
199   PetscTryMethod(viewer, "PetscViewerHDF5SetBaseDimension2_C", (PetscViewer, PetscBool), (viewer, flg));
200   PetscFunctionReturn(PETSC_SUCCESS);
201 }
202 
203 /*@
204      PetscViewerHDF5GetBaseDimension2 - Vectors of 1 dimension (i.e. bs/dof is 1) will be saved in the HDF5 file with a
205        dimension of 2.
206 
207     Logically Collective
208 
209   Input Parameter:
210 .  viewer - the `PetscViewer`, must be `PETSCVIEWERHDF5`
211 
212   Output Parameter:
213 .  flg - if `PETSC_TRUE` the vector will always have at least a dimension of 2 even if that first dimension is of size 1
214 
215   Level: intermediate
216 
217   Note:
218   Setting this option allegedly makes code that reads the HDF5 in easier since they do not have a "special case" of a bs/dof
219   of one when the dimension is lower. Others think the option is crazy.
220 
221 .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerFileSetMode()`, `PetscViewerCreate()`, `PetscViewerSetType()`, `PetscViewerBinaryOpen()`
222 @*/
223 PetscErrorCode PetscViewerHDF5GetBaseDimension2(PetscViewer viewer, PetscBool *flg)
224 {
225   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;
226 
227   PetscFunctionBegin;
228   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);
229   *flg = hdf5->basedimension2;
230   PetscFunctionReturn(PETSC_SUCCESS);
231 }
232 
233 static PetscErrorCode PetscViewerHDF5SetSPOutput_HDF5(PetscViewer viewer, PetscBool flg)
234 {
235   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;
236 
237   PetscFunctionBegin;
238   hdf5->spoutput = flg;
239   PetscFunctionReturn(PETSC_SUCCESS);
240 }
241 
242 /*@
243      PetscViewerHDF5SetSPOutput - Data is written to disk in single precision even if PETSc is
244        compiled with double precision `PetscReal`.
245 
246     Logically Collective
247 
248   Input Parameters:
249 +  viewer - the PetscViewer; if it is a `PETSCVIEWERHDF5` then this command is ignored
250 -  flg - if `PETSC_TRUE` the data will be written to disk with single precision
251 
252   Options Database Key:
253 .  -viewer_hdf5_sp_output - turns on (true) or off (false) output in single precision
254 
255   Level: intermediate
256 
257   Note:
258     Setting this option does not make any difference if PETSc is compiled with single precision
259          in the first place. It does not affect reading datasets (HDF5 handle this internally).
260 
261 .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerFileSetMode()`, `PetscViewerCreate()`, `PetscViewerSetType()`, `PetscViewerBinaryOpen()`,
262           `PetscReal`, `PetscViewerHDF5GetSPOutput()`
263 @*/
264 PetscErrorCode PetscViewerHDF5SetSPOutput(PetscViewer viewer, PetscBool flg)
265 {
266   PetscFunctionBegin;
267   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);
268   PetscTryMethod(viewer, "PetscViewerHDF5SetSPOutput_C", (PetscViewer, PetscBool), (viewer, flg));
269   PetscFunctionReturn(PETSC_SUCCESS);
270 }
271 
272 /*@
273      PetscViewerHDF5GetSPOutput - Data is written to disk in single precision even if PETSc is
274        compiled with double precision `PetscReal`.
275 
276     Logically Collective
277 
278   Input Parameter:
279 .  viewer - the PetscViewer, must be of type `PETSCVIEWERHDF5`
280 
281   Output Parameter:
282 .  flg - if `PETSC_TRUE` the data will be written to disk with single precision
283 
284   Level: intermediate
285 
286 .seealso: [](sec_viewers), `PetscViewerFileSetMode()`, `PetscViewerCreate()`, `PetscViewerSetType()`, `PetscViewerBinaryOpen()`,
287           `PetscReal`, `PetscViewerHDF5SetSPOutput()`
288 @*/
289 PetscErrorCode PetscViewerHDF5GetSPOutput(PetscViewer viewer, PetscBool *flg)
290 {
291   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;
292 
293   PetscFunctionBegin;
294   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);
295   *flg = hdf5->spoutput;
296   PetscFunctionReturn(PETSC_SUCCESS);
297 }
298 
299 static PetscErrorCode PetscViewerHDF5SetCollective_HDF5(PetscViewer viewer, PetscBool flg)
300 {
301   PetscFunctionBegin;
302   /* H5FD_MPIO_COLLECTIVE is wrong in hdf5 1.10.2, and is the same as H5FD_MPIO_INDEPENDENT in earlier versions
303      - see e.g. https://gitlab.cosma.dur.ac.uk/swift/swiftsim/issues/431 */
304 #if H5_VERSION_GE(1, 10, 3) && defined(H5_HAVE_PARALLEL)
305   {
306     PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;
307     PetscCallHDF5(H5Pset_dxpl_mpio, (hdf5->dxpl_id, flg ? H5FD_MPIO_COLLECTIVE : H5FD_MPIO_INDEPENDENT));
308   }
309 #else
310   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"));
311 #endif
312   PetscFunctionReturn(PETSC_SUCCESS);
313 }
314 
315 /*@
316   PetscViewerHDF5SetCollective - Use collective MPI-IO transfer mode for HDF5 reads and writes.
317 
318   Logically Collective; flg must contain common value
319 
320   Input Parameters:
321 + viewer - the `PetscViewer`; if it is not `PETSCVIEWERHDF5` then this command is ignored
322 - flg - `PETSC_TRUE` for collective mode; `PETSC_FALSE` for independent mode (default)
323 
324   Options Database Key:
325 . -viewer_hdf5_collective - turns on (true) or off (false) collective transfers
326 
327   Level: intermediate
328 
329   Note:
330   Collective mode gives the MPI-IO layer underneath HDF5 a chance to do some additional collective optimizations and hence can perform better.
331   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.
332 
333   Developer Note:
334   In the HDF5 layer, `PETSC_TRUE` / `PETSC_FALSE` means `H5Pset_dxpl_mpio()` is called with `H5FD_MPIO_COLLECTIVE` / `H5FD_MPIO_INDEPENDENT`, respectively.
335   This in turn means use of MPI_File_{read,write}_all /  MPI_File_{read,write} in the MPI-IO layer, respectively.
336   See HDF5 documentation and MPI-IO documentation for details.
337 
338 .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5GetCollective()`, `PetscViewerCreate()`, `PetscViewerSetType()`, `PetscViewerHDF5Open()`
339 @*/
340 PetscErrorCode PetscViewerHDF5SetCollective(PetscViewer viewer, PetscBool flg)
341 {
342   PetscFunctionBegin;
343   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);
344   PetscValidLogicalCollectiveBool(viewer, flg, 2);
345   PetscTryMethod(viewer, "PetscViewerHDF5SetCollective_C", (PetscViewer, PetscBool), (viewer, flg));
346   PetscFunctionReturn(PETSC_SUCCESS);
347 }
348 
349 static PetscErrorCode PetscViewerHDF5GetCollective_HDF5(PetscViewer viewer, PetscBool *flg)
350 {
351 #if defined(H5_HAVE_PARALLEL)
352   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;
353   H5FD_mpio_xfer_t  mode;
354 #endif
355 
356   PetscFunctionBegin;
357 #if !defined(H5_HAVE_PARALLEL)
358   *flg = PETSC_FALSE;
359 #else
360   PetscCallHDF5(H5Pget_dxpl_mpio, (hdf5->dxpl_id, &mode));
361   *flg = (mode == H5FD_MPIO_COLLECTIVE) ? PETSC_TRUE : PETSC_FALSE;
362 #endif
363   PetscFunctionReturn(PETSC_SUCCESS);
364 }
365 
366 /*@
367   PetscViewerHDF5GetCollective - Return flag whether collective MPI-IO transfer mode is used for HDF5 reads and writes.
368 
369   Not Collective
370 
371   Input Parameters:
372 . viewer - the `PETSCVIEWERHDF5` `PetscViewer`
373 
374   Output Parameters:
375 . flg - the flag
376 
377   Level: intermediate
378 
379   Note:
380   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.
381   For more details, see `PetscViewerHDF5SetCollective()`.
382 
383 .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5SetCollective()`, `PetscViewerCreate()`, `PetscViewerSetType()`, `PetscViewerHDF5Open()`
384 @*/
385 PetscErrorCode PetscViewerHDF5GetCollective(PetscViewer viewer, PetscBool *flg)
386 {
387   PetscFunctionBegin;
388   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);
389   PetscValidBoolPointer(flg, 2);
390 
391   PetscUseMethod(viewer, "PetscViewerHDF5GetCollective_C", (PetscViewer, PetscBool *), (viewer, flg));
392   PetscFunctionReturn(PETSC_SUCCESS);
393 }
394 
395 static PetscErrorCode PetscViewerFileSetName_HDF5(PetscViewer viewer, const char name[])
396 {
397   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;
398   hid_t             plist_id;
399 
400   PetscFunctionBegin;
401   if (hdf5->file_id) PetscCallHDF5(H5Fclose, (hdf5->file_id));
402   if (hdf5->filename) PetscCall(PetscFree(hdf5->filename));
403   PetscCall(PetscStrallocpy(name, &hdf5->filename));
404   /* Set up file access property list with parallel I/O access */
405   PetscCallHDF5Return(plist_id, H5Pcreate, (H5P_FILE_ACCESS));
406 #if defined(H5_HAVE_PARALLEL)
407   PetscCallHDF5(H5Pset_fapl_mpio, (plist_id, PetscObjectComm((PetscObject)viewer), MPI_INFO_NULL));
408 #endif
409   /* Create or open the file collectively */
410   switch (hdf5->btype) {
411   case FILE_MODE_READ:
412     if (PetscDefined(USE_DEBUG)) {
413       PetscMPIInt rank;
414       PetscBool   flg;
415 
416       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
417       if (rank == 0) {
418         PetscCall(PetscTestFile(hdf5->filename, 'r', &flg));
419         PetscCheck(flg, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "File %s requested for reading does not exist", hdf5->filename);
420       }
421       PetscCallMPI(MPI_Barrier(PetscObjectComm((PetscObject)viewer)));
422     }
423     PetscCallHDF5Return(hdf5->file_id, H5Fopen, (name, H5F_ACC_RDONLY, plist_id));
424     break;
425   case FILE_MODE_APPEND:
426   case FILE_MODE_UPDATE: {
427     PetscBool flg;
428     PetscCall(PetscTestFile(hdf5->filename, 'r', &flg));
429     if (flg) PetscCallHDF5Return(hdf5->file_id, H5Fopen, (name, H5F_ACC_RDWR, plist_id));
430     else PetscCallHDF5Return(hdf5->file_id, H5Fcreate, (name, H5F_ACC_EXCL, H5P_DEFAULT, plist_id));
431     break;
432   }
433   case FILE_MODE_WRITE:
434     PetscCallHDF5Return(hdf5->file_id, H5Fcreate, (name, H5F_ACC_TRUNC, H5P_DEFAULT, plist_id));
435     break;
436   case FILE_MODE_UNDEFINED:
437     SETERRQ(PetscObjectComm((PetscObject)viewer), PETSC_ERR_ORDER, "Must call PetscViewerFileSetMode() before PetscViewerFileSetName()");
438   default:
439     SETERRQ(PetscObjectComm((PetscObject)viewer), PETSC_ERR_SUP, "Unsupported file mode %s", PetscFileModes[hdf5->btype]);
440   }
441   PetscCheck(hdf5->file_id >= 0, PETSC_COMM_SELF, PETSC_ERR_LIB, "H5Fcreate failed for %s", name);
442   PetscCallHDF5(H5Pclose, (plist_id));
443   PetscCall(PetscViewerHDF5ResetAttachedDMPlexStorageVersion(viewer));
444   PetscFunctionReturn(PETSC_SUCCESS);
445 }
446 
447 static PetscErrorCode PetscViewerFileGetName_HDF5(PetscViewer viewer, const char **name)
448 {
449   PetscViewer_HDF5 *vhdf5 = (PetscViewer_HDF5 *)viewer->data;
450 
451   PetscFunctionBegin;
452   *name = vhdf5->filename;
453   PetscFunctionReturn(PETSC_SUCCESS);
454 }
455 
456 static PetscErrorCode PetscViewerSetUp_HDF5(PetscViewer viewer)
457 {
458   /*
459   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5*) viewer->data;
460   */
461 
462   PetscFunctionBegin;
463   PetscFunctionReturn(PETSC_SUCCESS);
464 }
465 
466 static PetscErrorCode PetscViewerHDF5SetDefaultTimestepping_HDF5(PetscViewer viewer, PetscBool flg)
467 {
468   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;
469 
470   PetscFunctionBegin;
471   hdf5->defTimestepping = flg;
472   PetscFunctionReturn(PETSC_SUCCESS);
473 }
474 
475 static PetscErrorCode PetscViewerHDF5GetDefaultTimestepping_HDF5(PetscViewer viewer, PetscBool *flg)
476 {
477   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;
478 
479   PetscFunctionBegin;
480   *flg = hdf5->defTimestepping;
481   PetscFunctionReturn(PETSC_SUCCESS);
482 }
483 
484 /*@
485   PetscViewerHDF5SetDefaultTimestepping - Set the flag for default timestepping
486 
487   Logically Collective
488 
489   Input Parameters:
490 + viewer - the `PetscViewer`; if it is not `PETSCVIEWERHDF5` then this command is ignored
491 - flg    - if `PETSC_TRUE` we will assume that timestepping is on
492 
493   Options Database Key:
494 . -viewer_hdf5_default_timestepping - turns on (true) or off (false) default timestepping
495 
496   Level: intermediate
497 
498   Note:
499   If the timestepping attribute is not found for an object, then the default timestepping is used
500 
501 .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5GetDefaultTimestepping()`, `PetscViewerHDF5PushTimestepping()`, `PetscViewerHDF5GetTimestep()`
502 @*/
503 PetscErrorCode PetscViewerHDF5SetDefaultTimestepping(PetscViewer viewer, PetscBool flg)
504 {
505   PetscFunctionBegin;
506   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);
507   PetscTryMethod(viewer, "PetscViewerHDF5SetDefaultTimestepping_C", (PetscViewer, PetscBool), (viewer, flg));
508   PetscFunctionReturn(PETSC_SUCCESS);
509 }
510 
511 /*@
512   PetscViewerHDF5GetDefaultTimestepping - Get the flag for default timestepping
513 
514   Not collective
515 
516   Input Parameter:
517 . viewer - the `PetscViewer` of type `PETSCVIEWERHDF5`
518 
519   Output Parameter:
520 . flg    - if `PETSC_TRUE` we will assume that timestepping is on
521 
522   Level: intermediate
523 
524 .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5SetDefaultTimestepping()`, `PetscViewerHDF5PushTimestepping()`, `PetscViewerHDF5GetTimestep()`
525 @*/
526 PetscErrorCode PetscViewerHDF5GetDefaultTimestepping(PetscViewer viewer, PetscBool *flg)
527 {
528   PetscFunctionBegin;
529   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);
530   PetscUseMethod(viewer, "PetscViewerHDF5GetDefaultTimestepping_C", (PetscViewer, PetscBool *), (viewer, flg));
531   PetscFunctionReturn(PETSC_SUCCESS);
532 }
533 
534 /*MC
535    PETSCVIEWERHDF5 - A viewer that writes to an HDF5 file
536 
537   Level: beginner
538 
539 .seealso: [](sec_viewers), `PetscViewerHDF5Open()`, `PetscViewerStringSPrintf()`, `PetscViewerSocketOpen()`, `PetscViewerDrawOpen()`, `PETSCVIEWERSOCKET`,
540           `PetscViewerCreate()`, `PetscViewerASCIIOpen()`, `PetscViewerBinaryOpen()`, `PETSCVIEWERBINARY`, `PETSCVIEWERDRAW`, `PETSCVIEWERSTRING`,
541           `PetscViewerMatlabOpen()`, `VecView()`, `DMView()`, `PetscViewerMatlabPutArray()`, `PETSCVIEWERASCII`, `PETSCVIEWERMATLAB`,
542           `PetscViewerFileSetName()`, `PetscViewerFileSetMode()`, `PetscViewerFormat`, `PetscViewerType`, `PetscViewerSetType()`
543 M*/
544 
545 PETSC_EXTERN PetscErrorCode PetscViewerCreate_HDF5(PetscViewer v)
546 {
547   PetscViewer_HDF5 *hdf5;
548 
549   PetscFunctionBegin;
550 #if !defined(H5_HAVE_PARALLEL)
551   {
552     PetscMPIInt size;
553     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)v), &size));
554     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)");
555   }
556 #endif
557 
558   PetscCall(PetscNew(&hdf5));
559 
560   v->data                = (void *)hdf5;
561   v->ops->destroy        = PetscViewerDestroy_HDF5;
562   v->ops->setfromoptions = PetscViewerSetFromOptions_HDF5;
563   v->ops->setup          = PetscViewerSetUp_HDF5;
564   v->ops->view           = PetscViewerView_HDF5;
565   v->ops->flush          = PetscViewerFlush_HDF5;
566   hdf5->btype            = FILE_MODE_UNDEFINED;
567   hdf5->filename         = NULL;
568   hdf5->timestep         = -1;
569   hdf5->groups           = NULL;
570 
571   PetscCallHDF5Return(hdf5->dxpl_id, H5Pcreate, (H5P_DATASET_XFER));
572 
573   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerFileSetName_C", PetscViewerFileSetName_HDF5));
574   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerFileGetName_C", PetscViewerFileGetName_HDF5));
575   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerFileSetMode_C", PetscViewerFileSetMode_HDF5));
576   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerFileGetMode_C", PetscViewerFileGetMode_HDF5));
577   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerHDF5SetBaseDimension2_C", PetscViewerHDF5SetBaseDimension2_HDF5));
578   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerHDF5SetSPOutput_C", PetscViewerHDF5SetSPOutput_HDF5));
579   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerHDF5SetCollective_C", PetscViewerHDF5SetCollective_HDF5));
580   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerHDF5GetCollective_C", PetscViewerHDF5GetCollective_HDF5));
581   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerHDF5GetDefaultTimestepping_C", PetscViewerHDF5GetDefaultTimestepping_HDF5));
582   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerHDF5SetDefaultTimestepping_C", PetscViewerHDF5SetDefaultTimestepping_HDF5));
583   PetscFunctionReturn(PETSC_SUCCESS);
584 }
585 
586 /*@C
587    PetscViewerHDF5Open - Opens a file for HDF5 input/output as a `PETSCVIEWERHDF5` `PetscViewer`
588 
589    Collective
590 
591    Input Parameters:
592 +  comm - MPI communicator
593 .  name - name of file
594 -  type - type of file
595 
596    Output Parameter:
597 .  hdf5v - `PetscViewer` for HDF5 input/output to use with the specified file
598 
599   Options Database Keys:
600 +  -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
601 -  -viewer_hdf5_sp_output - forces (if true) the viewer to write data in single precision independent on the precision of PetscReal
602 
603    Level: beginner
604 
605    Notes:
606    Reading is always available, regardless of the mode. Available modes are
607 .vb
608   FILE_MODE_READ - open existing HDF5 file for read only access, fail if file does not exist [H5Fopen() with H5F_ACC_RDONLY]
609   FILE_MODE_WRITE - if file exists, fully overwrite it, else create new HDF5 file [H5FcreateH5Fcreate() with H5F_ACC_TRUNC]
610   FILE_MODE_APPEND - if file exists, keep existing contents [H5Fopen() with H5F_ACC_RDWR], else create new HDF5 file [H5FcreateH5Fcreate() with H5F_ACC_EXCL]
611   FILE_MODE_UPDATE - same as FILE_MODE_APPEND
612 .ve
613 
614    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.
615 
616    This PetscViewer should be destroyed with PetscViewerDestroy().
617 
618 .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerASCIIOpen()`, `PetscViewerPushFormat()`, `PetscViewerDestroy()`, `PetscViewerHDF5SetBaseDimension2()`,
619           `PetscViewerHDF5SetSPOutput()`, `PetscViewerHDF5GetBaseDimension2()`, `VecView()`, `MatView()`, `VecLoad()`,
620           `MatLoad()`, `PetscFileMode`, `PetscViewer`, `PetscViewerSetType()`, `PetscViewerFileSetMode()`, `PetscViewerFileSetName()`
621 @*/
622 PetscErrorCode PetscViewerHDF5Open(MPI_Comm comm, const char name[], PetscFileMode type, PetscViewer *hdf5v)
623 {
624   PetscFunctionBegin;
625   PetscCall(PetscViewerCreate(comm, hdf5v));
626   PetscCall(PetscViewerSetType(*hdf5v, PETSCVIEWERHDF5));
627   PetscCall(PetscViewerFileSetMode(*hdf5v, type));
628   PetscCall(PetscViewerFileSetName(*hdf5v, name));
629   PetscCall(PetscViewerSetFromOptions(*hdf5v));
630   PetscFunctionReturn(PETSC_SUCCESS);
631 }
632 
633 /*@C
634   PetscViewerHDF5GetFileId - Retrieve the file id, this file ID then can be used in direct HDF5 calls
635 
636   Not collective
637 
638   Input Parameter:
639 . viewer - the `PetscViewer`
640 
641   Output Parameter:
642 . file_id - The file id
643 
644   Level: intermediate
645 
646 .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`
647 @*/
648 PetscErrorCode PetscViewerHDF5GetFileId(PetscViewer viewer, hid_t *file_id)
649 {
650   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;
651 
652   PetscFunctionBegin;
653   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);
654   if (file_id) *file_id = hdf5->file_id;
655   PetscFunctionReturn(PETSC_SUCCESS);
656 }
657 
658 /*@C
659   PetscViewerHDF5PushGroup - Set the current HDF5 group for output
660 
661   Not collective
662 
663   Input Parameters:
664 + viewer - the `PetscViewer` of type `PETSCVIEWERHDF5`
665 - name - The group name
666 
667   Level: intermediate
668 
669   Notes:
670   This is designed to mnemonically resemble the Unix cd command.
671 .vb
672   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.
673   `NULL`, empty string, or any sequence of all slashes (e.g. "///") is interpreted as the root group "/".
674   "." means the current group is pushed again.
675 .ve
676 
677   Example:
678   Suppose the current group is "/a".
679 .vb
680   If name is `NULL`, empty string, or a sequence of all slashes (e.g. "///"), then the new group will be "/".
681   If name is ".", then the new group will be "/a".
682   If name is "b", then the new group will be "/a/b".
683   If name is "/b", then the new group will be "/b".
684 .ve
685 
686   Developer Note:
687   The root group "/" is internally stored as `NULL`.
688 
689 .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `PetscViewerHDF5PopGroup()`, `PetscViewerHDF5GetGroup()`, `PetscViewerHDF5OpenGroup()`, `PetscViewerHDF5WriteGroup()`
690 @*/
691 PetscErrorCode PetscViewerHDF5PushGroup(PetscViewer viewer, const char name[])
692 {
693   PetscViewer_HDF5         *hdf5 = (PetscViewer_HDF5 *)viewer->data;
694   PetscViewerHDF5GroupList *groupNode;
695   size_t                    i, len;
696   char                      buf[PETSC_MAX_PATH_LEN];
697   const char               *gname;
698 
699   PetscFunctionBegin;
700   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);
701   if (name) PetscValidCharPointer(name, 2);
702   PetscCall(PetscStrlen(name, &len));
703   gname = NULL;
704   if (len) {
705     if (len == 1 && name[0] == '.') {
706       /* use current name */
707       gname = (hdf5->groups && hdf5->groups->name) ? hdf5->groups->name : NULL;
708     } else if (name[0] == '/') {
709       /* absolute */
710       for (i = 1; i < len; i++) {
711         if (name[i] != '/') {
712           gname = name;
713           break;
714         }
715       }
716     } else {
717       /* relative */
718       const char *parent = (hdf5->groups && hdf5->groups->name) ? hdf5->groups->name : "";
719       PetscCall(PetscSNPrintf(buf, sizeof(buf), "%s/%s", parent, name));
720       gname = buf;
721     }
722   }
723   PetscCall(PetscNew(&groupNode));
724   PetscCall(PetscStrallocpy(gname, (char **)&groupNode->name));
725   groupNode->next = hdf5->groups;
726   hdf5->groups    = groupNode;
727   PetscFunctionReturn(PETSC_SUCCESS);
728 }
729 
730 /*@
731   PetscViewerHDF5PopGroup - Return the current HDF5 group for output to the previous value
732 
733   Not collective
734 
735   Input Parameter:
736 . viewer - the `PetscViewer` of type `PETSCVIEWERHDF5`
737 
738   Level: intermediate
739 
740 .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `PetscViewerHDF5PushGroup()`, `PetscViewerHDF5GetGroup()`, `PetscViewerHDF5OpenGroup()`, `PetscViewerHDF5WriteGroup()`
741 @*/
742 PetscErrorCode PetscViewerHDF5PopGroup(PetscViewer viewer)
743 {
744   PetscViewer_HDF5         *hdf5 = (PetscViewer_HDF5 *)viewer->data;
745   PetscViewerHDF5GroupList *groupNode;
746 
747   PetscFunctionBegin;
748   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);
749   PetscCheck(hdf5->groups, PetscObjectComm((PetscObject)viewer), PETSC_ERR_ARG_WRONGSTATE, "HDF5 group stack is empty, cannot pop");
750   groupNode    = hdf5->groups;
751   hdf5->groups = hdf5->groups->next;
752   PetscCall(PetscFree(groupNode->name));
753   PetscCall(PetscFree(groupNode));
754   PetscFunctionReturn(PETSC_SUCCESS);
755 }
756 
757 PetscErrorCode PetscViewerHDF5GetGroup_Internal(PetscViewer viewer, const char *name[])
758 {
759   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;
760 
761   PetscFunctionBegin;
762   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);
763   PetscValidPointer(name, 2);
764   if (hdf5->groups) *name = hdf5->groups->name;
765   else *name = NULL;
766   PetscFunctionReturn(PETSC_SUCCESS);
767 }
768 
769 /*@C
770   PetscViewerHDF5OpenGroup - Open the HDF5 group with the name (full path) returned by `PetscViewerHDF5GetGroup()`,
771   and return this group's ID and file ID.
772   If `PetscViewerHDF5GetGroup()` yields NULL, then group ID is file ID.
773 
774   Not collective
775 
776   Input Parameters:
777 + viewer - the `PetscViewer` of type `PETSCVIEWERHDF5`
778 - path - (Optional) The path relative to the pushed group
779 
780   Output Parameters:
781 + fileId - The HDF5 file ID
782 - groupId - The HDF5 group ID
783 
784   Level: intermediate
785 
786   Note:
787   If path starts with '/', it is taken as an absolute path overriding currently pushed group, else path is relative to the current pushed group.
788   `NULL` or empty path means the current pushed group.
789 
790   If the viewer is writable, the group is created if it doesn't exist yet.
791 
792 .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `PetscViewerHDF5PushGroup()`, `PetscViewerHDF5PopGroup()`, `PetscViewerHDF5GetGroup()`, `PetscViewerHDF5WriteGroup()`
793 @*/
794 PetscErrorCode PetscViewerHDF5OpenGroup(PetscViewer viewer, const char path[], hid_t *fileId, hid_t *groupId)
795 {
796   hid_t       file_id;
797   H5O_type_t  type;
798   const char *fileName  = NULL;
799   char       *groupName = NULL;
800   PetscBool   writable, has;
801 
802   PetscFunctionBegin;
803   PetscCall(PetscViewerWritable(viewer, &writable));
804   PetscCall(PetscViewerHDF5GetFileId(viewer, &file_id));
805   PetscCall(PetscViewerFileGetName(viewer, &fileName));
806   PetscCall(PetscViewerHDF5GetGroup(viewer, path, &groupName));
807   PetscCall(PetscViewerHDF5Traverse_Internal(viewer, groupName, writable, &has, &type));
808   if (!has) {
809     PetscCheck(writable, PetscObjectComm((PetscObject)viewer), PETSC_ERR_FILE_UNEXPECTED, "Group %s does not exist and file %s is not open for writing", groupName, fileName);
810     SETERRQ(PetscObjectComm((PetscObject)viewer), PETSC_ERR_LIB, "HDF5 failed to create group %s although file %s is open for writing", groupName, fileName);
811   }
812   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);
813   PetscCallHDF5Return(*groupId, H5Gopen2, (file_id, groupName, H5P_DEFAULT));
814   PetscCall(PetscFree(groupName));
815   *fileId = file_id;
816   PetscFunctionReturn(PETSC_SUCCESS);
817 }
818 
819 /*@C
820   PetscViewerHDF5WriteGroup - Ensure the HDF5 group exists in the HDF5 file
821 
822   Not collective
823 
824   Input Parameters:
825 + viewer - the `PetscViewer` of type `PETSCVIEWERHDF5`
826 - path - (Optional) The path relative to the pushed group
827 
828   Level: intermediate
829 
830   Note:
831   If path starts with '/', it is taken as an absolute path overriding currently pushed group, else path is relative to the current pushed group.
832   `NULL` or empty path means the current pushed group.
833 
834   This will fail if the viewer is not writable.
835 
836 .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `PetscViewerHDF5PushGroup()`, `PetscViewerHDF5PopGroup()`, `PetscViewerHDF5GetGroup()`, `PetscViewerHDF5OpenGroup()`
837 @*/
838 PetscErrorCode PetscViewerHDF5WriteGroup(PetscViewer viewer, const char path[])
839 {
840   hid_t fileId, groupId;
841 
842   PetscFunctionBegin;
843   PetscCall(PetscViewerHDF5OpenGroup(viewer, path, &fileId, &groupId)); // make sure group is actually created
844   PetscCallHDF5(H5Gclose, (groupId));
845   PetscFunctionReturn(PETSC_SUCCESS);
846 }
847 
848 /*@
849   PetscViewerHDF5PushTimestepping - Activate timestepping mode for subsequent HDF5 reading and writing.
850 
851   Not collective
852 
853   Input Parameter:
854 . viewer - the `PetscViewer` of type `PETSCVIEWERHDF5`
855 
856   Level: intermediate
857 
858   Notes:
859   On first `PetscViewerHDF5PushTimestepping()`, the initial time step is set to 0.
860   Next timesteps can then be set using `PetscViewerHDF5IncrementTimestep()` or `PetscViewerHDF5SetTimestep()`.
861   Current timestep value determines which timestep is read from or written to any dataset on the next HDF5 I/O operation [e.g. `VecView()`].
862   Use `PetscViewerHDF5PopTimestepping()` to deactivate timestepping mode; calling it by the end of the program is NOT mandatory.
863   Current timestep is remembered between `PetscViewerHDF5PopTimestepping()` and the next `PetscViewerHDF5PushTimestepping()`.
864 
865   If a dataset was stored with timestepping, it can be loaded only in the timestepping mode again.
866   Loading a timestepped dataset with timestepping disabled, or vice-versa results in an error.
867 
868   Developer note:
869   Timestepped HDF5 dataset has an extra dimension and attribute "timestepping" set to true.
870 
871 .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `PetscViewerHDF5PopTimestepping()`, `PetscViewerHDF5IsTimestepping()`, `PetscViewerHDF5SetTimestep()`, `PetscViewerHDF5IncrementTimestep()`, `PetscViewerHDF5GetTimestep()`
872 @*/
873 PetscErrorCode PetscViewerHDF5PushTimestepping(PetscViewer viewer)
874 {
875   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;
876 
877   PetscFunctionBegin;
878   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);
879   PetscCheck(!hdf5->timestepping, PetscObjectComm((PetscObject)viewer), PETSC_ERR_ARG_WRONGSTATE, "Timestepping is already pushed");
880   hdf5->timestepping = PETSC_TRUE;
881   if (hdf5->timestep < 0) hdf5->timestep = 0;
882   PetscFunctionReturn(PETSC_SUCCESS);
883 }
884 
885 /*@
886   PetscViewerHDF5PopTimestepping - Deactivate timestepping mode for subsequent HDF5 reading and writing.
887 
888   Not collective
889 
890   Input Parameter:
891 . viewer - the `PetscViewer` of type `PETSCVIEWERHDF5`
892 
893   Level: intermediate
894 
895   Note:
896   See `PetscViewerHDF5PushTimestepping()` for details.
897 
898 .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `PetscViewerHDF5PushTimestepping()`, `PetscViewerHDF5IsTimestepping()`, `PetscViewerHDF5SetTimestep()`, `PetscViewerHDF5IncrementTimestep()`, `PetscViewerHDF5GetTimestep()`
899 @*/
900 PetscErrorCode PetscViewerHDF5PopTimestepping(PetscViewer viewer)
901 {
902   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;
903 
904   PetscFunctionBegin;
905   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);
906   PetscCheck(hdf5->timestepping, PetscObjectComm((PetscObject)viewer), PETSC_ERR_ARG_WRONGSTATE, "Timestepping has not been pushed yet. Call PetscViewerHDF5PushTimestepping() first");
907   hdf5->timestepping = PETSC_FALSE;
908   PetscFunctionReturn(PETSC_SUCCESS);
909 }
910 
911 /*@
912   PetscViewerHDF5IsTimestepping - Ask the viewer whether it is in timestepping mode currently.
913 
914   Not collective
915 
916   Input Parameter:
917 . viewer - the `PetscViewer` of type `PETSCVIEWERHDF5`
918 
919   Output Parameter:
920 . flg - is timestepping active?
921 
922   Level: intermediate
923 
924   Note:
925   See `PetscViewerHDF5PushTimestepping()` for details.
926 
927 .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `PetscViewerHDF5PushTimestepping()`, `PetscViewerHDF5PopTimestepping()`, `PetscViewerHDF5SetTimestep()`, `PetscViewerHDF5IncrementTimestep()`, `PetscViewerHDF5GetTimestep()`
928 @*/
929 PetscErrorCode PetscViewerHDF5IsTimestepping(PetscViewer viewer, PetscBool *flg)
930 {
931   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;
932 
933   PetscFunctionBegin;
934   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);
935   *flg = hdf5->timestepping;
936   PetscFunctionReturn(PETSC_SUCCESS);
937 }
938 
939 /*@
940   PetscViewerHDF5IncrementTimestep - Increments current timestep for the HDF5 output. Fields are stacked in time.
941 
942   Not collective
943 
944   Input Parameter:
945 . viewer - the `PetscViewer` of type `PETSCVIEWERHDF5`
946 
947   Level: intermediate
948 
949   Note:
950   This can be called only if the viewer is in timestepping mode. See `PetscViewerHDF5PushTimestepping()` for details.
951 
952 .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `PetscViewerHDF5PushTimestepping()`, `PetscViewerHDF5SetTimestep()`, `PetscViewerHDF5GetTimestep()`
953 @*/
954 PetscErrorCode PetscViewerHDF5IncrementTimestep(PetscViewer viewer)
955 {
956   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;
957 
958   PetscFunctionBegin;
959   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);
960   PetscCheck(hdf5->timestepping, PetscObjectComm((PetscObject)viewer), PETSC_ERR_ARG_WRONGSTATE, "Timestepping has not been pushed yet. Call PetscViewerHDF5PushTimestepping() first");
961   ++hdf5->timestep;
962   PetscFunctionReturn(PETSC_SUCCESS);
963 }
964 
965 /*@
966   PetscViewerHDF5SetTimestep - Set the current timestep for the HDF5 output. Fields are stacked in time.
967 
968   Logically collective
969 
970   Input Parameters:
971 + viewer - the `PetscViewer` of type `PETSCVIEWERHDF5`
972 - timestep - The timestep
973 
974   Level: intermediate
975 
976   Note:
977   This can be called only if the viewer is in timestepping mode. See `PetscViewerHDF5PushTimestepping()` for details.
978 
979 .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `PetscViewerHDF5PushTimestepping()`, `PetscViewerHDF5IncrementTimestep()`, `PetscViewerHDF5GetTimestep()`
980 @*/
981 PetscErrorCode PetscViewerHDF5SetTimestep(PetscViewer viewer, PetscInt timestep)
982 {
983   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;
984 
985   PetscFunctionBegin;
986   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);
987   PetscValidLogicalCollectiveInt(viewer, timestep, 2);
988   PetscCheck(timestep >= 0, PetscObjectComm((PetscObject)viewer), PETSC_ERR_ARG_WRONGSTATE, "Timestep %" PetscInt_FMT " is negative", timestep);
989   PetscCheck(hdf5->timestepping, PetscObjectComm((PetscObject)viewer), PETSC_ERR_ARG_WRONGSTATE, "Timestepping has not been pushed yet. Call PetscViewerHDF5PushTimestepping() first");
990   hdf5->timestep = timestep;
991   PetscFunctionReturn(PETSC_SUCCESS);
992 }
993 
994 /*@
995   PetscViewerHDF5GetTimestep - Get the current timestep for the HDF5 output. Fields are stacked in time.
996 
997   Not collective
998 
999   Input Parameter:
1000 . viewer - the `PetscViewer` of type `PETSCVIEWERHDF5`
1001 
1002   Output Parameter:
1003 . timestep - The timestep
1004 
1005   Level: intermediate
1006 
1007   Note:
1008   This can be called only if the viewer is in the timestepping mode. See `PetscViewerHDF5PushTimestepping()` for details.
1009 
1010 .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `PetscViewerHDF5PushTimestepping()`, `PetscViewerHDF5IncrementTimestep()`, `PetscViewerHDF5SetTimestep()`
1011 @*/
1012 PetscErrorCode PetscViewerHDF5GetTimestep(PetscViewer viewer, PetscInt *timestep)
1013 {
1014   PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;
1015 
1016   PetscFunctionBegin;
1017   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);
1018   PetscValidIntPointer(timestep, 2);
1019   PetscCheck(hdf5->timestepping, PetscObjectComm((PetscObject)viewer), PETSC_ERR_ARG_WRONGSTATE, "Timestepping has not been pushed yet. Call PetscViewerHDF5PushTimestepping() first");
1020   *timestep = hdf5->timestep;
1021   PetscFunctionReturn(PETSC_SUCCESS);
1022 }
1023 
1024 /*@C
1025   PetscDataTypeToHDF5DataType - Converts the PETSc name of a datatype to its HDF5 name.
1026 
1027   Not collective
1028 
1029   Input Parameter:
1030 . ptype - the PETSc datatype name (for example `PETSC_DOUBLE`)
1031 
1032   Output Parameter:
1033 . mtype - the HDF5  datatype
1034 
1035   Level: advanced
1036 
1037 .seealso: [](sec_viewers), `PetscDataType`, `PetscHDF5DataTypeToPetscDataType()`
1038 @*/
1039 PetscErrorCode PetscDataTypeToHDF5DataType(PetscDataType ptype, hid_t *htype)
1040 {
1041   PetscFunctionBegin;
1042   if (ptype == PETSC_INT)
1043 #if defined(PETSC_USE_64BIT_INDICES)
1044     *htype = H5T_NATIVE_LLONG;
1045 #else
1046     *htype = H5T_NATIVE_INT;
1047 #endif
1048   else if (ptype == PETSC_DOUBLE) *htype = H5T_NATIVE_DOUBLE;
1049   else if (ptype == PETSC_LONG) *htype = H5T_NATIVE_LONG;
1050   else if (ptype == PETSC_SHORT) *htype = H5T_NATIVE_SHORT;
1051   else if (ptype == PETSC_ENUM) *htype = H5T_NATIVE_INT;
1052   else if (ptype == PETSC_BOOL) *htype = H5T_NATIVE_INT;
1053   else if (ptype == PETSC_FLOAT) *htype = H5T_NATIVE_FLOAT;
1054   else if (ptype == PETSC_CHAR) *htype = H5T_NATIVE_CHAR;
1055   else if (ptype == PETSC_BIT_LOGICAL) *htype = H5T_NATIVE_UCHAR;
1056   else if (ptype == PETSC_STRING) *htype = H5Tcopy(H5T_C_S1);
1057   else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Unsupported PETSc datatype");
1058   PetscFunctionReturn(PETSC_SUCCESS);
1059 }
1060 
1061 /*@C
1062   PetscHDF5DataTypeToPetscDataType - Finds the PETSc name of a datatype from its HDF5 name
1063 
1064   Not collective
1065 
1066   Input Parameter:
1067 . htype - the HDF5 datatype (for example `H5T_NATIVE_DOUBLE`, ...)
1068 
1069   Output Parameter:
1070 . ptype - the PETSc datatype name (for example `PETSC_DOUBLE`)
1071 
1072   Level: advanced
1073 
1074 .seealso: [](sec_viewers), `PetscDataType`, `PetscHDF5DataTypeToPetscDataType()`
1075 @*/
1076 PetscErrorCode PetscHDF5DataTypeToPetscDataType(hid_t htype, PetscDataType *ptype)
1077 {
1078   PetscFunctionBegin;
1079 #if defined(PETSC_USE_64BIT_INDICES)
1080   if (htype == H5T_NATIVE_INT) *ptype = PETSC_LONG;
1081   else if (htype == H5T_NATIVE_LLONG) *ptype = PETSC_INT;
1082 #else
1083   if (htype == H5T_NATIVE_INT) *ptype = PETSC_INT;
1084 #endif
1085   else if (htype == H5T_NATIVE_DOUBLE) *ptype = PETSC_DOUBLE;
1086   else if (htype == H5T_NATIVE_LONG) *ptype = PETSC_LONG;
1087   else if (htype == H5T_NATIVE_SHORT) *ptype = PETSC_SHORT;
1088   else if (htype == H5T_NATIVE_FLOAT) *ptype = PETSC_FLOAT;
1089   else if (htype == H5T_NATIVE_CHAR) *ptype = PETSC_CHAR;
1090   else if (htype == H5T_NATIVE_UCHAR) *ptype = PETSC_CHAR;
1091   else if (htype == H5T_C_S1) *ptype = PETSC_STRING;
1092   else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Unsupported HDF5 datatype");
1093   PetscFunctionReturn(PETSC_SUCCESS);
1094 }
1095 
1096 /*@C
1097  PetscViewerHDF5WriteAttribute - Write an attribute
1098 
1099   Collective
1100 
1101   Input Parameters:
1102 + viewer - The `PETSCVIEWERHDF5` viewer
1103 . parent - The parent dataset/group name
1104 . name   - The attribute name
1105 . datatype - The attribute type
1106 - value    - The attribute value
1107 
1108   Level: advanced
1109 
1110   Note:
1111   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.
1112 
1113 .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `PetscViewerHDF5WriteObjectAttribute()`, `PetscViewerHDF5ReadAttribute()`, `PetscViewerHDF5HasAttribute()`,
1114           `PetscViewerHDF5PushGroup()`, `PetscViewerHDF5PopGroup()`, `PetscViewerHDF5GetGroup()`
1115 @*/
1116 PetscErrorCode PetscViewerHDF5WriteAttribute(PetscViewer viewer, const char parent[], const char name[], PetscDataType datatype, const void *value)
1117 {
1118   char     *parentAbsPath;
1119   hid_t     h5, dataspace, obj, attribute, dtype;
1120   PetscBool has;
1121 
1122   PetscFunctionBegin;
1123   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);
1124   if (parent) PetscValidCharPointer(parent, 2);
1125   PetscValidCharPointer(name, 3);
1126   PetscValidLogicalCollectiveEnum(viewer, datatype, 4);
1127   PetscValidPointer(value, 5);
1128   PetscCall(PetscViewerHDF5GetGroup(viewer, parent, &parentAbsPath));
1129   PetscCall(PetscViewerHDF5Traverse_Internal(viewer, parentAbsPath, PETSC_TRUE, NULL, NULL));
1130   PetscCall(PetscViewerHDF5HasAttribute_Internal(viewer, parentAbsPath, name, &has));
1131   PetscCall(PetscDataTypeToHDF5DataType(datatype, &dtype));
1132   if (datatype == PETSC_STRING) {
1133     size_t len;
1134     PetscCall(PetscStrlen((const char *)value, &len));
1135     PetscCallHDF5(H5Tset_size, (dtype, len + 1));
1136   }
1137   PetscCall(PetscViewerHDF5GetFileId(viewer, &h5));
1138   PetscCallHDF5Return(dataspace, H5Screate, (H5S_SCALAR));
1139   PetscCallHDF5Return(obj, H5Oopen, (h5, parentAbsPath, H5P_DEFAULT));
1140   if (has) {
1141     PetscCallHDF5Return(attribute, H5Aopen_name, (obj, name));
1142   } else {
1143     PetscCallHDF5Return(attribute, H5Acreate2, (obj, name, dtype, dataspace, H5P_DEFAULT, H5P_DEFAULT));
1144   }
1145   PetscCallHDF5(H5Awrite, (attribute, dtype, value));
1146   if (datatype == PETSC_STRING) PetscCallHDF5(H5Tclose, (dtype));
1147   PetscCallHDF5(H5Aclose, (attribute));
1148   PetscCallHDF5(H5Oclose, (obj));
1149   PetscCallHDF5(H5Sclose, (dataspace));
1150   PetscCall(PetscFree(parentAbsPath));
1151   PetscFunctionReturn(PETSC_SUCCESS);
1152 }
1153 
1154 /*@C
1155  PetscViewerHDF5WriteObjectAttribute - Write an attribute to the dataset matching the given `PetscObject` by name
1156 
1157   Collective
1158 
1159   Input Parameters:
1160 + viewer   - The `PETSCVIEWERHDF5` viewer
1161 . obj      - The object whose name is used to lookup the parent dataset, relative to the current group.
1162 . name     - The attribute name
1163 . datatype - The attribute type
1164 - value    - The attribute value
1165 
1166   Level: advanced
1167 
1168   Note:
1169   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).
1170   You might want to check first if it does using `PetscViewerHDF5HasObject()`.
1171 
1172 .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `PetscViewerHDF5WriteAttribute()`, `PetscViewerHDF5ReadObjectAttribute()`, `PetscViewerHDF5HasObjectAttribute()`,
1173           `PetscViewerHDF5HasObject()`, `PetscViewerHDF5PushGroup()`, `PetscViewerHDF5PopGroup()`, `PetscViewerHDF5GetGroup()`
1174 @*/
1175 PetscErrorCode PetscViewerHDF5WriteObjectAttribute(PetscViewer viewer, PetscObject obj, const char name[], PetscDataType datatype, const void *value)
1176 {
1177   PetscFunctionBegin;
1178   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);
1179   PetscValidHeader(obj, 2);
1180   PetscValidCharPointer(name, 3);
1181   PetscValidPointer(value, 5);
1182   PetscCall(PetscViewerHDF5CheckNamedObject_Internal(viewer, obj));
1183   PetscCall(PetscViewerHDF5WriteAttribute(viewer, obj->name, name, datatype, value));
1184   PetscFunctionReturn(PETSC_SUCCESS);
1185 }
1186 
1187 /*@C
1188  PetscViewerHDF5ReadAttribute - Read an attribute
1189 
1190   Collective
1191 
1192   Input Parameters:
1193 + viewer - The `PETSCVIEWERHDF5` viewer
1194 . parent - The parent dataset/group name
1195 . name   - The attribute name
1196 . datatype - The attribute type
1197 - defaultValue - The pointer to the default value
1198 
1199   Output Parameter:
1200 . value    - The pointer to the read HDF5 attribute value
1201 
1202   Level: advanced
1203 
1204   Notes:
1205   If defaultValue is `NULL` and the attribute is not found, an error occurs.
1206 
1207   If defaultValue is not `NULL` and the attribute is not found, `defaultValue` is copied to value.
1208 
1209   The pointers `defaultValue` and value can be the same; for instance
1210 .vb
1211   flg = PETSC_FALSE;
1212   PetscCall(`PetscViewerHDF5ReadAttribute`(viewer,name,"attr",PETSC_BOOL,&flg,&flg));
1213 .ve
1214   is valid, but make sure the default value is initialized.
1215 
1216   If the datatype is `PETSC_STRING`, the output string is newly allocated so one must `PetscFree()` it when no longer needed.
1217 
1218   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.
1219 
1220 .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `PetscViewerHDF5ReadObjectAttribute()`, `PetscViewerHDF5WriteAttribute()`, `PetscViewerHDF5HasAttribute()`, `PetscViewerHDF5HasObject()`, `PetscViewerHDF5PushGroup()`, `PetscViewerHDF5PopGroup()`, `PetscViewerHDF5GetGroup()`
1221 @*/
1222 PetscErrorCode PetscViewerHDF5ReadAttribute(PetscViewer viewer, const char parent[], const char name[], PetscDataType datatype, const void *defaultValue, void *value)
1223 {
1224   char     *parentAbsPath;
1225   hid_t     h5, obj, attribute, dtype;
1226   PetscBool has;
1227 
1228   PetscFunctionBegin;
1229   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);
1230   if (parent) PetscValidCharPointer(parent, 2);
1231   PetscValidCharPointer(name, 3);
1232   if (defaultValue) PetscValidPointer(defaultValue, 5);
1233   PetscValidPointer(value, 6);
1234   PetscCall(PetscDataTypeToHDF5DataType(datatype, &dtype));
1235   PetscCall(PetscViewerHDF5GetGroup(viewer, parent, &parentAbsPath));
1236   PetscCall(PetscViewerHDF5Traverse_Internal(viewer, parentAbsPath, PETSC_FALSE, &has, NULL));
1237   if (has) PetscCall(PetscViewerHDF5HasAttribute_Internal(viewer, parentAbsPath, name, &has));
1238   if (!has) {
1239     if (defaultValue) {
1240       if (defaultValue != value) {
1241         if (datatype == PETSC_STRING) {
1242           PetscCall(PetscStrallocpy(*(char **)defaultValue, (char **)value));
1243         } else {
1244           size_t len;
1245           PetscCallHDF5ReturnNoCheck(len, H5Tget_size, (dtype));
1246           PetscCall(PetscMemcpy(value, defaultValue, len));
1247         }
1248       }
1249       PetscCall(PetscFree(parentAbsPath));
1250       PetscFunctionReturn(PETSC_SUCCESS);
1251     } else SETERRQ(PetscObjectComm((PetscObject)viewer), PETSC_ERR_FILE_UNEXPECTED, "Attribute %s/%s does not exist and default value not provided", parentAbsPath, name);
1252   }
1253   PetscCall(PetscViewerHDF5GetFileId(viewer, &h5));
1254   PetscCallHDF5Return(obj, H5Oopen, (h5, parentAbsPath, H5P_DEFAULT));
1255   PetscCallHDF5Return(attribute, H5Aopen_name, (obj, name));
1256   if (datatype == PETSC_STRING) {
1257     size_t len;
1258     hid_t  atype;
1259     PetscCallHDF5Return(atype, H5Aget_type, (attribute));
1260     PetscCallHDF5ReturnNoCheck(len, H5Tget_size, (atype));
1261     PetscCall(PetscMalloc((len + 1) * sizeof(char), value));
1262     PetscCallHDF5(H5Tset_size, (dtype, len + 1));
1263     PetscCallHDF5(H5Aread, (attribute, dtype, *(char **)value));
1264   } else {
1265     PetscCallHDF5(H5Aread, (attribute, dtype, value));
1266   }
1267   PetscCallHDF5(H5Aclose, (attribute));
1268   /* H5Oclose can be used to close groups, datasets, or committed datatypes */
1269   PetscCallHDF5(H5Oclose, (obj));
1270   PetscCall(PetscFree(parentAbsPath));
1271   PetscFunctionReturn(PETSC_SUCCESS);
1272 }
1273 
1274 /*@C
1275  PetscViewerHDF5ReadObjectAttribute - Read an attribute from the dataset matching the given `PetscObject` by name
1276 
1277   Collective
1278 
1279   Input Parameters:
1280 + viewer   - The `PETSCVIEWERHDF5` viewer
1281 . obj      - The object whose name is used to lookup the parent dataset, relative to the current group.
1282 . name     - The attribute name
1283 - datatype - The attribute type
1284 
1285   Output Parameter:
1286 . value    - The attribute value
1287 
1288   Level: advanced
1289 
1290   Note:
1291   This fails if current_group/object_name doesn't resolve to a dataset (the path doesn't exist or is not a dataset).
1292   You might want to check first if it does using `PetscViewerHDF5HasObject()`.
1293 
1294 .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `PetscViewerHDF5ReadAttribute()` `PetscViewerHDF5WriteObjectAttribute()`, `PetscViewerHDF5HasObjectAttribute()`, `PetscViewerHDF5PushGroup()`, `PetscViewerHDF5PopGroup()`, `PetscViewerHDF5GetGroup()`
1295 @*/
1296 PetscErrorCode PetscViewerHDF5ReadObjectAttribute(PetscViewer viewer, PetscObject obj, const char name[], PetscDataType datatype, void *defaultValue, void *value)
1297 {
1298   PetscFunctionBegin;
1299   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);
1300   PetscValidHeader(obj, 2);
1301   PetscValidCharPointer(name, 3);
1302   PetscValidPointer(value, 6);
1303   PetscCall(PetscViewerHDF5CheckNamedObject_Internal(viewer, obj));
1304   PetscCall(PetscViewerHDF5ReadAttribute(viewer, obj->name, name, datatype, defaultValue, value));
1305   PetscFunctionReturn(PETSC_SUCCESS);
1306 }
1307 
1308 static inline PetscErrorCode PetscViewerHDF5Traverse_Inner_Internal(hid_t h5, const char name[], PetscBool createGroup, PetscBool *exists_)
1309 {
1310   htri_t exists;
1311   hid_t  group;
1312 
1313   PetscFunctionBegin;
1314   PetscCallHDF5Return(exists, H5Lexists, (h5, name, H5P_DEFAULT));
1315   if (exists) PetscCallHDF5Return(exists, H5Oexists_by_name, (h5, name, H5P_DEFAULT));
1316   if (!exists && createGroup) {
1317     PetscCallHDF5Return(group, H5Gcreate2, (h5, name, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT));
1318     PetscCallHDF5(H5Gclose, (group));
1319     exists = PETSC_TRUE;
1320   }
1321   *exists_ = (PetscBool)exists;
1322   PetscFunctionReturn(PETSC_SUCCESS);
1323 }
1324 
1325 static PetscErrorCode PetscViewerHDF5Traverse_Internal(PetscViewer viewer, const char name[], PetscBool createGroup, PetscBool *has, H5O_type_t *otype)
1326 {
1327   const char rootGroupName[] = "/";
1328   hid_t      h5;
1329   PetscBool  exists = PETSC_FALSE;
1330   PetscInt   i;
1331   int        n;
1332   char     **hierarchy;
1333   char       buf[PETSC_MAX_PATH_LEN] = "";
1334 
1335   PetscFunctionBegin;
1336   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);
1337   if (name) PetscValidCharPointer(name, 2);
1338   else name = rootGroupName;
1339   if (has) {
1340     PetscValidBoolPointer(has, 4);
1341     *has = PETSC_FALSE;
1342   }
1343   if (otype) {
1344     PetscValidIntPointer(otype, 5);
1345     *otype = H5O_TYPE_UNKNOWN;
1346   }
1347   PetscCall(PetscViewerHDF5GetFileId(viewer, &h5));
1348 
1349   /*
1350      Unfortunately, H5Oexists_by_name() fails if any object in hierarchy is missing.
1351      Hence, each of them needs to be tested separately:
1352      1) whether it's a valid link
1353      2) whether this link resolves to an object
1354      See H5Oexists_by_name() documentation.
1355   */
1356   PetscCall(PetscStrToArray(name, '/', &n, &hierarchy));
1357   if (!n) {
1358     /*  Assume group "/" always exists in accordance with HDF5 >= 1.10.0. See H5Lexists() documentation. */
1359     if (has) *has = PETSC_TRUE;
1360     if (otype) *otype = H5O_TYPE_GROUP;
1361     PetscCall(PetscStrToArrayDestroy(n, hierarchy));
1362     PetscFunctionReturn(PETSC_SUCCESS);
1363   }
1364   for (i = 0; i < n; i++) {
1365     PetscCall(PetscStrlcat(buf, "/", sizeof(buf)));
1366     PetscCall(PetscStrlcat(buf, hierarchy[i], sizeof(buf)));
1367     PetscCall(PetscViewerHDF5Traverse_Inner_Internal(h5, buf, createGroup, &exists));
1368     if (!exists) break;
1369   }
1370   PetscCall(PetscStrToArrayDestroy(n, hierarchy));
1371 
1372   /* If the object exists, get its type */
1373   if (exists && otype) {
1374     H5O_info_t info;
1375 
1376     /* We could use H5Iget_type() here but that would require opening the object. This way we only need its name. */
1377     PetscCallHDF5(H5Oget_info_by_name, (h5, name, &info, H5P_DEFAULT));
1378     *otype = info.type;
1379   }
1380   if (has) *has = exists;
1381   PetscFunctionReturn(PETSC_SUCCESS);
1382 }
1383 
1384 /*@C
1385  PetscViewerHDF5HasGroup - Check whether the current (pushed) group exists in the HDF5 file
1386 
1387   Collective
1388 
1389   Input Parameters:
1390 + viewer - The `PETSCVIEWERHDF5` viewer
1391 - path - (Optional) The path relative to the pushed group
1392 
1393   Output Parameter:
1394 . has - Flag for group existence
1395 
1396   Level: advanced
1397 
1398   Notes:
1399   If path starts with '/', it is taken as an absolute path overriding currently pushed group, else path is relative to the current pushed group.
1400   `NULL` or empty path means the current pushed group.
1401 
1402   If path exists but is not a group, `PETSC_FALSE` is returned.
1403 
1404 .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5HasAttribute()`, `PetscViewerHDF5HasDataset()`, `PetscViewerHDF5PushGroup()`, `PetscViewerHDF5PopGroup()`, `PetscViewerHDF5GetGroup()`, `PetscViewerHDF5OpenGroup()`
1405 @*/
1406 PetscErrorCode PetscViewerHDF5HasGroup(PetscViewer viewer, const char path[], PetscBool *has)
1407 {
1408   H5O_type_t type;
1409   char      *abspath;
1410 
1411   PetscFunctionBegin;
1412   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);
1413   if (path) PetscValidCharPointer(path, 2);
1414   PetscValidBoolPointer(has, 3);
1415   PetscCall(PetscViewerHDF5GetGroup(viewer, path, &abspath));
1416   PetscCall(PetscViewerHDF5Traverse_Internal(viewer, abspath, PETSC_FALSE, NULL, &type));
1417   *has = (PetscBool)(type == H5O_TYPE_GROUP);
1418   PetscCall(PetscFree(abspath));
1419   PetscFunctionReturn(PETSC_SUCCESS);
1420 }
1421 
1422 /*@C
1423  PetscViewerHDF5HasDataset - Check whether a given dataset exists in the HDF5 file
1424 
1425   Collective
1426 
1427   Input Parameters:
1428 + viewer - The `PETSCVIEWERHDF5` viewer
1429 - path - The dataset path
1430 
1431   Output Parameter:
1432 . has - Flag whether dataset exists
1433 
1434   Level: advanced
1435 
1436   Notes:
1437   If path starts with '/', it is taken as an absolute path overriding currently pushed group, else path is relative to the current pushed group.
1438 
1439   If `path` is `NULL` or empty, has is set to `PETSC_FALSE`.
1440 
1441   If `path` exists but is not a dataset, has is set to `PETSC_FALSE` as well.
1442 
1443 .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5HasObject()`, `PetscViewerHDF5HasAttribute()`, `PetscViewerHDF5HasGroup()`, `PetscViewerHDF5PushGroup()`, `PetscViewerHDF5PopGroup()`, `PetscViewerHDF5GetGroup()`
1444 @*/
1445 PetscErrorCode PetscViewerHDF5HasDataset(PetscViewer viewer, const char path[], PetscBool *has)
1446 {
1447   H5O_type_t type;
1448   char      *abspath;
1449 
1450   PetscFunctionBegin;
1451   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);
1452   if (path) PetscValidCharPointer(path, 2);
1453   PetscValidBoolPointer(has, 3);
1454   PetscCall(PetscViewerHDF5GetGroup(viewer, path, &abspath));
1455   PetscCall(PetscViewerHDF5Traverse_Internal(viewer, abspath, PETSC_FALSE, NULL, &type));
1456   *has = (PetscBool)(type == H5O_TYPE_DATASET);
1457   PetscCall(PetscFree(abspath));
1458   PetscFunctionReturn(PETSC_SUCCESS);
1459 }
1460 
1461 /*@
1462  PetscViewerHDF5HasObject - Check whether a dataset with the same name as given object exists in the HDF5 file under current group
1463 
1464   Collective
1465 
1466   Input Parameters:
1467 + viewer - The `PETSCVIEWERHDF5` viewer
1468 - obj    - The named object
1469 
1470   Output Parameter:
1471 . has    - Flag for dataset existence
1472 
1473   Level: advanced
1474 
1475   Notes:
1476   If the object is unnamed, an error occurs.
1477 
1478   If the path current_group/object_name exists but is not a dataset, has is set to `PETSC_FALSE` as well.
1479 
1480 .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `PetscViewerHDF5HasDataset()`, `PetscViewerHDF5HasAttribute()`, `PetscViewerHDF5PushGroup()`, `PetscViewerHDF5PopGroup()`, `PetscViewerHDF5GetGroup()`
1481 @*/
1482 PetscErrorCode PetscViewerHDF5HasObject(PetscViewer viewer, PetscObject obj, PetscBool *has)
1483 {
1484   size_t len;
1485 
1486   PetscFunctionBegin;
1487   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);
1488   PetscValidHeader(obj, 2);
1489   PetscValidBoolPointer(has, 3);
1490   PetscCall(PetscStrlen(obj->name, &len));
1491   PetscCheck(len, PetscObjectComm((PetscObject)viewer), PETSC_ERR_ARG_WRONG, "Object must be named");
1492   PetscCall(PetscViewerHDF5HasDataset(viewer, obj->name, has));
1493   PetscFunctionReturn(PETSC_SUCCESS);
1494 }
1495 
1496 /*@C
1497  PetscViewerHDF5HasAttribute - Check whether an attribute exists
1498 
1499   Collective
1500 
1501   Input Parameters:
1502 + viewer - The `PETSCVIEWERHDF5` viewer
1503 . parent - The parent dataset/group name
1504 - name   - The attribute name
1505 
1506   Output Parameter:
1507 . has    - Flag for attribute existence
1508 
1509   Level: advanced
1510 
1511   Note:
1512   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.
1513 
1514 .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `PetscViewerHDF5HasObjectAttribute()`, `PetscViewerHDF5WriteAttribute()`, `PetscViewerHDF5ReadAttribute()`, `PetscViewerHDF5PushGroup()`, `PetscViewerHDF5PopGroup()`, `PetscViewerHDF5GetGroup()`
1515 @*/
1516 PetscErrorCode PetscViewerHDF5HasAttribute(PetscViewer viewer, const char parent[], const char name[], PetscBool *has)
1517 {
1518   char *parentAbsPath;
1519 
1520   PetscFunctionBegin;
1521   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);
1522   if (parent) PetscValidCharPointer(parent, 2);
1523   PetscValidCharPointer(name, 3);
1524   PetscValidBoolPointer(has, 4);
1525   PetscCall(PetscViewerHDF5GetGroup(viewer, parent, &parentAbsPath));
1526   PetscCall(PetscViewerHDF5Traverse_Internal(viewer, parentAbsPath, PETSC_FALSE, has, NULL));
1527   if (*has) PetscCall(PetscViewerHDF5HasAttribute_Internal(viewer, parentAbsPath, name, has));
1528   PetscCall(PetscFree(parentAbsPath));
1529   PetscFunctionReturn(PETSC_SUCCESS);
1530 }
1531 
1532 /*@C
1533  PetscViewerHDF5HasObjectAttribute - Check whether an attribute is attached to the dataset matching the given `PetscObject` by name
1534 
1535   Collective
1536 
1537   Input Parameters:
1538 + viewer - The `PETSCVIEWERHDF5` viewer
1539 . obj    - The object whose name is used to lookup the parent dataset, relative to the current group.
1540 - name   - The attribute name
1541 
1542   Output Parameter:
1543 . has    - Flag for attribute existence
1544 
1545   Level: advanced
1546 
1547   Note:
1548   This fails if current_group/object_name doesn't resolve to a dataset (the path doesn't exist or is not a dataset).
1549   You might want to check first if it does using `PetscViewerHDF5HasObject()`.
1550 
1551 .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `PetscViewerHDF5HasAttribute()`, `PetscViewerHDF5WriteObjectAttribute()`, `PetscViewerHDF5ReadObjectAttribute()`, `PetscViewerHDF5HasObject()`, `PetscViewerHDF5PushGroup()`, `PetscViewerHDF5PopGroup()`, `PetscViewerHDF5GetGroup()`
1552 @*/
1553 PetscErrorCode PetscViewerHDF5HasObjectAttribute(PetscViewer viewer, PetscObject obj, const char name[], PetscBool *has)
1554 {
1555   PetscFunctionBegin;
1556   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);
1557   PetscValidHeader(obj, 2);
1558   PetscValidCharPointer(name, 3);
1559   PetscValidBoolPointer(has, 4);
1560   PetscCall(PetscViewerHDF5CheckNamedObject_Internal(viewer, obj));
1561   PetscCall(PetscViewerHDF5HasAttribute(viewer, obj->name, name, has));
1562   PetscFunctionReturn(PETSC_SUCCESS);
1563 }
1564 
1565 static PetscErrorCode PetscViewerHDF5HasAttribute_Internal(PetscViewer viewer, const char parent[], const char name[], PetscBool *has)
1566 {
1567   hid_t  h5;
1568   htri_t hhas;
1569 
1570   PetscFunctionBegin;
1571   PetscCall(PetscViewerHDF5GetFileId(viewer, &h5));
1572   PetscCallHDF5Return(hhas, H5Aexists_by_name, (h5, parent, name, H5P_DEFAULT));
1573   *has = hhas ? PETSC_TRUE : PETSC_FALSE;
1574   PetscFunctionReturn(PETSC_SUCCESS);
1575 }
1576 
1577 /*
1578   The variable Petsc_Viewer_HDF5_keyval is used to indicate an MPI attribute that
1579   is attached to a communicator, in this case the attribute is a PetscViewer.
1580 */
1581 PetscMPIInt Petsc_Viewer_HDF5_keyval = MPI_KEYVAL_INVALID;
1582 
1583 /*@C
1584   PETSC_VIEWER_HDF5_ - Creates an `PETSCVIEWERHDF5` `PetscViewer` shared by all processors in a communicator.
1585 
1586   Collective
1587 
1588   Input Parameter:
1589 . comm - the MPI communicator to share the `PETSCVIEWERHDF5` `PetscViewer`
1590 
1591   Level: intermediate
1592 
1593   Options Database Key:
1594 . -viewer_hdf5_filename <name> - name of the HDF5 file
1595 
1596   Environmental variable:
1597 . `PETSC_VIEWER_HDF5_FILENAME` - name of the HDF5 file
1598 
1599   Note:
1600   Unlike almost all other PETSc routines, `PETSC_VIEWER_HDF5_()` does not return
1601   an error code.  The HDF5 `PetscViewer` is usually used in the form
1602 $       XXXView(XXX object, PETSC_VIEWER_HDF5_(comm));
1603 
1604 .seealso: [](sec_viewers), `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `PetscViewerCreate()`, `PetscViewerDestroy()`
1605 @*/
1606 PetscViewer PETSC_VIEWER_HDF5_(MPI_Comm comm)
1607 {
1608   PetscErrorCode ierr;
1609   PetscMPIInt    mpi_ierr;
1610   PetscBool      flg;
1611   PetscViewer    viewer;
1612   char           fname[PETSC_MAX_PATH_LEN];
1613   MPI_Comm       ncomm;
1614 
1615   PetscFunctionBegin;
1616   ierr = PetscCommDuplicate(comm, &ncomm, NULL);
1617   if (ierr) {
1618     ierr = PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_HDF5_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
1619     PetscFunctionReturn(NULL);
1620   }
1621   if (Petsc_Viewer_HDF5_keyval == MPI_KEYVAL_INVALID) {
1622     mpi_ierr = MPI_Comm_create_keyval(MPI_COMM_NULL_COPY_FN, MPI_COMM_NULL_DELETE_FN, &Petsc_Viewer_HDF5_keyval, NULL);
1623     if (mpi_ierr) {
1624       ierr = PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_HDF5_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
1625       PetscFunctionReturn(NULL);
1626     }
1627   }
1628   mpi_ierr = MPI_Comm_get_attr(ncomm, Petsc_Viewer_HDF5_keyval, (void **)&viewer, (int *)&flg);
1629   if (mpi_ierr) {
1630     ierr = PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_HDF5_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
1631     PetscFunctionReturn(NULL);
1632   }
1633   if (!flg) { /* PetscViewer not yet created */
1634     ierr = PetscOptionsGetenv(ncomm, "PETSC_VIEWER_HDF5_FILENAME", fname, PETSC_MAX_PATH_LEN, &flg);
1635     if (ierr) {
1636       ierr = PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_HDF5_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_REPEAT, " ");
1637       PetscFunctionReturn(NULL);
1638     }
1639     if (!flg) {
1640       ierr = PetscStrncpy(fname, "output.h5", sizeof(fname));
1641       if (ierr) {
1642         ierr = PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_HDF5_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_REPEAT, " ");
1643         PetscFunctionReturn(NULL);
1644       }
1645     }
1646     ierr = PetscViewerHDF5Open(ncomm, fname, FILE_MODE_WRITE, &viewer);
1647     if (ierr) {
1648       ierr = PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_HDF5_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_REPEAT, " ");
1649       PetscFunctionReturn(NULL);
1650     }
1651     ierr = PetscObjectRegisterDestroy((PetscObject)viewer);
1652     if (ierr) {
1653       ierr = PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_HDF5_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_REPEAT, " ");
1654       PetscFunctionReturn(NULL);
1655     }
1656     mpi_ierr = MPI_Comm_set_attr(ncomm, Petsc_Viewer_HDF5_keyval, (void *)viewer);
1657     if (mpi_ierr) {
1658       ierr = PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_HDF5_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
1659       PetscFunctionReturn(NULL);
1660     }
1661   }
1662   ierr = PetscCommDestroy(&ncomm);
1663   if (ierr) {
1664     ierr = PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_HDF5_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_REPEAT, " ");
1665     PetscFunctionReturn(NULL);
1666   }
1667   PetscFunctionReturn(viewer);
1668 }
1669