Medusa  1.1
Coordinate Free Mehless Method implementation
HDF.cpp
Go to the documentation of this file.
1 #include <medusa/bits/io/HDF.hpp>
3 
4 
10 namespace mm {
11 
12 std::string HDF::str(HDF::Mode mode) {
13  switch (mode) {
14  case APPEND: return "APPEND";
15  case DESTROY: return "DESTROY";
16  case READONLY: return "READONLY";
17  }
18  return "IMPOSSIBLE";
19 }
20 
21 hid_t HDF::openFileHelper(const std::string& filename, HDF::Mode mode) {
22  hid_t file = -1;
23  assert_msg(!filename.empty(), "Filename is empty.");
24  std::ifstream test_access(filename);
25  switch (mode) {
26  case APPEND:
27  if (test_access.good()) { // exists
28  file = H5Fopen(filename.c_str(), H5F_ACC_RDWR, H5P_DEFAULT);
29  } else {
30  file = H5Fcreate(filename.c_str(), H5F_ACC_EXCL, H5P_DEFAULT, H5P_DEFAULT);
31  }
32  break;
33  case DESTROY:
34  file = H5Fcreate(filename.c_str(), H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
35  break;
36  case READONLY:
37  assert_msg(test_access.good(), "To use readonly access, file '%s' must exist "
38  "and be accessible.", filename);
39  file = H5Fopen(filename.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);
40  break;
41  }
42  assert_msg(file >= 0, "Opening file %s with mode %s failed with status %d. Are you sure "
43  "all potential intermediate directories exist?",
44  filename, str(mode), file);
45  return file;
46 }
47 
48 void HDF::openGroup(std::string group_name) {
49  assert_msg(H5Iis_valid(file), "File id %d invalid. Did you open a file before creating "
50  "a group?", file);
51  closeGroup();
52  assert_msg(!group_name.empty(), "Group name must not be empty.");
53  assert_msg(group_name.size() == 1 || group_name.back() != '/',
54  "Group name must not end with a '/' unless it's root, got '%s'.", group_name);
55 
56  if (group_name[0] == '/') {
57  group_name_ = "";
58  } else {
59  group_name = "/" + group_name;
60  if (group_name_ == "/") group_name_ = "";
61  }
62  std::string::size_type idx = 0;
63  do {
64  auto new_idx = group_name.find('/', idx + 1);
65  assert_msg(new_idx - idx > 1, "Two consecutive '/' not allowed in group name '%s'.",
66  group_name);
67  if (new_idx == std::string::npos) { new_idx = group_name.size(); }
68  group_name_ += group_name.substr(idx, new_idx - idx);
69  idx = new_idx;
70 
71  // Root group treated separately to ensure compatibility with 1.8 version.
72  // See: https://support.hdfgroup.org/HDF5/doc/RM/RM_H5L.html#Link-Exists
73  if (group_name_ != "/" && !H5Lexists(file, group_name_.c_str(), H5P_DEFAULT)) {
74  group = H5Gcreate(file, group_name_.c_str(), H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
75  assert_msg(group >= 0, "Failed creating group '%s' in file '%s' with status '%d'.",
77  H5Gclose(group);
78  }
79  } while (idx != group_name.size());
80  group = H5Gopen(file, group_name_.c_str(), H5P_DEFAULT);
81  assert_msg(group >= 0, "Failed opening group '%s' in file '%s' with status '%d'.",
83 }
84 
86 template <>
87 std::string HDF::readAttribute<std::string>(const std::string& attr_name) const {
88  assert_msg(H5Iis_valid(group), "Group id %d invalid. Did you open a group before reading?",
89  group);
90  hid_t attr = H5Aopen(group, attr_name.c_str(), H5P_DEFAULT);
91  assert_msg(attr >= 0, "Attribute '%s' could not be accessed in group '%s' in file '%s'.",
92  attr_name, group_name_, filename_);
93 
94  hid_t type = H5Aget_type(attr);
95  assert_msg(type >= 0, "Failed getting type of attribute '%s' in group '%s' in file '%s'.",
96  attr_name, group_name_, filename_);
97 
98  H5T_str_t pad = H5Tget_strpad(type);
99  assert_msg(pad >= 0, "Attribute '%s' in group '%s' in file '%s' is not a string attribute.",
100  attr_name, group_name_, filename_);
101  std::size_t size = H5Tget_size(type);
102  std::vector<char> str;
103  str.reserve(size);
104  herr_t status = H5Aread(attr, type, str.data());
105  assert_msg(status >= 0, "Failed reading attribute '%s' from group '%s' in file '%s'.",
106  attr_name, groupName(), filename_);
107 
108  H5Tclose(type);
109  H5Aclose(attr);
110  return std::string(str.data(), size);
111 }
112 
113 std::string HDF::readNullTerminatedStringAttribute(const std::string& attr_name) const {
114  std::string s = readStringAttribute(attr_name);
115  return std::string(s.c_str());
116 }
117 
118 int HDF::readIntAttribute(const std::string& attr_name) const {
119  return readAttribute<int>(attr_name);
120 }
121 
122 bool HDF::readBoolAttribute(const std::string& attr_name) const {
123  return readAttribute<bool>(attr_name);
124 }
125 
126 double HDF::readDoubleAttribute(const std::string& attr_name) const {
127  return readAttribute<double>(attr_name);
128 }
129 
130 float HDF::readFloatAttribute(const std::string& attr_name) const {
131  return readAttribute<float>(attr_name);
132 }
133 
134 std::string HDF::readStringAttribute(const std::string& attr_name) const {
135  return readAttribute<std::string>(attr_name);
136 }
137 
138 void HDF::writeIntAttribute(const std::string& attr_name, int value, bool overwrite) const {
139  writeAttribute(attr_name, value, H5T_NATIVE_INT, overwrite);
140 }
141 
142 void HDF::writeBoolAttribute(const std::string& attr_name, bool value, bool overwrite) const {
143  writeAttribute(attr_name, static_cast<hbool_t>(value), H5T_NATIVE_HBOOL, overwrite);
144 }
145 
146 void HDF::writeDoubleAttribute(const std::string& attr_name, double value, bool overwrite) const {
147  writeAttribute(attr_name, value, H5T_NATIVE_DOUBLE, overwrite);
148 }
149 
150 void HDF::writeFloatAttribute(const std::string& attr_name, float value, bool overwrite) const {
151  writeAttribute(attr_name, value, H5T_NATIVE_FLOAT, overwrite);
152 }
153 
154 void HDF::writeStringAttribute(const std::string& attr_name, const std::string& value,
155  bool overwrite) const {
156  assert_msg(H5Iis_valid(group), "Group id %d invalid. Did you open a group before writing?",
157  group);
158  assert_msg(!value.empty(), "Storing empty strings is not supported in HDF5.");
159  if (H5Aexists(group, attr_name.c_str())) {
160  if (!overwrite) {
161  assert_msg(false, "Attribute '%s' in group '%s' in file '%s' already exists. To "
162  "overwrite its contents use parameter overwrite=true.",
163  attr_name, group_name_, filename_);
164  }
165  herr_t status = H5Adelete(group, attr_name.c_str());
166  assert_msg(status >= 0, "Failed deleting existing attribute '%s' in group '%s' in "
167  "file '%s' before writing a new one.",
168  attr_name, group_name_, filename_);
169  }
170 
171  hid_t space = H5Screate(H5S_SCALAR);
172  hid_t type = H5Tcopy(H5T_C_S1);
173  herr_t err = H5Tset_size(type, value.size());
174  assert_msg(err >= 0, "Set size failed.");
175  hid_t attr = H5Acreate2(group, attr_name.c_str(), type, space, H5P_DEFAULT, H5P_DEFAULT);
176  assert_msg(attr >= 0, "Failed creating attribute '%s' in group '%s' in file '%s'.",
177  attr_name, group_name_, filename_);
178 
179  herr_t status = H5Awrite(attr, type, value.data());
180  assert_msg(status >= 0, "Failed writing attribute '%s' to group '%s' in file '%s'.",
181  attr_name, group_name_, filename_);
182  err = H5Tclose(type);
183  assert_msg(err >= 0, "Type closing failed.");
184  err = H5Sclose(space);
185  assert_msg(err >= 0, "Space closing failed.");
186  err = H5Aclose(attr);
187  assert_msg(err >= 0, "Attribute closing failed.");
188 }
189 
190 std::vector<int> HDF::readIntArray(const std::string& dataset_name) const {
191  return read1DArray<int>(dataset_name);
192 }
193 
194 std::vector<double> HDF::readDoubleArray(const std::string& dataset_name) const {
195  return read1DArray<double>(dataset_name);
196 }
197 
198 std::vector<float> HDF::readFloatArray(const std::string& dataset_name) const {
199  return read1DArray<float>(dataset_name);
200 }
201 
202 std::vector<std::vector<int>> HDF::readInt2DArray(const std::string& dataset_name) const {
203  return read2DArray<int>(dataset_name);
204 }
205 
206 std::vector<std::vector<double>> HDF::readDouble2DArray(const std::string& dataset_name) const {
207  return read2DArray<double>(dataset_name);
208 }
209 
210 std::vector<std::vector<float>> HDF::readFloat2DArray(const std::string& dataset_name) const {
211  return read2DArray<float>(dataset_name);
212 }
213 
214 std::vector<std::vector<std::vector<int>>>
215 HDF::readInt3DArray(const std::string& dataset_name) const {
216  return read3DArray<int>(dataset_name);
217 }
218 
219 std::vector<std::vector<std::vector<double>>>
220 HDF::readDouble3DArray(const std::string& dataset_name) const {
221  return read3DArray<double>(dataset_name);
222 }
223 
224 std::vector<std::vector<std::vector<float>>>
225 HDF::readFloat3DArray(const std::string& dataset_name) const {
226  return read3DArray<float>(dataset_name);
227 }
228 
229 void HDF::flush() const {
230  if (!H5Iis_valid(file)) return;
231  H5Fflush(file, H5F_SCOPE_GLOBAL);
232  herr_t status = H5Fclose(file);
233  assert_msg(status >= 0, "Flushing file '%s' failed with status %d.", filename_, status);
234 }
235 
236 void HDF::closeGroup() const {
237  if (!H5Iis_valid(group)) return;
238  herr_t status = H5Gclose(group);
239  assert_msg(status >= 0, "Closing group '%s' in file '%s' failed with status %d.",
240  group_name_, filename_, status);
241 }
242 
243 void HDF::closeFile() const {
244  closeGroup();
245  if (!H5Iis_valid(file)) return;
246  herr_t status = H5Fclose(file);
247  assert_msg(status >= 0, "Closing file '%s' failed with status %d.", filename_, status);
248 }
249 
250 HDF HDF::atomic() const {
251  assert_msg(!filename_.empty(), "Filename should not be empty.");
252  assert_msg(!group_name_.empty(), "Group name should not be empty.");
253  assert_msg(!H5Iis_valid(group), "Group '%s' already opened, using atomic makes no sense.",
254  group_name_);
255  assert_msg(!H5Iis_valid(file), "File '%s' already opened, using atomic makes no sense.",
256  filename_);
257  HDF hdf(filename_, APPEND);
258  hdf.openGroup(group_name_);
259  return hdf; // file is closed in destructor
260 }
261 
262 void HDF::setGroupName(const std::string& group_name) {
263  closeGroup();
264  assert_msg(!group_name.empty(), "Group name must not be empty");
265  assert_msg(group_name.find("//") == std::string::npos, "Group name '%s' should not contain "
266  "two consecutive '/'.", group_name);
267  if (group_name[0] == '/') {
268  group_name_ = group_name;
269  } else {
270  group_name_ = '/' + group_name;
271  }
272  if (group_name.length() >= 2 && group_name.back() == '/') {
273  group_name_.pop_back();
274  }
275 }
276 
277 herr_t HDF::memberIterateCallback(hid_t loc_id, const char* name, const H5L_info_t*, void* data) {
278  auto* results = static_cast<Members*>(data);
279  H5O_info_t infobuf;
280  herr_t status = H5Oget_info_by_name(loc_id, name, &infobuf, H5P_DEFAULT);
281  assert_msg(status >= 0, "Could not get type of object '%s'.", name);
282  switch (infobuf.type) {
283  case H5O_TYPE_GROUP:
284  results->groups.emplace_back(name);
285  break;
286  case H5O_TYPE_DATASET:
287  results->datasets.emplace_back(name);
288  break;
289  case H5O_TYPE_NAMED_DATATYPE:
290  results->datatypes.emplace_back(name);
291  break;
292  default:
293  results->unknowns.emplace_back(name);
294  }
295  return 0;
296 }
297 
299  assert_msg(H5Iis_valid(group), "Group id %d invalid. Did you open a group before "
300  "iterating over it?", group);
301  Members result;
302  herr_t status = H5Literate(group, H5_INDEX_NAME, H5_ITER_INC, nullptr,
303  memberIterateCallback, static_cast<void*>(&result));
304  assert_msg(status >= 0, "Error during iteration through group '%s'.", group_name_);
305  return result;
306 }
307 
308 } // namespace mm
mm::HDF::readIntArray
std::vector< int > readIntArray(const std::string &dataset_name) const
Read given dataset into vector of ints.
Definition: HDF.cpp:190
mm
Root namespace for the whole library.
Definition: Gaussian.hpp:14
mm::HDF::str
static std::string str(Mode mode)
Convert HDF::Mode enum to string.
Definition: HDF.cpp:12
mm::HDF::readDouble3DArray
std::vector< std::vector< std::vector< double > > > readDouble3DArray(const std::string &dataset_name) const
Read given dataset into 3D vector of doubles.
Definition: HDF.cpp:220
mm::HDF::readFloatAttribute
float readFloatAttribute(const std::string &attr_name) const
Reads float attribute.
Definition: HDF.cpp:130
mm::HDF::readDoubleArray
std::vector< double > readDoubleArray(const std::string &dataset_name) const
Read given dataset into vector of doubles.
Definition: HDF.cpp:194
mm::HDF::memberIterateCallback
static herr_t memberIterateCallback(hid_t loc_id, const char *name, const H5L_info_t *, void *data)
Callback required by HDF Literate function when getting a list of members.
Definition: HDF.cpp:277
mm::HDF::readFloat3DArray
std::vector< std::vector< std::vector< float > > > readFloat3DArray(const std::string &dataset_name) const
Read given dataset into 3D vector of floats.
Definition: HDF.cpp:225
mm::HDF::closeFile
void closeFile() const
Closes current file and all objects associated with it (e.g. the group), if open.
Definition: HDF.cpp:243
mm::HDF::readFloat2DArray
std::vector< std::vector< float > > readFloat2DArray(const std::string &dataset_name) const
Read given dataset into 2D vector of floats.
Definition: HDF.cpp:210
mm::HDF::readDouble2DArray
std::vector< std::vector< double > > readDouble2DArray(const std::string &dataset_name) const
Read given dataset into 2D vector of doubles.
Definition: HDF.cpp:206
mm::HDF::writeDoubleAttribute
void writeDoubleAttribute(const std::string &attr_name, double value, bool overwrite=false) const
Write double attribute.
Definition: HDF.cpp:146
mm::HDF::openFileHelper
static hid_t openFileHelper(const std::string &filename, Mode mode)
Opens given file with given mode.
Definition: HDF.cpp:21
mm::sh::str
std::string str(shape_flags f)
Convert shape flags to a string representation.
Definition: shape_flags.hpp:32
mm::HDF::group
hid_t group
Currently open group identifier.
Definition: HDF_fwd.hpp:80
mm::HDF::setGroupName
void setGroupName(const std::string &group_name)
Set new group name without opening the new group. Any previously opened group is closed.
Definition: HDF.cpp:262
assert_msg
#define assert_msg(cond,...)
Assert with better error reporting.
Definition: assert.hpp:75
mm::HDF::READONLY
@ READONLY
Read only open, the file must exist.
Definition: HDF_fwd.hpp:87
mm::HDF
Simplified HDF5 I/O utilities.
Definition: HDF_fwd.hpp:75
HDF.hpp
mm::HDF::group_name_
std::string group_name_
Current group name.
Definition: HDF_fwd.hpp:77
mm::HDF::writeStringAttribute
void writeStringAttribute(const std::string &attr_name, const std::string &value, bool overwrite=false) const
Write string attribute.
Definition: HDF.cpp:154
mm::HDF::readInt3DArray
std::vector< std::vector< std::vector< int > > > readInt3DArray(const std::string &dataset_name) const
Read given dataset into 3D vector of ints.
Definition: HDF.cpp:215
HDF_fwd.hpp
mm::HDF::readInt2DArray
std::vector< std::vector< int > > readInt2DArray(const std::string &dataset_name) const
Read given dataset into 2D vector of ints.
Definition: HDF.cpp:202
mm::HDF::readBoolAttribute
bool readBoolAttribute(const std::string &attr_name) const
Reads bool attribute.
Definition: HDF.cpp:122
mm::HDF::Mode
Mode
Possible file opening modes.
Definition: HDF_fwd.hpp:84
mm::HDF::readStringAttribute
std::string readStringAttribute(const std::string &attr_name) const
Reads non-null-terminated string attribute.
Definition: HDF.cpp:134
mm::HDF::filename_
std::string filename_
Current file name.
Definition: HDF_fwd.hpp:76
mm::HDF::readDoubleAttribute
double readDoubleAttribute(const std::string &attr_name) const
Reads double attribute.
Definition: HDF.cpp:126
mm::HDF::filename
const std::string & filename() const
Get current filename.
Definition: HDF_fwd.hpp:166
mm::HDF::members
Members members() const
Returns an object with categorized names of all members of current group.
Definition: HDF.cpp:298
mm::HDF::writeIntAttribute
void writeIntAttribute(const std::string &attr_name, int value, bool overwrite=false) const
Write int attribute.
Definition: HDF.cpp:138
mm::HDF::closeGroup
void closeGroup() const
Closes current group, if open.
Definition: HDF.cpp:236
mm::HDF::flush
void flush() const
Flush opened file, if it is open.
Definition: HDF.cpp:229
mm::HDF::DESTROY
@ DESTROY
Removes old contents, if any exist.
Definition: HDF_fwd.hpp:86
mm::HDF::readIntAttribute
int readIntAttribute(const std::string &attr_name) const
Reads int attribute.
Definition: HDF.cpp:118
mm::HDF::writeAttribute
void writeAttribute(const std::string &attr_name, const T &value, const hid_t &type, bool overwrite=false) const
Writes given attribute with given value and type to file.
Definition: HDF.hpp:38
mm::pad
container_t pad(container_t container, T value)
Pads a ragged array with given value.
Definition: stdtypesutils.hpp:78
mm::HDF::writeFloatAttribute
void writeFloatAttribute(const std::string &attr_name, float value, bool overwrite=false) const
Write float attribute.
Definition: HDF.cpp:150
mm::HDF::APPEND
@ APPEND
Appends to the existing contents, if any exist.
Definition: HDF_fwd.hpp:85
mm::HDF::openGroup
void openGroup(std::string group_name)
Open given group, closing any previously opened group().
Definition: HDF.cpp:48
mm::HDF::file
hid_t file
Currently open file identifier.
Definition: HDF_fwd.hpp:79
mm::HDF::readNullTerminatedStringAttribute
std::string readNullTerminatedStringAttribute(const std::string &attr_name) const
Reads null-terminated string attribute.
Definition: HDF.cpp:113
mm::HDF::readFloatArray
std::vector< float > readFloatArray(const std::string &dataset_name) const
Read given dataset into vector of floats.
Definition: HDF.cpp:198
mm::HDF::writeBoolAttribute
void writeBoolAttribute(const std::string &attr_name, bool value, bool overwrite=false) const
Write bool attribute.
Definition: HDF.cpp:142
mm::HDF::Members
Holds categorized names for all members of a group.
Definition: HDF_fwd.hpp:534
mm::HDF::atomic
HDF atomic() const
Allows for "atomic" read and write operations to HDF5 files.
Definition: HDF.cpp:250