Actual source code: ex48.c

  1: static char help[] = "Tests HDF5 attribute I/O.\n\n";

  3: #include <petscviewerhdf5.h>
  4: #include <petscvec.h>

  6: static PetscInt  n       = 5; /* testing vector size */
  7: static PetscBool verbose = PETSC_FALSE;
  8: #define SLEN 128

 10: /* sequence of unique absolute paths */
 11: #define nap 9
 12: static const char *apaths[nap] = {/* 0 */ "/",
 13:                                   "/g1",
 14:                                   "/g1/g2",
 15:                                   "/g1/nonExistingGroup1",
 16:                                   "/g1/g3",
 17:                                   /* 5 */ "/g1/g3/g4",
 18:                                   "/g1/nonExistingGroup2",
 19:                                   "/g1/nonExistingGroup2/g5",
 20:                                   "/g1/g6/g7"};

 22: #define np 21
 23: /* sequence of paths (absolute or relative); "<" encodes Pop */
 24: static const char *paths[np] = {
 25:   /* 0 */
 26:   "/",
 27:   "/g1",
 28:   "/g1/g2",
 29:   "/g1/nonExistingGroup1",
 30:   "<",
 31:   /* 5 */
 32:   ".", /* /g1/g2 */
 33:   "<",
 34:   "<",
 35:   "g3", /* /g1/g3 */
 36:   "g4", /* /g1/g3/g4 */
 37:         /* 10 */
 38:   "<",
 39:   "<",
 40:   ".", /* /g1 */
 41:   "<",
 42:   "nonExistingGroup2", /* /g1/nonExistingG2 */
 43:                        /* 15 */
 44:   "g5",                /* /g1/nonExistingG2/g5 */
 45:   "<",
 46:   "<",
 47:   "g6/g7", /* /g1/g6/g7 */
 48:   "<",
 49:   /* 20 */
 50:   "<",
 51: };
 52: /* corresponding expected absolute paths - positions in abspath */
 53: static const PetscInt paths2apaths[np] = {
 54:   /* 0 */
 55:   0,
 56:   1,
 57:   2,
 58:   3,
 59:   2,
 60:   /* 5 */
 61:   2,
 62:   2,
 63:   1,
 64:   4,
 65:   5,
 66:   /* 10 */
 67:   4,
 68:   1,
 69:   1,
 70:   1,
 71:   6,
 72:   /* 15 */
 73:   7,
 74:   6,
 75:   1,
 76:   8,
 77:   1,
 78:   /* 20 */
 79:   0,
 80: };

 82: #define ns 4
 83: /* for "" attribute will be stored to group, otherwise to given dataset */
 84: static const char *datasets[ns] = {"", "x", "nonExistingVec", "y"};

 86: /* beware this yields PETSC_FALSE for "" but group "" is interpreted as "/" */
 87: static inline PetscErrorCode shouldExist(const char name[], PetscBool emptyExists, PetscBool *has)
 88: {
 89:   size_t len = 0;

 91:   PetscFunctionBegin;
 92:   PetscCall(PetscStrlen(name, &len));
 93:   *has = emptyExists;
 94:   if (len) {
 95:     char *loc = NULL;
 96:     PetscCall(PetscStrstr(name, "nonExisting", &loc));
 97:     *has = PetscNot(loc);
 98:   }
 99:   PetscFunctionReturn(PETSC_SUCCESS);
100: }

102: static inline PetscErrorCode isPop(const char path[], PetscBool *has)
103: {
104:   PetscFunctionBegin;
105:   PetscCall(PetscStrcmp(path, "<", has));
106:   PetscFunctionReturn(PETSC_SUCCESS);
107: }

109: static inline PetscErrorCode isDot(const char path[], PetscBool *has)
110: {
111:   PetscFunctionBegin;
112:   PetscCall(PetscStrcmp(path, ".", has));
113:   PetscFunctionReturn(PETSC_SUCCESS);
114: }

116: static inline PetscErrorCode isRoot(const char path[], PetscBool *flg)
117: {
118:   size_t len;

120:   PetscFunctionBegin;
121:   PetscCall(PetscStrlen(path, &len));
122:   *flg = PetscNot(len);
123:   if (!*flg) PetscCall(PetscStrcmp(path, "/", flg));
124:   PetscFunctionReturn(PETSC_SUCCESS);
125: }

127: static inline PetscErrorCode compare(PetscDataType dt, void *ptr0, void *ptr1, PetscBool *flg)
128: {
129:   PetscFunctionBegin;
130:   switch (dt) {
131:   case PETSC_INT:
132:     *flg = (PetscBool)(*(PetscInt *)ptr0 == *(PetscInt *)ptr1);
133:     if (verbose) {
134:       if (*flg) {
135:         PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT, *(PetscInt *)ptr0));
136:       } else {
137:         PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT " != %" PetscInt_FMT "\n", *(PetscInt *)ptr0, *(PetscInt *)ptr1));
138:       }
139:     }
140:     break;
141:   case PETSC_REAL:
142:     *flg = (PetscBool)(*(PetscReal *)ptr0 == *(PetscReal *)ptr1);
143:     if (verbose) {
144:       if (*flg) {
145:         PetscCall(PetscPrintf(PETSC_COMM_SELF, "%f", *(PetscReal *)ptr0));
146:       } else {
147:         PetscCall(PetscPrintf(PETSC_COMM_SELF, "%f != %f\n", *(PetscReal *)ptr0, *(PetscReal *)ptr1));
148:       }
149:     }
150:     break;
151:   case PETSC_BOOL:
152:     *flg = (PetscBool)(*(PetscBool *)ptr0 == *(PetscBool *)ptr1);
153:     if (verbose) {
154:       if (*flg) {
155:         PetscCall(PetscPrintf(PETSC_COMM_SELF, "%s", PetscBools[*(PetscBool *)ptr0]));
156:       } else {
157:         PetscCall(PetscPrintf(PETSC_COMM_SELF, "%s != %s\n", PetscBools[*(PetscBool *)ptr0], PetscBools[*(PetscBool *)ptr1]));
158:       }
159:     }
160:     break;
161:   case PETSC_STRING:
162:     PetscCall(PetscStrcmp((const char *)ptr0, (const char *)ptr1, flg));
163:     if (verbose) {
164:       if (*flg) {
165:         PetscCall(PetscPrintf(PETSC_COMM_SELF, "%s", (char *)ptr0));
166:       } else {
167:         PetscCall(PetscPrintf(PETSC_COMM_SELF, "%s != %s\n", (char *)ptr0, (char *)ptr1));
168:       }
169:     }
170:     break;
171:   default:
172:     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "PetscDataType %s not handled here", PetscDataTypes[dt]);
173:   }
174:   PetscFunctionReturn(PETSC_SUCCESS);
175: }

