icedb  version 0.5.1
Snow particle scattering database API
shapeIOpsu.cpp
Go to the documentation of this file.
1 #include <icedb/defs.h>
2 #include <icedb/error.hpp>
3 #include <iostream>
4 #include <hdf5.h>
5 #include <hdf5_hl.h>
6 #include <boost/filesystem.hpp>
7 #include <memory>
8 #include <map>
9 #include <set>
10 #include <string>
11 #include <vector>
12 #include "shapeIOtext.hpp"
13 
14 namespace icedb {
15  namespace Examples {
16  namespace Shapes {
18  namespace ScopedHandles {
20  static inline bool isInvalid(hid_t h) {
21  if (h < 0) return true;
22  return false;
23  }
24  };
25  struct CloseHDF5File {
26  static inline void Close(hid_t h) {
27  herr_t err = H5Fclose(h);
29  .add("Reason", "Cannot close an open HDF5 file.");
30  }
31  };
33  static inline void Close(hid_t h) {
34  herr_t err = H5Dclose(h);
36  .add("Reason", "Cannot close an HDF5 dataset.");
37  }
38  };
40  static inline void Close(hid_t h) {
41  herr_t err = H5Sclose(h);
43  .add("Reason", "Cannot close an HDF5 dataspace.");
44  }
45  };
47  static inline void Close(hid_t h) {
48  herr_t err = H5Tclose(h);
50  .add("Reason", "Cannot close an HDF5 datatype.");
51  }
52  };
53 
56  template <typename HandleType, class InvalidValueClass, class CloseMethod>
57  struct ScopedHandle
58  {
59  HandleType h = 0;
60  bool valid() const { return !InvalidValueClass::isInvalid(h); }
62  if (valid()) CloseMethod::Close(h);
63  h = 0;
64  }
65  ScopedHandle(HandleType newh) : h(newh) {}
66  };
67 
72 
73  }
74 
75  typedef hid_t MatchType_t;
76  template <class DataType>
77  MatchType_t MatchType()
78  {
80  .add("Reason", "Unsupported type during conversion.");
81  }
82  template<> MatchType_t MatchType<int32_t>() {
83  return H5T_NATIVE_INT32;
84  }
85  template<> MatchType_t MatchType<float>() {
86  return H5T_NATIVE_FLOAT;
87  }
88 
89  template <typename T>
90  void readDataset(hid_t file_id, const char* dataset_name, std::vector<T> &outdata)
91  {
92  try {
93  ScopedHandles::H5D_handle hData(H5Dopen(file_id, dataset_name, H5P_DEFAULT));
95  // Get dimensionality
96  ScopedHandles::H5DS_handle hSpace(H5Dget_space(hData.h));
98  int rank = H5Sget_simple_extent_ndims(hSpace.h);
99  if (rank == 0 || rank > 2) ICEDB_throw(icedb::error::error_types::xBadInput);
100  hssize_t numElems = H5Sget_simple_extent_npoints(hSpace.h);
101 
102  // Verify that the dataset has the expected type
103  ScopedHandles::H5T_handle hDatatype(H5Dget_type(hData.h));
104  MatchType_t funcDataType = MatchType<T>();
105  if (H5Tequal(funcDataType, hDatatype.h) <= 0) ICEDB_throw(icedb::error::error_types::xBadInput);
106  // If this conversion fails, then either we are trying to open a dataset with
107  // the wrong data type (i.e. float vs int32 vs int16), or the architecture
108  // between the systems used to store and read the data are _very_ different,
109  // which is highly unlikely.
110 
111  // Resize output vector
112  outdata.resize(numElems);
113 
114  // Read the data
115  if (H5Dread(
116  hData.h, // dataset
117  hDatatype.h, // in-memory data type
118  H5S_ALL, // memory data space; reading the entire dataset
119  H5S_ALL, // file data space; reading the entire dataset
120  H5P_DEFAULT, // default transfer properties
121  (void*)outdata.data()) // the output buffer (pre-sized)
123 
124  }
125  catch (icedb::error::xError &err) {
126  err
127  .add("Reason", "Cannot open an HDF5 dataspace.")
128  .add("Dataset", dataset_name);
129  throw err; // rethrow
130  }
131  }
132 
137  ShapeDataBasic readPSUfile(const std::string &filename)
138  {
139  using namespace std;
140  ShapeDataBasic shpdata;
141  // The particle index is not specific enough. Let's use the filename for an id.
142  boost::filesystem::path p(filename);
143  auto pfile = p.filename();
144 #if BOOST_VERSION < 104600
145  string id = pfile.c_str(); // Needed for older RHEL machines
146 #else
147  string id = pfile.string().c_str(); // Totally assuming a lack of non-Latin characters in the path.
148 #endif
149 
150  ScopedHandles::H5F_handle hFile(H5Fopen(filename.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT));
152  .add("Reason", "Cannot open a netCDF/HDF5 file.")
153  .add("Filename", filename);
154 
155  // A valid PSU file has these tables: particle_index, sphere_index, r, x, y, z.
156  // particle_index has one row, one column.
157  // The rest have one column, and a number of rows that correspond to the number of spheres used to represent the particle.
158 
159  auto verifyDatasetExists = [](hid_t file_id, const char* dataset_name) -> bool {
160  if ((H5Lexists(file_id, dataset_name, H5P_DEFAULT) <= 0)) return false;
161  H5O_info_t objinfo;
162  if ((H5Oget_info_by_name(file_id, dataset_name, &objinfo, H5P_DEFAULT) < 0)) return false;
163  if (objinfo.type != H5O_TYPE_DATASET) return false;
164  return true;
165  };
166 
167  if (!verifyDatasetExists(hFile.h, "/particle_index")
168  || !verifyDatasetExists(hFile.h, "/sphere_index")
169  || !verifyDatasetExists(hFile.h, "/r")
170  || !verifyDatasetExists(hFile.h, "/x")
171  || !verifyDatasetExists(hFile.h, "/y")
172  || !verifyDatasetExists(hFile.h, "/z"))
174  .add("Reason", "This file does not have the proper structure for a Penn State geometry file.")
175  .add("Filename", filename);
176 
177  // Open all of the datasets. Make sure that they have the correct dimensionality.
178  // Read the data into vectors. Verify that the data have the appropriate sizes.
179  vector<float> xs, ys, zs, rs;
180  vector<int32_t> sphere_indices;
181 
182  // No need to read particle_index. Not being used.
183  readDataset<int32_t>(hFile.h, "/sphere_index", sphere_indices);
184  readDataset<float>(hFile.h, "/r", rs);
185  readDataset(hFile.h, "/x", xs);
186  readDataset(hFile.h, "/y", ys);
187  readDataset(hFile.h, "/z", zs);
188 
189  // Check that the read arrays have matching sizes.
190  const size_t numPoints = rs.size();
191  if (numPoints != xs.size()) ICEDB_throw(icedb::error::error_types::xAssert);
192  if (numPoints != ys.size()) ICEDB_throw(icedb::error::error_types::xAssert);
193  if (numPoints != zs.size()) ICEDB_throw(icedb::error::error_types::xAssert);
194  if (numPoints != sphere_indices.size()) ICEDB_throw(icedb::error::error_types::xAssert);
195 
196  // Finally, pack the data in the shpdata structure.
197  shpdata.required.number_of_particle_scattering_elements = static_cast<uint64_t>(numPoints);
199  shpdata.required.particle_id = filename; // TODO: improve this
201 
205  shpdata.required.particle_scattering_element_coordinates.resize(3 * numPoints);
206  for (size_t i = 0; i < numPoints; ++i) {
207  shpdata.required.particle_scattering_element_coordinates[(3 * i) + 0] = xs[i];
208  shpdata.required.particle_scattering_element_coordinates[(3 * i) + 1] = ys[i];
209  shpdata.required.particle_scattering_element_coordinates[(3 * i) + 2] = zs[i];
210  }
211 
213  shpdata.optional.particle_scattering_element_spacing = 0.001f; // 1 mm
215 
216 
217  return shpdata;
218  }
219  }
220  }
221 }
MatchType_t MatchType< float >()
Definition: shapeIOpsu.cpp:85
MatchType_t MatchType()
Definition: shapeIOpsu.cpp:77
ScopedHandle< hid_t, InvalidHDF5Handle, CloseHDF5Datatype > H5T_handle
Definition: shapeIOpsu.cpp:71
void readDataset(hid_t file_id, const char *dataset_name, std::vector< T > &outdata)
Definition: shapeIOpsu.cpp:90
uint8_t particle_scattering_element_coordinates_are_integral
Definition: shape.hpp:21
STL namespace.
MatchType_t MatchType< int32_t >()
Definition: shapeIOpsu.cpp:82
#define ICEDB_throw(x)
Definition: error.hpp:88
xError & add(const std::string &key, const T value)
Definition: error.cpp:271
ScopedHandle< hid_t, InvalidHDF5Handle, CloseHDF5Dataset > H5D_handle
Definition: shapeIOpsu.cpp:69
ScopedHandle< hid_t, InvalidHDF5Handle, CloseHDF5File > H5F_handle
Definition: shapeIOpsu.cpp:68
ShapeDataBasic readPSUfile(const std::string &filename)
Reads a Penn State-style geometry file.
Definition: shapeIOpsu.cpp:137
ShapeCommonOptionalData optional
Definition: shape.hpp:44
ScopedHandle< hid_t, InvalidHDF5Handle, CloseHDF5Dataspace > H5DS_handle
Definition: shapeIOpsu.cpp:70