Actual source code: hdf5io.c
1: #include <petsc/private/viewerhdf5impl.h>
2: #include <petsclayouthdf5.h>
3: #include <petscis.h>
5: struct _n_HDF5ReadCtx {
6: const char *name;
7: hid_t file, group, dataset, dataspace;
8: int lenInd, bsInd, complexInd, rdim;
9: hsize_t *dims;
10: PetscBool complexVal, dim2;
12: // Needed for compression
13: PetscInt runs;
14: PetscInt *cind;
15: };
16: typedef struct _n_HDF5ReadCtx *HDF5ReadCtx;
18: PetscErrorCode PetscViewerHDF5CheckTimestepping_Internal(PetscViewer viewer, const char name[])
19: {
20: PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;
21: PetscBool timestepping = PETSC_FALSE;
23: PetscFunctionBegin;
24: PetscCall(PetscViewerHDF5ReadAttribute(viewer, name, "timestepping", PETSC_BOOL, &hdf5->defTimestepping, ×tepping));
25: if (timestepping != hdf5->timestepping) {
26: const char *group;
28: PetscCall(PetscViewerHDF5GetGroup(viewer, NULL, &group));
29: SETERRQ(PetscObjectComm((PetscObject)viewer), PETSC_ERR_FILE_UNEXPECTED, "Dataset %s/%s stored with timesteps? %s Timestepping pushed? %s", group, name, PetscBools[timestepping], PetscBools[hdf5->timestepping]);
30: }
31: PetscFunctionReturn(PETSC_SUCCESS);
32: }
34: static PetscErrorCode PetscViewerHDF5ReadInitialize_Private(PetscViewer viewer, const char name[], HDF5ReadCtx *ctx)
35: {
36: PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;
37: HDF5ReadCtx h = NULL;
39: PetscFunctionBegin;
40: PetscCall(PetscViewerHDF5CheckTimestepping_Internal(viewer, name));
41: PetscCall(PetscNew(&h));
42: h->name = name;
43: PetscCall(PetscViewerHDF5OpenGroup(viewer, NULL, &h->file, &h->group));
44: PetscCallHDF5Return(h->dataset, H5Dopen2, (h->group, name, H5P_DEFAULT));
45: PetscCallHDF5Return(h->dataspace, H5Dget_space, (h->dataset));
46: PetscCall(PetscViewerHDF5ReadAttribute(viewer, name, "complex", PETSC_BOOL, &h->complexVal, &h->complexVal));
47: if (!hdf5->horizontal) {
48: /* MATLAB stores column vectors horizontally */
49: PetscCall(PetscViewerHDF5HasAttribute(viewer, name, "MATLAB_class", &hdf5->horizontal));
50: }
51: h->runs = 0;
52: h->cind = NULL;
53: *ctx = h;
54: PetscFunctionReturn(PETSC_SUCCESS);
55: }
57: static PetscErrorCode PetscViewerHDF5ReadFinalize_Private(PetscViewer viewer, HDF5ReadCtx *ctx)
58: {
59: HDF5ReadCtx h;
61: PetscFunctionBegin;
62: h = *ctx;
63: PetscCallHDF5(H5Gclose, (h->group));
64: PetscCallHDF5(H5Sclose, (h->dataspace));
65: PetscCallHDF5(H5Dclose, (h->dataset));
66: PetscCall(PetscFree((*ctx)->dims));
67: PetscCall(PetscFree((*ctx)->cind));
68: PetscCall(PetscFree(*ctx));
69: PetscFunctionReturn(PETSC_SUCCESS);
70: }
72: // Need forward declaration because we have a cyclic call chain
73: static PetscErrorCode PetscViewerHDF5Load_Internal(PetscViewer, const char[], PetscBool, PetscLayout, hid_t, void **);
75: static PetscErrorCode PetscViewerHDF5ReadSizes_Private(PetscViewer viewer, HDF5ReadCtx ctx, PetscBool uncompress, PetscBool setup, PetscLayout *map_)
76: {
77: PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;
78: PetscInt bs, N;
79: PetscLayout map;
80: PetscBool compressed;
82: PetscFunctionBegin;
83: if (!*map_) PetscCall(PetscLayoutCreate(PetscObjectComm((PetscObject)viewer), map_));
84: map = *map_;
86: PetscCall(PetscViewerHDF5HasAttribute(viewer, ctx->name, "compressed", &compressed));
87: if (compressed && uncompress) {
88: hid_t inttype;
89: PetscLayout cmap;
90: PetscInt *lcind, N = 0;
91: PetscMPIInt *counts, *displs, size, n;
92: const PetscInt *range;
93: MPI_Comm comm;
95: inttype = PetscDefined(USE_64BIT_INDICES) ? H5T_NATIVE_LLONG : H5T_NATIVE_INT;
96: PetscCall(PetscObjectGetComm((PetscObject)viewer, &comm));
97: PetscCall(PetscLayoutCreate(PetscObjectComm((PetscObject)viewer), &cmap));
98: cmap->bs = 3;
99: PetscCall(PetscViewerHDF5Load_Internal(viewer, ctx->name, PETSC_FALSE, cmap, inttype, (void **)&lcind));
100: PetscCheck(!(cmap->n % 3), PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Compressed IS must have an even number of entries, not %" PetscInt_FMT, cmap->n);
101: for (PetscInt i = 0; i < cmap->n / 3; ++i) N += lcind[i * 3 + 0];
102: PetscCallMPI(MPIU_Allreduce(MPI_IN_PLACE, &N, 1, MPIU_INT, MPIU_SUM, comm));
103: ctx->runs = cmap->N / 3;
104: PetscCall(PetscMalloc1(cmap->N, &ctx->cind));
105: PetscCallMPI(MPI_Comm_size(comm, &size));
106: PetscCall(PetscLayoutGetRanges(cmap, &range));
107: PetscCall(PetscMalloc2(size, &counts, size, &displs));
108: for (PetscInt r = 0; r < size; ++r) {
109: PetscCall(PetscMPIIntCast(range[r + 1] - range[r], &counts[r]));
110: PetscCall(PetscMPIIntCast(range[r], &displs[r]));
111: }
112: PetscCall(PetscMPIIntCast(cmap->n, &n));
113: PetscCallMPI(MPI_Allgatherv(lcind, n, MPIU_INT, ctx->cind, counts, displs, MPIU_INT, comm));
114: PetscCall(PetscFree2(counts, displs));
115: PetscCall(PetscFree(lcind));
116: PetscCall(PetscLayoutDestroy(&cmap));
118: ctx->dim2 = PETSC_FALSE;
119: ctx->rdim = 1;
120: ctx->lenInd = 0;
121: PetscCall(PetscMalloc1(ctx->rdim, &ctx->dims));
122: ctx->dims[0] = N;
123: bs = 1;
124: goto layout;
125: }
127: /* Get actual number of dimensions in dataset */
128: PetscCallHDF5Return(ctx->rdim, H5Sget_simple_extent_dims, (ctx->dataspace, NULL, NULL));
129: PetscCall(PetscMalloc1(ctx->rdim, &ctx->dims));
130: PetscCallHDF5Return(ctx->rdim, H5Sget_simple_extent_dims, (ctx->dataspace, ctx->dims, NULL));
132: /*
133: Dimensions are in this order:
134: [0] timesteps (optional)
135: [lenInd] entries (numbers or blocks)
136: ...
137: [bsInd] entries of blocks (optional)
138: [bsInd+1] real & imaginary part (optional)
139: = rdim-1
140: */
142: /* Get entries dimension index */
143: ctx->lenInd = 0;
144: if (hdf5->timestepping) ++ctx->lenInd;
146: /* Get block dimension index */
147: if (ctx->complexVal) {
148: ctx->bsInd = ctx->rdim - 2;
149: ctx->complexInd = ctx->rdim - 1;
150: } else {
151: ctx->bsInd = ctx->rdim - 1;
152: ctx->complexInd = -1;
153: }
154: PetscCheck(ctx->lenInd <= ctx->bsInd, PetscObjectComm((PetscObject)viewer), PETSC_ERR_PLIB, "Calculated block dimension index = %d < %d = length dimension index.", ctx->bsInd, ctx->lenInd);
155: PetscCheck(ctx->bsInd <= ctx->rdim - 1, PetscObjectComm((PetscObject)viewer), PETSC_ERR_FILE_UNEXPECTED, "Calculated block dimension index = %d > %d = total number of dimensions - 1.", ctx->bsInd, ctx->rdim - 1);
156: PetscCheck(!ctx->complexVal || ctx->dims[ctx->complexInd] == 2, PETSC_COMM_SELF, PETSC_ERR_FILE_UNEXPECTED, "Complex numbers must have exactly 2 parts (%" PRIuHSIZE ")", ctx->dims[ctx->complexInd]);
158: if (hdf5->horizontal) {
159: /* support horizontal 1D arrays (MATLAB vectors) - swap meaning of blocks and entries */
160: int t = ctx->lenInd;
161: ctx->lenInd = ctx->bsInd;
162: ctx->bsInd = t;
163: }
165: /* Get block size */
166: ctx->dim2 = PETSC_FALSE;
167: if (ctx->lenInd == ctx->bsInd) {
168: bs = 1; /* support vectors stored as 1D array */
169: } else {
170: bs = (PetscInt)ctx->dims[ctx->bsInd];
171: if (bs == 1) ctx->dim2 = PETSC_TRUE; /* vector with blocksize of 1, still stored as 2D array */
172: }
174: layout:
175: /* Get global size */
176: PetscCall(PetscIntCast(bs * ctx->dims[ctx->lenInd], &N));
178: /* Set global size, blocksize and type if not yet set */
179: PetscCall(PetscLayoutSetBlockSize(map, bs));
180: if (map->N < 0) PetscCall(PetscLayoutSetSize(map, N));
181: else PetscCheck(map->N == N, PetscObjectComm((PetscObject)viewer), PETSC_ERR_FILE_UNEXPECTED, "Global size of array %s in file is %" PetscInt_FMT ", not %" PetscInt_FMT " as expected", ctx->name, N, map->N);
182: if (setup) PetscCall(PetscLayoutSetUp(map));
183: PetscFunctionReturn(PETSC_SUCCESS);
184: }
186: static PetscErrorCode PetscViewerHDF5ReadSelectHyperslab_Private(PetscViewer viewer, HDF5ReadCtx ctx, PetscLayout map, hid_t *memspace)
187: {
188: PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;
189: hsize_t *count, *offset;
190: PetscInt bs, n, low;
191: int i;
193: PetscFunctionBegin;
194: /* Compute local size and ownership range */
195: PetscCall(PetscLayoutSetUp(map));
196: PetscCall(PetscLayoutGetBlockSize(map, &bs));
197: PetscCall(PetscLayoutGetLocalSize(map, &n));
198: PetscCall(PetscLayoutGetRange(map, &low, NULL));
200: /* Each process defines a dataset and reads it from the hyperslab in the file */
201: PetscCall(PetscMalloc2(ctx->rdim, &count, ctx->rdim, &offset));
202: for (i = 0; i < ctx->rdim; i++) {
203: /* By default, select all entries with no offset */
204: offset[i] = 0;
205: count[i] = ctx->dims[i];
206: }
207: if (hdf5->timestepping) {
208: count[0] = 1;
209: offset[0] = hdf5->timestep;
210: }
211: {
212: PetscCall(PetscHDF5IntCast(n / bs, &count[ctx->lenInd]));
213: PetscCall(PetscHDF5IntCast(low / bs, &offset[ctx->lenInd]));
214: }
215: PetscCallHDF5Return(*memspace, H5Screate_simple, (ctx->rdim, count, NULL));
216: PetscCallHDF5(H5Sselect_hyperslab, (ctx->dataspace, H5S_SELECT_SET, offset, NULL, count, NULL));
217: PetscCall(PetscFree2(count, offset));
218: PetscFunctionReturn(PETSC_SUCCESS);
219: }
221: static PetscErrorCode PetscViewerHDF5ReadArray_Private(PetscViewer viewer, HDF5ReadCtx h, hid_t datatype, hid_t memspace, void *arr)
222: {
223: PetscViewer_HDF5 *hdf5 = (PetscViewer_HDF5 *)viewer->data;
225: PetscFunctionBegin;
226: PetscCallHDF5(H5Dread, (h->dataset, datatype, memspace, h->dataspace, hdf5->dxpl_id, arr));
227: PetscFunctionReturn(PETSC_SUCCESS);
228: }
230: static PetscErrorCode PetscViewerHDF5Load_Internal(PetscViewer viewer, const char name[], PetscBool uncompress, PetscLayout map, hid_t datatype, void **newarr)
231: {
232: PetscBool has;
233: const char *group;
234: HDF5ReadCtx h = NULL;
235: hid_t memspace = 0;
236: size_t unitsize;
237: void *arr;
239: PetscFunctionBegin;
240: PetscCall(PetscViewerHDF5GetGroup(viewer, NULL, &group));
241: PetscCall(PetscViewerHDF5HasDataset(viewer, name, &has));
242: PetscCheck(has, PetscObjectComm((PetscObject)viewer), PETSC_ERR_FILE_UNEXPECTED, "Object (dataset) \"%s\" not stored in group %s", name, group);
243: PetscCall(PetscViewerHDF5ReadInitialize_Private(viewer, name, &h));
244: #if PetscDefined(USE_COMPLEX)
245: if (!h->complexVal) {
246: H5T_class_t clazz = H5Tget_class(datatype);
247: PetscCheck(clazz != H5T_FLOAT, PetscObjectComm((PetscObject)viewer), PETSC_ERR_SUP, "Dataset %s/%s is marked as real but PETSc is configured for complex scalars. The conversion is not yet implemented. Configure with --with-scalar-type=real to read this dataset", group ? group : "", name);
248: }
249: #else
250: PetscCheck(!h->complexVal, PetscObjectComm((PetscObject)viewer), PETSC_ERR_SUP, "Dataset %s/%s is marked as complex but PETSc is configured for real scalars. Configure with --with-scalar-type=complex to read this dataset", group, name);
251: #endif
253: PetscCall(PetscViewerHDF5ReadSizes_Private(viewer, h, uncompress, PETSC_TRUE, &map));
254: PetscCall(PetscViewerHDF5ReadSelectHyperslab_Private(viewer, h, map, &memspace));
256: if (h->runs && uncompress) {
257: PetscInt *ind;
259: PetscCall(PetscInfo(viewer, "Read compressed object with name %s of size %" PetscInt_FMT ":%" PetscInt_FMT "\n", name, map->n, map->N));
260: // Each process stores the whole compression, so skip any leading parts
261: PetscCall(PetscMalloc1(map->n, &ind));
262: for (PetscInt i = 0, off = 0; i < h->runs; ++i) {
263: for (PetscInt j = 0, inc = 0; j < h->cind[i * 3 + 0]; ++j, ++off, inc += h->cind[i * 3 + 1]) {
264: if (off >= map->rend) {
265: i = h->runs;
266: break;
267: }
268: if (off >= map->rstart) ind[off - map->rstart] = h->cind[i * 3 + 2] + inc;
269: }
270: }
271: *newarr = ind;
272: goto cleanup;
273: }
275: unitsize = H5Tget_size(datatype);
276: if (h->complexVal) unitsize *= 2;
277: /* unitsize is size_t i.e. always unsigned, so the negative check is pointless? */
278: PetscCheck(unitsize > 0 && unitsize <= PetscMax(sizeof(PetscInt), sizeof(PetscScalar)), PETSC_COMM_SELF, PETSC_ERR_LIB, "Sanity check failed: HDF5 function H5Tget_size(datatype) returned suspicious value %zu", unitsize);
279: PetscCall(PetscMalloc(map->n * unitsize, &arr));
281: PetscCall(PetscViewerHDF5ReadArray_Private(viewer, h, datatype, memspace, arr));
282: *newarr = arr;
284: cleanup:
285: PetscCallHDF5(H5Sclose, (memspace));
286: PetscCall(PetscViewerHDF5ReadFinalize_Private(viewer, &h));
287: PetscCall(PetscFree(group));
288: PetscFunctionReturn(PETSC_SUCCESS);
289: }
291: /*@C
292: PetscViewerHDF5Load - Read a raw array from the `PETSCVIEWERHDF5` dataset in parallel
294: Collective; No Fortran Support
296: Input Parameters:
297: + viewer - The `PETSCVIEWERHDF5` viewer
298: . name - The dataset name
299: - datatype - The HDF5 datatype of the items in the dataset
301: Input/Output Parameter:
302: . map - The layout which specifies array partitioning, on output the
303: set up layout (with global size and blocksize according to dataset)
305: Output Parameter:
306: . newarr - The partitioned array, a memory image of the given dataset
308: Level: developer
310: Notes:
311: This is intended mainly for internal use; users should use higher level routines such as `ISLoad()`, `VecLoad()`, `DMLoad()`.
313: The array is partitioned according to the given `PetscLayout` which is converted to an HDF5 hyperslab.
315: This name is relative to the current group returned by `PetscViewerHDF5OpenGroup()`.
317: .seealso: `PetscViewer`, `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `PetscViewerHDF5PushGroup()`, `PetscViewerHDF5OpenGroup()`, `PetscViewerHDF5ReadSizes()`,
318: `VecLoad()`, `ISLoad()`, `PetscLayout`
319: @*/
320: PetscErrorCode PetscViewerHDF5Load(PetscViewer viewer, const char name[], PetscLayout map, hid_t datatype, void **newarr)
321: {
322: PetscFunctionBegin;
323: PetscCall(PetscViewerHDF5Load_Internal(viewer, name, PETSC_TRUE, map, datatype, newarr));
324: PetscFunctionReturn(PETSC_SUCCESS);
325: }
327: /*@
328: PetscViewerHDF5ReadSizes - Read block size and global size of a `Vec` or `IS` stored in an HDF5 file.
330: Input Parameters:
331: + viewer - The `PETSCVIEWERHDF5` viewer
332: - name - The dataset name
334: Output Parameters:
335: + bs - block size
336: - N - global size
338: Level: advanced
340: Notes:
341: The dataset is stored as an HDF5 dataspace with 1-4 dimensions in the order
342: 1) # timesteps (optional), 2) # blocks, 3) # elements per block (optional), 4) real and imaginary part (only for complex).
344: The dataset can be stored as a 2D dataspace even if its blocksize is 1; see `PetscViewerHDF5SetBaseDimension2()`.
346: .seealso: `PetscViewer`, `PETSCVIEWERHDF5`, `PetscViewerHDF5Open()`, `VecLoad()`, `ISLoad()`, `VecGetSize()`, `ISGetSize()`, `PetscViewerHDF5SetBaseDimension2()`
347: @*/
348: PetscErrorCode PetscViewerHDF5ReadSizes(PetscViewer viewer, const char name[], PetscInt *bs, PetscInt *N)
349: {
350: HDF5ReadCtx h = NULL;
351: PetscLayout map = NULL;
353: PetscFunctionBegin;
355: PetscCall(PetscViewerHDF5ReadInitialize_Private(viewer, name, &h));
356: PetscCall(PetscViewerHDF5ReadSizes_Private(viewer, h, PETSC_TRUE, PETSC_FALSE, &map));
357: PetscCall(PetscViewerHDF5ReadFinalize_Private(viewer, &h));
358: if (bs) *bs = map->bs;
359: if (N) *N = map->N;
360: PetscCall(PetscLayoutDestroy(&map));
361: PetscFunctionReturn(PETSC_SUCCESS);
362: }