177: static inline PetscErrorCode alterString(const char oldstr[], char str[])
178: {
179:   size_t i, n;

181:   PetscFunctionBegin;
182:   PetscCall(PetscStrlen(oldstr, &n));
183:   PetscCall(PetscStrncpy(str, oldstr, n + 1));
184:   for (i = 0; i < n; i++) {
185:     if (('A' <= str[i] && str[i] < 'Z') || ('a' <= str[i] && str[i] < 'z')) {
186:       str[i]++;
187:       break;
188:     }
189:   }
190:   PetscFunctionReturn(PETSC_SUCCESS);
191: }

193: /* if name given, check dataset with this name exists under current group, otherwise just check current group exists */
194: /* flg: 0 doesn't exist, 1 group, 2 dataset */
195: static PetscErrorCode hasGroupOrDataset(PetscViewer viewer, const char path[], int *flg)
196: {
197:   PetscBool has;

199:   PetscFunctionBegin;
200:   *flg = 0;
201:   PetscCall(PetscViewerHDF5HasGroup(viewer, path, &has));
202:   if (has) *flg = 1;
203:   else {
204:     PetscCall(PetscViewerHDF5HasDataset(viewer, path, &has));
205:     if (has) *flg = 2;
206:   }
207:   PetscFunctionReturn(PETSC_SUCCESS);
208: }

210: #define nt 5 /* number of datatypes */
211: typedef struct _n_Capsule *Capsule;
212: struct _n_Capsule {
213:   char          names[nt][SLEN];
214:   PetscDataType types[nt];
215:   char          typeNames[nt][SLEN];
216:   size_t        sizes[nt];
217:   void         *vals[nt];
218:   PetscInt      id, ntypes;
219: };

221: static PetscErrorCode CapsuleCreate(Capsule old, Capsule *newcapsule)
222: {
223:   Capsule       c;
224:   PetscBool     bool0      = PETSC_TRUE;
225:   PetscInt      int0       = -1;
226:   PetscReal     real0      = -1.1;
227:   char          str0[]     = "Test String";
228:   char          nestr0[]   = "NONEXISTING STRING"; /* this attribute shall be skipped for writing */
229:   void         *vals[nt]   = {&bool0, &int0, &real0, str0, nestr0};
230:   size_t        sizes[nt]  = {sizeof(bool0), sizeof(int0), sizeof(real0), sizeof(str0), sizeof(str0)};
231:   PetscDataType types[nt]  = {PETSC_BOOL, PETSC_INT, PETSC_REAL, PETSC_STRING, PETSC_STRING};
232:   const char   *tNames[nt] = {"bool", "int", "real", "str", "nonExisting"};
233:   PetscInt      t;

235:   PetscFunctionBegin;
236:   PetscCall(PetscNew(&c));
237:   c->id     = 0;
238:   c->ntypes = nt;
239:   if (old) {
240:     /* alter values */
241:     t     = 0;
242:     bool0 = PetscNot(*((PetscBool *)old->vals[t]));
243:     t++;
244:     int0 = *((PetscInt *)old->vals[t]) * -2;
245:     t++;
246:     real0 = *((PetscReal *)old->vals[t]) * -2.0;
247:     t++;
248:     PetscCall(alterString((const char *)old->vals[t], str0));
249:     t++;
250:     c->id = old->id + 1;
251:   }
252:   for (t = 0; t < nt; t++) {
253:     c->sizes[t] = sizes[t];
254:     c->types[t] = types[t];
255:     PetscCall(PetscStrncpy(c->typeNames[t], tNames[t], sizeof(c->typeNames[t])));
256:     PetscCall(PetscSNPrintf(c->names[t], SLEN, "attr_%" PetscInt_FMT "_%s", c->id, tNames[t]));
257:     PetscCall(PetscMalloc(sizes[t], &c->vals[t]));
258:     PetscCall(PetscMemcpy(c->vals[t], vals[t], sizes[t]));
259:   }
260:   *newcapsule = c;
261:   PetscFunctionReturn(PETSC_SUCCESS);
262: }
263: #undef nt

265: static PetscErrorCode CapsuleWriteAttributes(Capsule c, PetscViewer v, const char parent[])
266: {
267:   PetscBool flg = PETSC_FALSE;

269:   PetscFunctionBegin;
270:   for (PetscInt t = 0; t < c->ntypes; t++) {
271:     PetscCall(shouldExist(c->names[t], PETSC_FALSE, &flg));
272:     if (!flg) continue;
273:     PetscCall(PetscViewerHDF5WriteAttribute(v, parent, c->names[t], c->types[t], c->vals[t]));
274:   }
275:   PetscFunctionReturn(PETSC_SUCCESS);
276: }

