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