Marray
marray-hdf5.hxx
1 // http://www.andres.sc/marray.html
2 //
3 #pragma once
4 #ifndef ANDRES_MARRAY_HDF5_HXX
5 #define ANDRES_MARRAY_HDF5_HXX
6 
7 #include "marray.hxx"
8 #include "hdf5.hxx"
9 
10 namespace andres {
11 namespace hdf5 {
12 
13 const std::string errorMessageLastMajorOrder =
14  "HDF5 up to at least version 1.8.16 does not support LastMajorOrder. "
15  "(Quote from the HDF5 1.8.16 manual: \"HDF5 uses C storage conventions, "
16  "assuming that the last listed dimension is the fastest-changing dimension "
17  "and the first-listed dimension is the slowest chang­ing.\") "
18  "The indexing order of the Marray or View at hand is LastMajorOrder. "
19  "In order to avoid confusion, such Marrays and Views are not saved directly to HDF5. "
20  "Consider copying to an Marray in FirstMajorOrder or a one-dimensional Marray";
21 
22 template<class T>
23  void save(const hid_t&, const std::string&, const Marray<T>&);
24 template<class T, bool isConst>
25  void save(const hid_t&, const std::string&, const View<T, isConst>&);
26 template<class T, class BaseIterator, class ShapeIterator>
27  void saveHyperslab(const hid_t&, const std::string&,
28  BaseIterator, BaseIterator, ShapeIterator, const Marray<T>&);
29 template<class T, class ShapeIterator>
30  void create(const hid_t&, const std::string&, ShapeIterator, ShapeIterator);
31 
32 template<class T>
33  void load(const hid_t&, const std::string&, Marray<T>&);
34 template<class T>
35  void load(const std::string&, const std::string&, Marray<T>&, HDF5Version = HDF5_VERSION_DEFAULT);
36 
37 template<class T>
38  void loadShape(const hid_t&, const std::string&, std::vector<T>&);
39 template<class T, class BaseIterator, class ShapeIterator>
40  void loadHyperslab(const hid_t&, const std::string&,
41  BaseIterator, BaseIterator, ShapeIterator, Marray<T>&);
42 
52 template<class T, class ShapeIterator>
53 void create(
54  const hid_t& groupHandle,
55  const std::string& datasetName,
56  ShapeIterator begin,
57  ShapeIterator end
58 ) {
59  marray_detail::Assert(MARRAY_NO_ARG_TEST || groupHandle >= 0);
60  HandleCheck<MARRAY_NO_DEBUG> handleCheck;
61 
62  // build dataspace
63  hid_t datatype = H5Tcopy(hdf5Type<T>());
64  std::size_t dimension = std::distance(begin, end);
65  std::vector<hsize_t> shape(dimension);
66  for(std::size_t j=0; j<dimension; ++j) {
67  shape[j] = hsize_t(*begin);
68  ++begin;
69  }
70  hid_t dataspace = H5Screate_simple(dimension, &shape[0], NULL);
71  if(dataspace < 0) {
72  H5Tclose(datatype);
73  throw std::runtime_error("Marray cannot create dataspace.");
74  }
75 
76  // create new dataset
77  hid_t dataset = H5Dcreate(groupHandle, datasetName.c_str(), datatype,
78  dataspace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
79  if(dataset < 0) {
80  H5Sclose(dataspace);
81  H5Tclose(datatype);
82  throw std::runtime_error("Marray cannot create dataset.");
83  }
84 
85  // clean up
86  H5Dclose(dataset);
87  H5Sclose(dataspace);
88  H5Tclose(datatype);
89  handleCheck.check();
90 }
91 
100 template<class T>
101 void save(
102  const hid_t& groupHandle,
103  const std::string& datasetName,
104  const Marray<T>& in
105 ) {
106  marray_detail::Assert(MARRAY_NO_ARG_TEST || groupHandle >= 0);
107 
108  if(in.coordinateOrder() == LastMajorOrder) {
109  throw std::runtime_error(errorMessageLastMajorOrder);
110  }
111 
112  HandleCheck<MARRAY_NO_DEBUG> handleCheck;
113 
114  // build dataspace
115  hid_t datatype = H5Tcopy(hdf5Type<T>());
116  std::vector<hsize_t> shape(in.dimension());
117  for(std::size_t j=0; j<in.dimension(); ++j) {
118  shape[j] = hsize_t(in.shape(j));
119  }
120  hid_t dataspace = H5Screate_simple(in.dimension(), &shape[0], NULL);
121  if(dataspace < 0) {
122  H5Tclose(datatype);
123  throw std::runtime_error("Marray cannot create dataspace.");
124  }
125 
126  // create new dataset
127  hid_t dataset = H5Dcreate(groupHandle, datasetName.c_str(), datatype,
128  dataspace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
129  if(dataset < 0) {
130  H5Sclose(dataspace);
131  H5Tclose(datatype);
132  throw std::runtime_error("Marray cannot create dataset.");
133  }
134 
135  // write
136  herr_t status = H5Dwrite(dataset, datatype, H5S_ALL, H5S_ALL, H5P_DEFAULT, &in(0));
137  H5Dclose(dataset);
138  H5Sclose(dataspace);
139  H5Tclose(datatype);
140  if(status < 0) {
141  throw std::runtime_error("Marray cannot write to dataset.");
142  }
143 
144  handleCheck.check();
145 }
146 
155 template<class T, bool isConst>
156 inline void save(
157  const hid_t& groupHandle,
158  const std::string& datasetName,
159  const View<T, isConst>& in
160 ) {
161  Marray<T> m = in;
162  save(groupHandle, datasetName, m);
163 }
164 
177 template<class T>
178 inline void
180  const std::string& filename,
181  const std::string& datasetName,
182  Marray<T>& out,
183  HDF5Version hdf5version
184 ) {
185  hid_t file = openFile(filename, READ_ONLY, hdf5version);
186  load(file, datasetName, out);
187  closeFile(file);
188 }
189 
200 template<class T>
201 void load(
202  const hid_t& groupHandle,
203  const std::string& datasetName,
204  Marray<T>& out
205 ) {
206  marray_detail::Assert(MARRAY_NO_ARG_TEST || groupHandle >= 0);
207 
208  HandleCheck<MARRAY_NO_DEBUG> handleCheck;
209 
210  hid_t dataset = H5Dopen(groupHandle, datasetName.c_str(), H5P_DEFAULT);
211  if(dataset < 0) {
212  throw std::runtime_error("Marray cannot open dataset.");
213  }
214  hid_t filespace = H5Dget_space(dataset);
215  hid_t type = H5Dget_type(dataset);
216  hid_t nativeType = H5Tget_native_type(type, H5T_DIR_DESCEND);
217  if(!H5Tequal(nativeType, hdf5Type<T>())) {
218  H5Dclose(dataset);
219  H5Tclose(nativeType);
220  H5Tclose(type);
221  H5Sclose(filespace);
222  throw std::runtime_error("Data types not equal error.");
223  }
224  int dimension = H5Sget_simple_extent_ndims(filespace);
225  std::vector<hsize_t> shape(dimension);
226  herr_t status = H5Sget_simple_extent_dims(filespace, &shape[0], NULL);
227  if(status < 0) {
228  H5Dclose(dataset);
229  H5Tclose(nativeType);
230  H5Tclose(type);
231  H5Sclose(filespace);
232  throw std::runtime_error("H5Sget_simple_extent_dims error.");
233  }
234  hid_t memspace = H5Screate_simple(dimension, &shape[0], NULL);
235 
236  // resize marray
237  std::vector<std::size_t> newShape((std::size_t)(dimension));
238  for(std::size_t j=0; j<newShape.size(); ++j) {
239  newShape[j] = (std::size_t)(shape[j]);
240  }
241  out = Marray<T>(SkipInitialization, newShape.begin(), newShape.end(), FirstMajorOrder);
242 
243  // read
244  status = H5Dread(dataset, nativeType, memspace, filespace, H5P_DEFAULT, &out(0));
245  H5Dclose(dataset);
246  H5Tclose(nativeType);
247  H5Tclose(type);
248  H5Sclose(memspace);
249  H5Sclose(filespace);
250  if(status < 0) {
251  throw std::runtime_error("Marray cannot read from dataset.");
252  }
253 
254  handleCheck.check();
255 }
256 
265 template<class T>
267  const hid_t& groupHandle,
268  const std::string& datasetName,
269  std::vector<T>& out
270 ) {
271  marray_detail::Assert(MARRAY_NO_ARG_TEST || groupHandle >= 0);
272  HandleCheck<MARRAY_NO_DEBUG> handleCheck;
273 
274  // load shape from HDF5 file
275  hid_t dataset = H5Dopen(groupHandle, datasetName.c_str(), H5P_DEFAULT);
276  if(dataset < 0) {
277  throw std::runtime_error("Marray cannot open dataset.");
278  }
279  hid_t filespace = H5Dget_space(dataset);
280  hsize_t dimension = H5Sget_simple_extent_ndims(filespace);
281  hsize_t* shape = new hsize_t[(std::size_t)(dimension)];
282  herr_t status = H5Sget_simple_extent_dims(filespace, shape, NULL);
283  if(status < 0) {
284  H5Dclose(dataset);
285  H5Sclose(filespace);
286  delete[] shape;
287  throw std::runtime_error("Marray cannot get extension of dataset.");
288  }
289 
290  // write shape to out
291  out = std::vector<T>(dimension);
292  for(std::size_t j=0; j<out.size(); ++j) {
293  out[j] = T(shape[j]);
294  }
295 
296  // clean up
297  delete[] shape;
298  H5Dclose(dataset);
299  H5Sclose(filespace);
300  handleCheck.check();
301 }
302 
316 template<class T, class BaseIterator, class ShapeIterator>
318  const hid_t& groupHandle,
319  const std::string& datasetName,
320  BaseIterator baseBegin,
321  BaseIterator baseEnd,
322  ShapeIterator shapeBegin,
323  Marray<T>& out
324 ) {
325  marray_detail::Assert(MARRAY_NO_ARG_TEST || groupHandle >= 0);
326  HandleCheck<MARRAY_NO_DEBUG> handleCheck;
327 
328  // open dataset
329  hid_t dataset = H5Dopen(groupHandle, datasetName.c_str(), H5P_DEFAULT);
330  if(dataset < 0) {
331  throw std::runtime_error("Marray cannot open dataset.");
332  }
333 
334  // determine shape of hyperslab and array
335  std::size_t size = std::distance(baseBegin, baseEnd);
336  std::vector<hsize_t> offset(size);
337  std::vector<hsize_t> slabShape(size);
338  std::vector<hsize_t> marrayShape(size);
339  const CoordinateOrder coordinateOrder = FirstMajorOrder;
340  for(std::size_t j=0; j<size; ++j) {
341  offset[j] = hsize_t(*baseBegin);
342  slabShape[j] = hsize_t(*shapeBegin);
343  marrayShape[j] = slabShape[j];
344  ++baseBegin;
345  ++shapeBegin;
346  }
347 
348  // select dataspace hyperslab
349  hid_t datatype = H5Dget_type(dataset);
350 
351  if(!H5Tequal(datatype, hdf5Type<T>())) {
352  throw std::runtime_error("data type of stored hdf5 dataset and passed array do not match in loadHyperslab");
353  }
354 
355  hid_t dataspace = H5Dget_space(dataset);
356  herr_t status = H5Sselect_hyperslab(dataspace, H5S_SELECT_SET, &offset[0], NULL, &slabShape[0], NULL);
357  if(status < 0) {
358  H5Tclose(datatype);
359  H5Sclose(dataspace);
360  H5Dclose(dataset);
361  throw std::runtime_error("Marray cannot select hyperslab. Check offset and shape!");
362  }
363 
364  // select memspace hyperslab
365  hid_t memspace = H5Screate_simple(int(size), &marrayShape[0], NULL);
366  std::vector<hsize_t> offsetOut(size, 0); // no offset
367  status = H5Sselect_hyperslab(memspace, H5S_SELECT_SET, &offsetOut[0], NULL, &marrayShape[0], NULL);
368  if(status < 0) {
369  H5Sclose(memspace);
370  H5Tclose(datatype);
371  H5Sclose(dataspace);
372  H5Dclose(dataset);
373  throw std::runtime_error("Marray cannot select hyperslab. Check offset and shape!");
374  }
375 
376  // read from dataspace into memspace
377  out = Marray<T>(SkipInitialization, &marrayShape[0], (&marrayShape[0])+size, coordinateOrder);
378  status = H5Dread(dataset, datatype, memspace, dataspace, H5P_DEFAULT, &(out(0)));
379 
380  // clean up
381  H5Sclose(memspace);
382  H5Tclose(datatype);
383  H5Sclose(dataspace);
384  H5Dclose(dataset);
385  if(status < 0) {
386  throw std::runtime_error("Marray cannot read from dataset.");
387  }
388  handleCheck.check();
389 }
390 
402 template<class T, class BaseIterator, class ShapeIterator>
403 void
405  const hid_t& groupHandle,
406  const std::string& datasetName,
407  BaseIterator baseBegin,
408  BaseIterator baseEnd,
409  ShapeIterator shapeBegin,
410  const Marray<T>& in
411 ) {
412  marray_detail::Assert(MARRAY_NO_ARG_TEST || groupHandle >= 0);
413 
414  if(in.coordinateOrder() == LastMajorOrder) {
415  throw std::runtime_error(errorMessageLastMajorOrder);
416  }
417 
418  HandleCheck<MARRAY_NO_DEBUG> handleCheck;
419 
420  // open dataset
421  hid_t dataset = H5Dopen(groupHandle, datasetName.c_str(), H5P_DEFAULT);
422  if(dataset < 0) {
423  throw std::runtime_error("Marray cannot open dataset.");
424  }
425 
426  // determine hyperslab shape
427  std::vector<hsize_t> memoryShape(in.dimension());
428  for(std::size_t j=0; j<in.dimension(); ++j) {
429  memoryShape[j] = in.shape(j);
430  }
431  std::size_t size = std::distance(baseBegin, baseEnd);
432  std::vector<hsize_t> offset(size);
433  std::vector<hsize_t> slabShape(size);
434  for(std::size_t j=0; j<size; ++j) {
435  offset[j] = hsize_t(*baseBegin);
436  slabShape[j] = hsize_t(*shapeBegin);
437  ++baseBegin;
438  ++shapeBegin;
439  }
440 
441  // select dataspace hyperslab
442  hid_t datatype = H5Dget_type(dataset);
443  hid_t dataspace = H5Dget_space(dataset);
444  herr_t status = H5Sselect_hyperslab(dataspace, H5S_SELECT_SET, &offset[0], NULL, &slabShape[0], NULL);
445  if(status < 0) {
446  H5Tclose(datatype);
447  H5Sclose(dataspace);
448  H5Dclose(dataset);
449  throw std::runtime_error("Marray cannot select hyperslab. Check offset and shape!");
450  }
451 
452  // select memspace hyperslab
453  hid_t memspace = H5Screate_simple(int(in.dimension()), &memoryShape[0], NULL);
454  std::vector<hsize_t> memoryOffset(int(in.dimension()), 0); // no offset
455  status = H5Sselect_hyperslab(memspace, H5S_SELECT_SET, &memoryOffset[0], NULL, &memoryShape[0], NULL);
456  if(status < 0) {
457  H5Sclose(memspace);
458  H5Tclose(datatype);
459  H5Sclose(dataspace);
460  H5Dclose(dataset);
461  throw std::runtime_error("Marray cannot select hyperslab. Check offset and shape!");
462  }
463 
464  // write from memspace to dataspace
465  status = H5Dwrite(dataset, datatype, memspace, dataspace, H5P_DEFAULT, &(in(0)));
466 
467  // clean up
468  H5Sclose(memspace);
469  H5Tclose(datatype);
470  H5Sclose(dataspace);
471  H5Dclose(dataset);
472  if(status < 0) {
473  throw std::runtime_error("Marray cannot write to dataset.");
474  }
475  handleCheck.check();
476 }
477 
478 } // namespace hdf5
479 } // namespace andres
480 
481 #endif