278: static PetscErrorCode CapsuleReadAndCompareAttributes(Capsule c, PetscViewer v, const char parent[])
279: {
280:   const char *group;
281:   int         gd  = 0;
282:   PetscBool   flg = PETSC_FALSE, hasAttr = PETSC_FALSE;
283:   MPI_Comm    comm;

285:   PetscFunctionBegin;
286:   PetscCall(PetscObjectGetComm((PetscObject)v, &comm));
287:   PetscCall(PetscViewerHDF5GetGroup(v, NULL, &group));
288:   PetscCall(hasGroupOrDataset(v, parent, &gd));
289:   /* check correct existence of attributes */
290:   for (PetscInt t = 0; t < c->ntypes; t++) {
291:     const char *attribute = c->names[t];
292:     PetscCall(shouldExist(attribute, PETSC_FALSE, &flg));
293:     PetscCall(PetscViewerHDF5HasAttribute(v, parent, attribute, &hasAttr));
294:     if (verbose) {
295:       PetscCall(PetscPrintf(comm, "    %-24s = ", attribute));
296:       if (!hasAttr) PetscCall(PetscPrintf(comm, "---"));
297:     }
298:     PetscCheck(gd || !hasAttr, comm, PETSC_ERR_PLIB, "Attribute %s/%s/%s exists while its parent %s/%s doesn't exist", group, parent, attribute, group, parent);
299:     PetscCheck(flg == hasAttr, comm, PETSC_ERR_PLIB, "Attribute %s/%s should exist? %s Exists? %s", parent, attribute, PetscBools[flg], PetscBools[hasAttr]);

301:     /* check loaded attributes are the same as original */
302:     if (hasAttr) {
303:       char  buffer[SLEN];
304:       char *str;
305:       void *ptr0;
306:       /* check the stored data is the same as original */
307:       //TODO datatype should better be output arg, not input
308:       //TODO string attributes should probably have a separate function since the handling is different;
309:       //TODO   or maybe it should just accept string buffer rather than pointer to string
310:       if (c->types[t] == PETSC_STRING) {
311:         PetscCall(PetscViewerHDF5ReadAttribute(v, parent, attribute, c->types[t], NULL, &str));
312:         ptr0 = str;
313:       } else {
314:         PetscCall(PetscViewerHDF5ReadAttribute(v, parent, attribute, c->types[t], NULL, &buffer));
315:         ptr0 = &buffer;
316:       }
317:       PetscCall(compare(c->types[t], ptr0, c->vals[t], &flg));
318:       PetscCheck(flg, comm, PETSC_ERR_PLIB, "Value of attribute %s/%s/%s is not equal to the original value", group, parent, attribute);
319:       if (verbose) PetscCall(PetscPrintf(comm, " (=)"));
320:       if (c->types[t] == PETSC_STRING) PetscCall(PetscFree(str));
321:     }
322:     if (verbose && gd) PetscCall(PetscPrintf(comm, "\n"));
323:   }
324:   PetscCall(PetscFree(group));
325:   PetscFunctionReturn(PETSC_SUCCESS);
326: }

328: static PetscErrorCode CapsuleDestroy(Capsule *c)
329: {
330:   PetscFunctionBegin;
331:   if (!*c) PetscFunctionReturn(PETSC_SUCCESS);
332:   for (PetscInt t = 0; t < (*c)->ntypes; t++) PetscCall(PetscFree((*c)->vals[t]));
333:   PetscCall(PetscFree(*c));
334:   PetscFunctionReturn(PETSC_SUCCESS);
335: }

337: static PetscErrorCode testGroupsDatasets(PetscViewer viewer)
338: {
339:   char        buf[PETSC_MAX_PATH_LEN];
340:   Vec         vecs[nap][ns];
341:   PetscInt    p, s;
342:   PetscBool   flg = PETSC_FALSE, flg1 = PETSC_FALSE, flg2 = PETSC_FALSE;
343:   PetscRandom rand;
344:   const char *filename;
345:   MPI_Comm    comm;

347:   PetscFunctionBegin;
348:   PetscCall(PetscObjectGetComm((PetscObject)viewer, &comm));
349:   PetscCall(PetscViewerFileGetName(viewer, &filename));
350:   if (verbose) PetscCall(PetscPrintf(comm, "# TEST testGroupsDatasets\n"));
351:   /* store random vectors */
352:   PetscCall(PetscRandomCreate(comm, &rand));
353:   PetscCall(PetscRandomSetInterval(rand, 0.0, 10.0));
354:   PetscCall(PetscRandomSetFromOptions(rand));
355:   PetscCall(PetscMemzero(vecs, nap * ns * sizeof(Vec)));

357:   /* test dataset writing */
358:   if (verbose) PetscCall(PetscPrintf(comm, "## WRITE PHASE\n"));
359:   for (p = 0; p < np; p++) {
360:     PetscCall(isPop(paths[p], &flg));
361:     PetscCall(isDot(paths[p], &flg1));
362:     PetscCall(shouldExist(apaths[paths2apaths[p]], PETSC_FALSE, &flg2));
363:     if (flg) {
364:       PetscCall(PetscViewerHDF5PopGroup(viewer));
365:     } else {
366:       PetscCall(PetscViewerHDF5PushGroup(viewer, paths[p]));
367:     }
368:     if (verbose) PetscCall(PetscPrintf(comm, "%-32s => %4s => %-32s  should exist? %s\n", paths[p], flg ? "pop" : "push", apaths[paths2apaths[p]], PetscBools[flg2]));
369:     if (flg || flg1 || !flg2) continue;

371:     for (s = 0; s < ns; s++) {
372:       Vec v;

374:       PetscCall(shouldExist(datasets[s], PETSC_FALSE, &flg));
375:       if (!flg) continue;

377:       PetscCall(VecCreate(comm, &v));
378:       PetscCall(PetscObjectSetName((PetscObject)v, datasets[s]));
379:       PetscCall(VecSetSizes(v, n, PETSC_DECIDE));
380:       PetscCall(VecSetFromOptions(v));
381:       PetscCall(VecSetRandom(v, rand));
382:       if (verbose) {
383:         PetscReal min, max;
384:         PetscCall(VecMin(v, NULL, &min));
385:         PetscCall(VecMax(v, NULL, &max));
386:         PetscCall(PetscPrintf(comm, "  Create dataset %s/%s, keep in memory in vecs[%" PetscInt_FMT "][%" PetscInt_FMT "], min %.3e max %.3e\n", apaths[paths2apaths[p]], datasets[s], paths2apaths[p], s, min, max));
387:       }

389:       PetscCall(VecView(v, viewer));
390:       vecs[paths2apaths[p]][s] = v;
391:     }
392:   }
393:   PetscCall(PetscViewerFlush(viewer));
394:   PetscCall(PetscRandomDestroy(&rand));

396:   if (verbose) PetscCall(PetscPrintf(comm, "\n## READ PHASE\n"));
397:   /* check correct existence of groups in file */
398:   for (p = 0; p < np; p++) {
399:     const char *group;
400:     const char *expected = apaths[paths2apaths[p]];

402:     /* check Push/Pop is correct */
403:     PetscCall(isPop(paths[p], &flg));
404:     if (flg) {
405:       PetscCall(PetscViewerHDF5PopGroup(viewer));
406:     } else {
407:       PetscCall(PetscViewerHDF5PushGroup(viewer, paths[p]));
408:     }
409:     PetscCall(PetscViewerHDF5GetGroup(viewer, NULL, &group));
410:     PetscCall(PetscViewerHDF5HasGroup(viewer, NULL, &flg1));
411:     if (verbose) PetscCall(PetscPrintf(comm, "%-32s => %4s => %-32s  exists? %s\n", paths[p], flg ? "pop" : "push", group, PetscBools[flg1]));
412:     PetscCall(PetscStrcmp(group, expected, &flg2));
413:     PetscCheck(flg2, comm, PETSC_ERR_PLIB, "Current group %s not equal to expected %s", group, expected);
414:     PetscCall(shouldExist(group, PETSC_TRUE, &flg2));
415:     PetscCheck(flg1 == flg2, comm, PETSC_ERR_PLIB, "Group %s should exist? %s Exists in %s? %s", group, PetscBools[flg2], filename, PetscBools[flg1]);
416:     PetscCall(PetscFree(group));
417:   }

419:   /* check existence of datasets; compare loaded vectors with original ones */
420:   for (p = 0; p < np; p++) {
421:     const char *group;

423:     /* check Push/Pop is correct */
424:     PetscCall(isPop(paths[p], &flg));
425:     if (flg) {
426:       PetscCall(PetscViewerHDF5PopGroup(viewer));
427:     } else {
428:       PetscCall(PetscViewerHDF5PushGroup(viewer, paths[p]));
429:     }
430:     PetscCall(PetscViewerHDF5GetGroup(viewer, NULL, &group));
431:     PetscCall(PetscViewerHDF5HasGroup(viewer, NULL, &flg));
432:     if (verbose) PetscCall(PetscPrintf(comm, "Has %s group? %s\n", group, PetscBools[flg]));
433:     for (s = 0; s < ns; s++) {
434:       const char *name     = datasets[s];
435:       char       *fullname = buf;

437:       /* check correct existence of datasets in file */
438:       PetscCall(PetscSNPrintf(fullname, sizeof(buf), "%s/%s", group, name));
439:       PetscCall(shouldExist(name, PETSC_FALSE, &flg1));
440:       flg1 = (PetscBool)(flg && flg1); /* both group and dataset need to exist */
441:       PetscCall(PetscViewerHDF5HasDataset(viewer, name, &flg2));
442:       if (verbose) PetscCall(PetscPrintf(comm, "    %s dataset? %s", fullname, PetscBools[flg2]));
443:       PetscCheck(flg2 == flg1, comm, PETSC_ERR_PLIB, "Dataset %s should exist? %s Exists in %s? %s", fullname, PetscBools[flg1], filename, PetscBools[flg2]);

445:       if (flg2) {
446:         Vec v;
447:         /* check loaded Vec is the same as original */
448:         PetscCall(VecCreate(comm, &v));
449:         PetscCall(PetscObjectSetName((PetscObject)v, name));
450:         PetscCall(VecLoad(v, viewer));
451:         PetscCall(VecEqual(v, vecs[paths2apaths[p]][s], &flg1));
452:         PetscCheck(flg1, comm, PETSC_ERR_PLIB, "Dataset %s in %s is not equal to the original Vec", fullname, filename);
453:         if (verbose) PetscCall(PetscPrintf(comm, " (=)"));
454:         PetscCall(VecDestroy(&v));
455:       }
456:       if (verbose) PetscCall(PetscPrintf(comm, "\n"));
457:     }
458:     PetscCall(PetscFree(group));
459:   }
460:   PetscCall(PetscViewerFlush(viewer));
461:   for (p = 0; p < nap; p++)
462:     for (s = 0; s < ns; s++) PetscCall(VecDestroy(&vecs[p][s]));
463:   if (verbose) PetscCall(PetscPrintf(comm, "# END  testGroupsDatasets\n\n"));
464:   PetscFunctionReturn(PETSC_SUCCESS);
465: }

467: static inline PetscErrorCode formPath(PetscBool relativize, const char path[], const char dataset[], char buf[], size_t bufsize)
468: {
469:   PetscBool isroot = PETSC_FALSE;

471:   PetscFunctionBegin;
472:   PetscCall(isRoot(path, &isroot));
473:   if (relativize) {
474:     if (isroot) {
475:       PetscCall(PetscStrncpy(buf, dataset, bufsize));
476:     } else {
477:       /* skip initial '/' in paths[p] if prefix given */
478:       PetscCall(PetscSNPrintf(buf, bufsize, "%s/%s", path + 1, dataset));
479:     }
480:   } else {
481:     PetscCall(PetscSNPrintf(buf, bufsize, "%s/%s", isroot ? "" : path, dataset));
482:   }
483:   PetscFunctionReturn(PETSC_SUCCESS);
484: }

486: /* test attribute writing, existence checking and reading, use absolute paths */
487: static PetscErrorCode testAttributesAbsolutePath(PetscViewer viewer, const char prefix[])
488: {
489:   char      buf[PETSC_MAX_PATH_LEN];
490:   Capsule   capsules[nap][ns], c = NULL, old = NULL;
491:   PetscInt  p, s;
492:   PetscBool flg = PETSC_FALSE, flg1 = PETSC_FALSE;
493:   MPI_Comm  comm;

495:   PetscFunctionBegin;
496:   PetscCall(PetscObjectGetComm((PetscObject)viewer, &comm));
497:   if (verbose) {
498:     if (prefix) {
499:       PetscCall(PetscPrintf(comm, "# TEST testAttributesAbsolutePath, prefix=\"%s\"\n", prefix));
500:     } else {
501:       PetscCall(PetscPrintf(comm, "# TEST testAttributesAbsolutePath\n"));
502:     }
503:     PetscCall(PetscPrintf(comm, "## WRITE PHASE\n"));
504:   }
505:   PetscCall(PetscMemzero(capsules, nap * ns * sizeof(Capsule)));

507:   /* test attribute writing */
508:   if (prefix) PetscCall(PetscViewerHDF5PushGroup(viewer, prefix));
509:   for (p = 0; p < np; p++)
510:     for (s = 0; s < ns; s++) {
511:       /* we test only absolute paths here */
512:       PetscCall(PetscViewerHDF5PathIsRelative(paths[p], PETSC_FALSE, &flg));
513:       if (flg) continue;
514:       {
515:         const char *group;

517:         PetscCall(PetscViewerHDF5GetGroup(viewer, NULL, &group));
518:         PetscCall(PetscStrcmp(group, prefix, &flg));
519:         PetscCheck(flg, comm, PETSC_ERR_PLIB, "prefix %s not equal to pushed group %s", prefix, group);
520:         PetscCall(PetscFree(group));
521:       }
522:       PetscCall(formPath((PetscBool)!!prefix, paths[p], datasets[s], buf, sizeof(buf)));
523:       PetscCall(shouldExist(buf, PETSC_TRUE, &flg));
524:       if (!flg) continue;

526:       if (verbose) {
527:         if (prefix) {
528:           PetscCall(PetscPrintf(comm, "Write attributes to %s/%s\n", prefix, buf));
529:         } else {
530:           PetscCall(PetscPrintf(comm, "Write attributes to %s\n", buf));
531:         }
532:       }

534:       PetscCall(CapsuleCreate(old, &c));
535:       PetscCall(CapsuleWriteAttributes(c, viewer, buf));
536:       PetscCheck(!capsules[paths2apaths[p]][s], comm, PETSC_ERR_PLIB, "capsules[%" PetscInt_FMT "][%" PetscInt_FMT "] gets overwritten for %s", paths2apaths[p], s, buf);
537:       capsules[paths2apaths[p]][s] = c;
538:       old                          = c;
539:     }
540:   if (prefix) PetscCall(PetscViewerHDF5PopGroup(viewer));
541:   PetscCall(PetscViewerFlush(viewer));

543:   if (verbose) PetscCall(PetscPrintf(comm, "\n## READ PHASE\n"));
544:   if (prefix) PetscCall(PetscViewerHDF5PushGroup(viewer, prefix));
545:   for (p = 0; p < np; p++)
546:     for (s = 0; s < ns; s++) {
547:       /* we test only absolute paths here */
548:       PetscCall(PetscViewerHDF5PathIsRelative(paths[p], PETSC_FALSE, &flg));
549:       if (flg) continue;

551:       /* check existence of given group/dataset */
552:       PetscCall(formPath((PetscBool)!!prefix, paths[p], datasets[s], buf, sizeof(buf)));
553:       PetscCall(shouldExist(buf, PETSC_TRUE, &flg));
554:       if (verbose) {
555:         if (prefix) {
556:           PetscCall(PetscPrintf(comm, "Has %s/%s? %s\n", prefix, buf, PetscBools[flg]));
557:         } else {
558:           PetscCall(PetscPrintf(comm, "Has %s? %s\n", buf, PetscBools[flg]));
559:         }
560:       }

562:       /* check attribute capsule has been created for given path */
563:       c    = capsules[paths2apaths[p]][s];
564:       flg1 = (PetscBool)!!c;
565:       PetscCheck(flg == flg1, comm, PETSC_ERR_PLIB, "Capsule should exist for %s? %s Exists? %s", buf, PetscBools[flg], PetscBools[flg1]);
566:       if (!flg) continue;

568:       /* check correct existence and fidelity of attributes in file */
569:       PetscCall(CapsuleReadAndCompareAttributes(c, viewer, buf));
570:     }
571:   if (prefix) PetscCall(PetscViewerHDF5PopGroup(viewer));
572:   PetscCall(PetscViewerFlush(viewer));
573:   for (p = 0; p < nap; p++)
574:     for (s = 0; s < ns; s++) PetscCall(CapsuleDestroy(&capsules[p][s]));
575:   if (verbose) PetscCall(PetscPrintf(comm, "# END  testAttributesAbsolutePath\n\n"));
576:   PetscFunctionReturn(PETSC_SUCCESS);
577: }

579: /* test attribute writing, existence checking and reading, use group push/pop */
580: static PetscErrorCode testAttributesPushedPath(PetscViewer viewer)
581: {
582:   Capsule   capsules[nap][ns], c = NULL, old = NULL;
583:   PetscInt  p;
584:   int       gd;
585:   PetscBool flg = PETSC_FALSE, flg1 = PETSC_FALSE;
586:   MPI_Comm  comm;

588:   PetscFunctionBegin;
589:   PetscCall(PetscObjectGetComm((PetscObject)viewer, &comm));
590:   if (verbose) {
591:     PetscCall(PetscPrintf(comm, "# TEST testAttributesPushedPath\n"));
592:     PetscCall(PetscPrintf(comm, "## WRITE PHASE\n"));
593:   }
594:   PetscCall(PetscMemzero(capsules, nap * ns * sizeof(Capsule)));

596:   /* test attribute writing */
597:   for (p = 0; p < np; p++) {
598:     PetscCall(isPop(paths[p], &flg));
599:     PetscCall(isDot(paths[p], &flg1));
600:     if (flg) {
601:       PetscCall(PetscViewerHDF5PopGroup(viewer));
602:     } else {
603:       PetscCall(PetscViewerHDF5PushGroup(viewer, paths[p]));
604:     }
605:     /* < and . have been already visited => skip */
606:     if (flg || flg1) continue;

608:     /* assume here that groups and datasets are already in the file */
609:     for (PetscInt s = 0; s < ns; s++) {
610:       PetscCall(hasGroupOrDataset(viewer, datasets[s], &gd));
611:       if (!gd) continue;
612:       if (verbose) PetscCall(PetscPrintf(comm, "Write attributes to %s/%s\n", apaths[paths2apaths[p]], datasets[s]));
613:       PetscCall(CapsuleCreate(old, &c));
614:       PetscCall(CapsuleWriteAttributes(c, viewer, datasets[s]));
615:       PetscCheck(!capsules[paths2apaths[p]][s], comm, PETSC_ERR_PLIB, "capsules[%" PetscInt_FMT "][%" PetscInt_FMT "] gets overwritten for %s/%s", paths2apaths[p], s, paths[p], datasets[s]);
616:       capsules[paths2apaths[p]][s] = c;
617:       old                          = c;
618:     }
619:   }
620:   PetscCall(PetscViewerFlush(viewer));

622:   if (verbose) PetscCall(PetscPrintf(comm, "\n## READ PHASE\n"));
623:   for (p = 0; p < np; p++) {
624:     const char *group;

626:     PetscCall(isPop(paths[p], &flg1));
627:     if (flg1) {
628:       PetscCall(PetscViewerHDF5PopGroup(viewer));
629:     } else {
630:       PetscCall(PetscViewerHDF5PushGroup(viewer, paths[p]));
631:     }
632:     PetscCall(PetscViewerHDF5GetGroup(viewer, NULL, &group));
633:     for (PetscInt s = 0; s < ns; s++) {
634:       PetscCall(hasGroupOrDataset(viewer, datasets[s], &gd));
635:       if (verbose) PetscCall(PetscPrintf(comm, "%s/%s   %s\n", group, datasets[s], gd ? (gd == 1 ? "is group" : "is dataset") : "does not exist"));

637:       /* check attribute capsule has been created for given path */
638:       c    = capsules[paths2apaths[p]][s];
639:       flg  = (PetscBool)!!gd;
640:       flg1 = (PetscBool)!!c;
641:       PetscCheck(flg == flg1, comm, PETSC_ERR_PLIB, "Capsule should exist for %s/%s? %s Exists? %s", group, datasets[s], PetscBools[flg], PetscBools[flg1]);
642:       if (!flg) continue;

644:       /* check correct existence of attributes in file */
645:       PetscCall(CapsuleReadAndCompareAttributes(c, viewer, datasets[s]));
646:     }
647:     PetscCall(PetscFree(group));
648:   }
649:   PetscCall(PetscViewerFlush(viewer));
650:   for (p = 0; p < nap; p++)
651:     for (PetscInt s = 0; s < ns; s++) PetscCall(CapsuleDestroy(&capsules[p][s]));
652:   if (verbose) PetscCall(PetscPrintf(comm, "# END  testAttributesPushedPath\n\n"));
653:   PetscFunctionReturn(PETSC_SUCCESS);
654: }

656: /* test attribute writing, existence checking and reading, use group push/pop */
657: static PetscErrorCode testObjectAttributes(PetscViewer viewer)
658: {
659:   Capsule   capsules[nap][ns], c = NULL, old = NULL;
660:   PetscInt  p;
661:   PetscBool flg = PETSC_FALSE, flg1 = PETSC_FALSE;
662:   MPI_Comm  comm;

664:   PetscFunctionBegin;
665:   PetscCall(PetscObjectGetComm((PetscObject)viewer, &comm));
666:   if (verbose) {
667:     PetscCall(PetscPrintf(comm, "# TEST testObjectAttributes\n"));
668:     PetscCall(PetscPrintf(comm, "## WRITE PHASE\n"));
669:   }
670:   PetscCall(PetscMemzero(capsules, nap * ns * sizeof(Capsule)));

672:   /* test attribute writing */
673:   for (p = 0; p < np; p++) {
674:     PetscCall(isPop(paths[p], &flg));
675:     PetscCall(isDot(paths[p], &flg1));
676:     if (flg) {
677:       PetscCall(PetscViewerHDF5PopGroup(viewer));
678:     } else {
679:       PetscCall(PetscViewerHDF5PushGroup(viewer, paths[p]));
680:     }
681:     /* < and . have been already visited => skip */
682:     if (flg || flg1) continue;

684:     /* assume here that groups and datasets are already in the file */
685:     for (PetscInt s = 0; s < ns; s++) {
686:       Vec         v;
687:       size_t      len;
688:       const char *name = datasets[s];

690:       PetscCall(PetscStrlen(name, &len));
691:       if (!len) continue;
692:       PetscCall(VecCreate(comm, &v));
693:       PetscCall(PetscObjectSetName((PetscObject)v, name));
694:       PetscCall(PetscViewerHDF5HasObject(viewer, (PetscObject)v, &flg));
695:       if (flg) {
696:         if (verbose) PetscCall(PetscPrintf(comm, "Write attributes to %s/%s\n", apaths[paths2apaths[p]], name));
697:         PetscCall(CapsuleCreate(old, &c));
698:         PetscCall(CapsuleWriteAttributes(c, viewer, name));
699:         PetscCheck(!capsules[paths2apaths[p]][s], comm, PETSC_ERR_PLIB, "capsules[%" PetscInt_FMT "][%" PetscInt_FMT "] gets overwritten for %s/%s", paths2apaths[p], s, paths[p], name);
700:         capsules[paths2apaths[p]][s] = c;
701:         old                          = c;
702:       }
703:       PetscCall(VecDestroy(&v));
704:     }
705:   }
706:   PetscCall(PetscViewerFlush(viewer));

708:   if (verbose) PetscCall(PetscPrintf(comm, "\n## READ PHASE\n"));
709:   for (p = 0; p < np; p++) {
710:     const char *group;

712:     PetscCall(isPop(paths[p], &flg));
713:     if (flg) {
714:       PetscCall(PetscViewerHDF5PopGroup(viewer));
715:     } else {
716:       PetscCall(PetscViewerHDF5PushGroup(viewer, paths[p]));
717:     }
718:     PetscCall(PetscViewerHDF5GetGroup(viewer, NULL, &group));
719:     for (PetscInt s = 0; s < ns; s++) {
720:       Vec         v;
721:       size_t      len;
722:       const char *name = datasets[s];

724:       PetscCall(PetscStrlen(name, &len));
725:       if (!len) continue;
726:       PetscCall(VecCreate(comm, &v));
727:       PetscCall(PetscObjectSetName((PetscObject)v, name));
728:       PetscCall(PetscViewerHDF5HasObject(viewer, (PetscObject)v, &flg));
729:       if (verbose) PetscCall(PetscPrintf(comm, "Is %s/%s dataset? %s\n", group, name, PetscBools[flg]));

731:       /* check attribute capsule has been created for given path */
732:       c    = capsules[paths2apaths[p]][s];
733:       flg1 = (PetscBool)!!c;
734:       PetscCheck(flg == flg1, comm, PETSC_ERR_PLIB, "Capsule should exist for %s/%s? %s Exists? %s", group, name, PetscBools[flg], PetscBools[flg1]);

736:       /* check correct existence of attributes in file */
737:       if (flg) PetscCall(CapsuleReadAndCompareAttributes(c, viewer, name));
738:       PetscCall(VecDestroy(&v));
739:     }
740:     PetscCall(PetscFree(group));
741:   }
742:   PetscCall(PetscViewerFlush(viewer));
743:   for (p = 0; p < nap; p++)
744:     for (PetscInt s = 0; s < ns; s++) PetscCall(CapsuleDestroy(&capsules[p][s]));
745:   if (verbose) PetscCall(PetscPrintf(comm, "# END  testObjectAttributes\n\n"));
746:   PetscFunctionReturn(PETSC_SUCCESS);
747: }

749: static PetscErrorCode testAttributesDefaultValue(PetscViewer viewer)
750: {
751: #define nv 4
752:   PetscBool bools[nv];
753:   PetscInt  ints[nv];
754:   PetscReal reals[nv];
755:   char     *strings[nv];
756:   PetscBool flg;
757:   MPI_Comm  comm;

759:   PetscFunctionBegin;
760:   PetscCall(PetscObjectGetComm((PetscObject)viewer, &comm));
761:   if (verbose) PetscCall(PetscPrintf(comm, "# TEST testAttributesDefaultValue\n"));

763:   PetscCall(PetscViewerHDF5ReadAttribute(viewer, "/", "attr_0_bool", PETSC_BOOL, NULL, &bools[0]));
764:   bools[1] = PetscNot(bools[0]);
765:   PetscCall(PetscViewerHDF5ReadAttribute(viewer, "/", "attr_0_bool", PETSC_BOOL, &bools[1], &bools[2]));
766:   PetscCall(PetscViewerHDF5ReadAttribute(viewer, "/", "attr_nonExisting_bool", PETSC_BOOL, &bools[1], &bools[3]));
767:   PetscCheck(bools[2] == bools[0], comm, PETSC_ERR_PLIB, "%s = bools[2] != bools[0] = %s", PetscBools[bools[2]], PetscBools[bools[0]]);
768:   PetscCheck(bools[3] == bools[1], comm, PETSC_ERR_PLIB, "%s = bools[3] != bools[1] = %s", PetscBools[bools[3]], PetscBools[bools[1]]);

770:   PetscCall(PetscViewerHDF5ReadAttribute(viewer, "/", "attr_0_int", PETSC_INT, NULL, &ints[0]));
771:   ints[1] = ints[0] * -333;
772:   PetscCall(PetscViewerHDF5ReadAttribute(viewer, "/", "attr_0_int", PETSC_INT, &ints[1], &ints[2]));
773:   PetscCall(PetscViewerHDF5ReadAttribute(viewer, "/", "attr_nonExisting_int", PETSC_INT, &ints[1], &ints[3]));
774:   PetscCheck(ints[2] == ints[0], comm, PETSC_ERR_PLIB, "%" PetscInt_FMT " = ints[2] != ints[0] = %" PetscInt_FMT, ints[2], ints[0]);
775:   PetscCheck(ints[3] == ints[1], comm, PETSC_ERR_PLIB, "%" PetscInt_FMT " = ints[3] != ints[1] = %" PetscInt_FMT, ints[3], ints[1]);
776:   if (verbose) PetscCall(PetscIntView(nv, ints, PETSC_VIEWER_STDOUT_WORLD));

778:   PetscCall(PetscViewerHDF5ReadAttribute(viewer, "/", "attr_0_real", PETSC_REAL, NULL, &reals[0]));
779:   reals[1] = reals[0] * -11.1;
780:   PetscCall(PetscViewerHDF5ReadAttribute(viewer, "/", "attr_0_real", PETSC_REAL, &reals[1], &reals[2]));
781:   PetscCall(PetscViewerHDF5ReadAttribute(viewer, "/", "attr_nonExisting_real", PETSC_REAL, &reals[1], &reals[3]));
782:   PetscCheck(reals[2] == reals[0], comm, PETSC_ERR_PLIB, "%f = reals[2] != reals[0] = %f", reals[2], reals[0]);
783:   PetscCheck(reals[3] == reals[1], comm, PETSC_ERR_PLIB, "%f = reals[3] != reals[1] = %f", reals[3], reals[1]);
784:   if (verbose) PetscCall(PetscRealView(nv, reals, PETSC_VIEWER_STDOUT_WORLD));

786:   PetscCall(PetscViewerHDF5ReadAttribute(viewer, "/", "attr_0_str", PETSC_STRING, NULL, &strings[0]));
787:   PetscCall(PetscStrallocpy(strings[0], &strings[1]));
788:   PetscCall(alterString(strings[0], strings[1]));
789:   PetscCall(PetscViewerHDF5ReadAttribute(viewer, "/", "attr_0_str", PETSC_STRING, &strings[1], &strings[2]));
790:   PetscCall(PetscViewerHDF5ReadAttribute(viewer, "/", "attr_nonExisting_str", PETSC_STRING, &strings[1], &strings[3]));
791:   PetscCall(PetscStrcmp(strings[2], strings[0], &flg));
792:   PetscCheck(flg, comm, PETSC_ERR_PLIB, "%s = strings[2] != strings[0] = %s", strings[2], strings[0]);
793:   PetscCall(PetscStrcmp(strings[3], strings[1], &flg));
794:   PetscCheck(flg, comm, PETSC_ERR_PLIB, "%s = strings[3] != strings[1] = %s", strings[3], strings[1]);
795:   for (PetscInt i = 0; i < nv; i++) PetscCall(PetscFree(strings[i]));

797:   PetscCall(PetscViewerFlush(viewer));
798:   if (verbose) PetscCall(PetscPrintf(comm, "# END  testAttributesDefaultValue\n"));
799: #undef nv
800:   PetscFunctionReturn(PETSC_SUCCESS);
801: }

803: int main(int argc, char **argv)
804: {
805:   static char filename[PETSC_MAX_PATH_LEN] = "ex48.h5";
806:   PetscMPIInt rank;
807:   MPI_Comm    comm;
808:   PetscViewer viewer;

810:   PetscFunctionBegin;
811:   PetscFunctionBeginUser;
812:   PetscCall(PetscInitialize(&argc, &argv, NULL, help));
813:   comm = PETSC_COMM_WORLD;
814:   PetscCallMPI(MPI_Comm_rank(comm, &rank));
815:   PetscCall(PetscOptionsGetInt(NULL, NULL, "-n", &n, NULL));
816:   PetscCall(PetscOptionsGetBool(NULL, NULL, "-verbose", &verbose, NULL));
817:   PetscCall(PetscOptionsGetString(NULL, NULL, "-filename", filename, sizeof(filename), NULL));
818:   if (verbose) PetscCall(PetscPrintf(comm, "np ns " PetscStringize(np) " " PetscStringize(ns) "\n"));

820:   PetscCall(PetscViewerHDF5Open(comm, filename, FILE_MODE_WRITE, &viewer));
821:   PetscCall(testGroupsDatasets(viewer));
822:   PetscCall(testAttributesAbsolutePath(viewer, "/"));
823:   PetscCall(testAttributesAbsolutePath(viewer, "/prefix"));
824:   PetscCall(PetscViewerDestroy(&viewer));

826:   /* test reopening in update mode */
827:   PetscCall(PetscViewerHDF5Open(comm, filename, FILE_MODE_UPDATE, &viewer));
828:   PetscCall(testAttributesPushedPath(viewer));
829:   PetscCall(testObjectAttributes(viewer));
830:   PetscCall(testAttributesDefaultValue(viewer));
831:   PetscCall(PetscViewerDestroy(&viewer));
832:   PetscCall(PetscFinalize());
833:   return 0;
834: }

836: /*TEST

838:      build:
839:        requires: hdf5

841:      test:
842:        nsize: {{1 4}}
843:        output_file: output/empty.out

845: TEST*/