Mantid
Loading...
Searching...
No Matches
NexusFileReader.h
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
7#pragma once
8
9#include "MantidNexusGeometry/H5ForwardCompatibility.h"
10#include "MantidNexusGeometry/NexusGeometryDefinitions.h"
11
12#include <Eigen/Dense>
13#include <H5Cpp.h>
14#include <boost/filesystem.hpp>
15#include <string>
16#include <vector>
17
18/*
19 * NexusFileReader: Test utility for unit testing in
20 * NexusGeometrySave::saveInstrument.
21 *
22 * @author Takudzwa Makoni, RAL (UKRI), ISIS
23 * @date 06/08/2019
24 */
25
26namespace Mantid {
27namespace NexusGeometry {
28using FullNXPath = std::vector<std::string>;
29// get Nexus file path as string. Used in Nexus Geometry unit tests.
30std::string toNXPathString(FullNXPath &path) {
31 std::string pathString = "";
32 for (const std::string &grp : path) {
33 pathString += "/" + grp;
34 }
35 return pathString;
36}
37
38// ported from NexusGeometryParser, for validating storage type of dataset
39// before reading its contents into a container
40template <typename ExpectedT> void validateStorageType(const H5::DataSet &data) {
41
42 const auto typeClass = data.getTypeClass();
43 const size_t sizeOfType = data.getDataType().getSize();
44 // Early check to prevent reinterpretation of underlying data.
45 if (std::is_floating_point<ExpectedT>::value) {
46 if (H5T_FLOAT != typeClass) {
47 throw std::runtime_error("Storage type mismatch. Expecting to extract a "
48 "floating point number");
49 }
50 if (sizeOfType != sizeof(ExpectedT)) {
51 throw std::runtime_error("Storage type mismatch for floats. This operation "
52 "is dangerous. Nexus stored has byte size:" +
53 std::to_string(sizeOfType));
54 }
55 } else if (std::is_integral<ExpectedT>::value) {
56 if (H5T_INTEGER != typeClass) {
57 throw std::runtime_error("Storage type mismatch. Expecting to extract a integer");
58 }
59 if (sizeOfType > sizeof(ExpectedT)) {
60 // endianness not checked
61 throw std::runtime_error("Storage type mismatch for integer. Result "
62 "would result in truncation. Nexus stored has byte size:" +
63 std::to_string(sizeOfType));
64 }
65 }
66}
67
68// test utility used for validation of the structure of a nexus file as needed
69// for unit tests in Nexus Geometry.
71
72 bool m_open = false;
73
74public:
75 NexusFileReader(const std::string &fullPath) {
76 boost::filesystem::path tmp = fullPath;
77
78 if (!boost::filesystem::exists(tmp)) {
79 throw std::invalid_argument("no such file.\n");
80 } else {
81 m_file.openFile(fullPath, H5F_ACC_RDONLY);
82 m_open = true;
83 }
84 }
85
86 int countNXgroup(const FullNXPath &pathToGroup, const std::string &nxClass) {
87 int counter = 0;
88 H5::Group parentGroup = openfullH5Path(pathToGroup);
89 for (hsize_t i = 0; i < parentGroup.getNumObjs(); ++i) {
90 if (parentGroup.getObjTypeByIdx(i) == GROUP_TYPE) {
91 H5std_string childPath = parentGroup.getObjnameByIdx(i);
92 // Open the sub group
93 auto childGroup = parentGroup.openGroup(childPath);
94 // Iterate through attributes to find NX_class
95 for (uint32_t attribute_index = 0; attribute_index < static_cast<uint32_t>(childGroup.getNumAttrs());
96 ++attribute_index) {
97 // Test attribute at current index for NX_class
98 H5::Attribute attribute = childGroup.openAttribute(attribute_index);
99 if (attribute.getName() == NX_CLASS) {
100 // Get attribute data type
101 H5::DataType dataType = attribute.getDataType();
102 // Get the NX_class type
103 H5std_string classType;
104 attribute.read(dataType, classType);
105 // If group of correct type, return the childGroup
106 if (classType == nxClass) {
107 counter++;
108 }
109 }
110 }
111 }
112 }
113 return counter;
114 }
115
116 // read a multidimensional dataset and returns vector containing the data
117 template <typename T>
118 std::vector<T> readDataSetMultidimensional(FullNXPath &pathToGroup, const std::string &dataSetName) {
119
120 std::vector<T> dataInFile;
121
122 // open dataset and read.
123 H5::Group parentGroup = openfullH5Path(pathToGroup);
124 H5::DataSet dataset = parentGroup.openDataSet(dataSetName);
125
126 validateStorageType<T>(dataset);
127 auto space = dataset.getSpace();
128
129 dataInFile.resize(space.getSelectNpoints());
130 dataset.read(dataInFile.data(), dataset.getDataType(), space);
131 return dataInFile;
132 }
133
134 /* safely open a HDF5 group path with additional helpful
135 debug information to output where open fails) */
136 H5::Group openfullH5Path(const FullNXPath &pathList) const {
137
138 H5::Group child;
139 H5::Group parent = m_file.openGroup(pathList[0]);
140
141 for (size_t i = 1; i < pathList.size(); ++i) {
142 child = parent.openGroup(pathList[i]);
143 parent = child;
144 }
145 return child;
146 }
147
148 // moves down the index through groups starting at root, and if
149 // child has expected CLASS_TYPE, and is in parent group with expected
150 // parent
151
152 bool parentNXgroupHasChildNXgroup(const std::string &parentNX_CLASS_TYPE, const std::string &childNX_CLASS_TYPE) {
153
154 H5::Group rootGroup = m_file.openGroup(DEFAULT_ROOT_ENTRY_NAME);
155
156 // if specified parent NX class type is NX entry, check the top level of
157 // file structure only. (dont take extra step to look for parent group)
158 if (parentNX_CLASS_TYPE == NX_ENTRY) {
159
160 for (size_t i = 0; i < rootGroup.getNumObjs(); ++i) {
161 if (rootGroup.getObjTypeByIdx(i) == GROUP_TYPE) {
162 std::string childPath = rootGroup.getObjnameByIdx(i);
163 // Open the sub group
164 H5::Group childGroup = rootGroup.openGroup(childPath);
165 // Test attribute at current index for NX_class
166 H5::Attribute attribute = childGroup.openAttribute(NX_CLASS);
167 std::string attrVal;
168 attribute.read(attribute.getDataType(), attrVal);
169 if (attrVal == childNX_CLASS_TYPE) {
170 return true;
171 }
172 }
173 }
174 }
175
176 // Iterate over children of root group, and determine if a group
177 for (size_t i = 0; i < rootGroup.getNumObjs(); ++i) {
178 if (rootGroup.getObjTypeByIdx(i) == GROUP_TYPE) {
179 std::string childPath = rootGroup.getObjnameByIdx(i);
180 // Open the sub group
181 H5::Group childGroup = rootGroup.openGroup(childPath);
182 // check current child group going down from root has the specified
183 // NX_CLASS parent group
184 H5::Attribute parentAttribute = childGroup.openAttribute(NX_CLASS);
185 std::string parentAttrVal;
186 parentAttribute.read(parentAttribute.getDataType(), parentAttrVal);
187 if (parentAttrVal == parentNX_CLASS_TYPE) {
188 for (size_t i = 0; i < childGroup.getNumObjs(); ++i) {
189 if (childGroup.getObjTypeByIdx(i) == GROUP_TYPE) {
190 std::string grandchildPath = childGroup.getObjnameByIdx(i);
191 // Open the sub group
192 H5::Group grandchildGroup = childGroup.openGroup(grandchildPath);
193 // check NX class
194 H5::Attribute grandchildAttribute = grandchildGroup.openAttribute(NX_CLASS);
195 std::string grandchildAttrVal;
196 grandchildAttribute.read(grandchildAttribute.getDataType(), grandchildAttrVal);
197 if (childNX_CLASS_TYPE == grandchildAttrVal) {
198 return true;
199 }
200 }
201 }
202 }
203 }
204 }
205
206 return false;
207 } // namespace
208
209 double readDoubleFromDataset(const std::string &datasetName, const FullNXPath &pathToGroup) {
210 double value;
211 int rank = 1;
212 hsize_t dims[static_cast<hsize_t>(1)];
213 dims[0] = static_cast<hsize_t>(1);
214
215 H5::DataSpace space = H5Screate_simple(rank, dims, nullptr);
216
217 // open dataset and read.
218 H5::Group parentGroup = openfullH5Path(pathToGroup);
219 H5::DataSet dataset = parentGroup.openDataSet(datasetName);
220 dataset.read(&value, H5::PredType::NATIVE_DOUBLE, space);
221 return value;
222 }
223
224 // HERE
225 std::vector<double> readDoubleVectorFrom_d_Attribute(const std::string &attrName, const std::string &datasetName,
226 const FullNXPath &pathToGroup) {
227
228 // open dataset and read.
229 H5::Group parentGroup = openfullH5Path(pathToGroup);
230 H5::DataSet dataset = parentGroup.openDataSet(datasetName);
231
232 H5::Attribute attribute = dataset.openAttribute(attrName);
233
234 H5::DataType dataType = attribute.getDataType();
235 H5::DataSpace dataSpace = attribute.getSpace();
236
237 std::vector<double> value;
238 value.resize(dataSpace.getSelectNpoints());
239
240 attribute.read(dataType, value.data());
241
242 return value;
243 }
244
245 // HERE
246 bool hasDatasetWithNXAttribute(const std::string &pathToGroup, const std::string &nx_attributeVal) {
247
248 H5::Group parentGroup = m_file.openGroup(pathToGroup);
249 auto numOfChildren = parentGroup.getNumObjs();
250 for (size_t i = 0; i < numOfChildren; i++) {
251 if (parentGroup.getObjTypeByIdx(i) == DATASET_TYPE) {
252 std::string dSetName = parentGroup.getObjnameByIdx(i);
253 H5::DataSet dSet = parentGroup.openDataSet(dSetName);
254 if (dSet.attrExists(NX_CLASS)) {
255 H5::Attribute attribute = dSet.openAttribute(NX_CLASS);
256 std::string attributeValue;
257 attribute.read(attribute.getDataType(), attributeValue);
258 if (attributeValue == nx_attributeVal)
259 return true;
260 }
261 }
262 }
263 return false;
264 }
265
266 // HERE
267 bool hasDatasetWithAttribute(const std::string &pathToGroup, const std::string &attributeVal,
268 const std::string &attrName) {
269
270 H5::Group parentGroup = m_file.openGroup(pathToGroup);
271 auto numOfChildren = parentGroup.getNumObjs();
272 for (size_t i = 0; i < numOfChildren; i++) {
273 if (parentGroup.getObjTypeByIdx(i) == DATASET_TYPE) {
274 std::string dSetName = parentGroup.getObjnameByIdx(i);
275 H5::DataSet dSet = parentGroup.openDataSet(dSetName);
276 if (dSet.attrExists(NX_CLASS)) {
277 H5::Attribute attribute = dSet.openAttribute(attrName);
278 std::string attributeValue;
279 attribute.read(attribute.getDataType(), attributeValue);
280 if (attributeValue == attributeVal)
281 return true;
282 }
283 }
284 }
285 return false;
286 }
287
288 bool hasDataset(const std::string &dsetName, const FullNXPath &pathToGroup) {
289
290 H5::Group parentGroup = openfullH5Path(pathToGroup);
291
292 auto numOfChildren = parentGroup.getNumObjs();
293 for (size_t i = 0; i < numOfChildren; i++) {
294 if (parentGroup.getObjTypeByIdx(i) == DATASET_TYPE) {
295 std::string dataSetName = parentGroup.getObjnameByIdx(i);
296 if (dsetName == dataSetName) {
297 return true;
298 }
299 }
300 }
301 return false;
302 }
303
304 bool groupHasNxClass(const std::string &attrVal, const std::string &pathToGroup) const {
305
306 H5::Attribute attribute;
307 H5::Group parentGroup = m_file.openGroup(pathToGroup);
308 attribute = parentGroup.openAttribute(NX_CLASS);
309 std::string attributeValue;
310 attribute.read(attribute.getDataType(), attributeValue);
311
312 return attributeValue == attrVal;
313 }
314
315 bool dataSetHasStrValue(const std::string &dataSetName, const std::string &dataSetValue,
316 const FullNXPath &pathToGroup /*where the dataset lives*/) const {
317
318 H5::Group parentGroup = openfullH5Path(pathToGroup);
319
320 try {
321 H5::DataSet dataSet = parentGroup.openDataSet(dataSetName);
322 std::string dataSetVal;
323 auto type = dataSet.getDataType();
324 dataSet.read(dataSetVal, type);
325 dataSetVal.resize(type.getSize());
326 return dataSetVal == dataSetValue;
327 } catch (H5::DataSetIException &) {
328 return false;
329 }
330 }
331
332 // check if dataset or group has name-specific attribute
333 bool hasAttributeInGroup(const std::string &attrName, const std::string &attrVal, const FullNXPath &pathToGroup) {
334
335 H5::Group parentGroup = openfullH5Path(pathToGroup);
336
337 H5::Attribute attribute = parentGroup.openAttribute(attrName);
338 std::string attributeValue;
339 auto type = attribute.getDataType();
340 attribute.read(type, attributeValue);
341 attributeValue.resize(type.getSize());
342 return attributeValue == attrVal;
343 }
344
345 bool hasNXAttributeInGroup(const std::string &attrVal, const FullNXPath &pathToGroup) {
346
347 H5::Group parentGroup = openfullH5Path(pathToGroup);
348
349 H5::Attribute attribute = parentGroup.openAttribute(NX_CLASS);
350 std::string attributeValue;
351 attribute.read(attribute.getDataType(), attributeValue);
352
353 return attributeValue == attrVal;
354 }
355
356 bool hasAttributeInDataSet(const std::string &dataSetName, const std::string &attrName, const std::string &attrVal,
357 const FullNXPath &pathToGroup /*where the dataset lives*/) {
358
359 H5::Attribute attribute;
360 H5::Group parentGroup = openfullH5Path(pathToGroup);
361 H5::DataSet dataSet = parentGroup.openDataSet(dataSetName);
362 attribute = dataSet.openAttribute(attrName);
363 std::string attributeValue;
364 attribute.read(attribute.getDataType(), attributeValue);
365
366 return attributeValue == attrVal;
367 }
368
369 bool hasNXAttributeInDataSet(const std::string &dataSetName, const std::string &attrVal,
370 const FullNXPath &pathToGroup) {
371 H5::Attribute attribute;
372 H5::Group parentGroup = openfullH5Path(pathToGroup);
373 H5::DataSet dataSet = parentGroup.openDataSet(dataSetName);
374 attribute = dataSet.openAttribute(NX_CLASS);
375 std::string attributeValue;
376 attribute.read(attribute.getDataType(), attributeValue);
377
378 return attributeValue == attrVal;
379 }
380
381 void close() {
382 if (m_open) {
383 m_file.close();
384 }
385 m_open = false;
386 }
387
389
390private:
391 H5::H5File m_file;
392
393}; // NexusFileReader
394} // namespace NexusGeometry
395} // namespace Mantid
gsl_vector * tmp
double value
The value of the point.
Definition: FitMW.cpp:51
int countNXgroup(const FullNXPath &pathToGroup, const std::string &nxClass)
bool hasDatasetWithAttribute(const std::string &pathToGroup, const std::string &attributeVal, const std::string &attrName)
bool dataSetHasStrValue(const std::string &dataSetName, const std::string &dataSetValue, const FullNXPath &pathToGroup) const
H5::Group openfullH5Path(const FullNXPath &pathList) const
std::vector< T > readDataSetMultidimensional(FullNXPath &pathToGroup, const std::string &dataSetName)
bool hasNXAttributeInGroup(const std::string &attrVal, const FullNXPath &pathToGroup)
bool hasAttributeInGroup(const std::string &attrName, const std::string &attrVal, const FullNXPath &pathToGroup)
std::vector< double > readDoubleVectorFrom_d_Attribute(const std::string &attrName, const std::string &datasetName, const FullNXPath &pathToGroup)
bool hasAttributeInDataSet(const std::string &dataSetName, const std::string &attrName, const std::string &attrVal, const FullNXPath &pathToGroup)
NexusFileReader(const std::string &fullPath)
bool hasDatasetWithNXAttribute(const std::string &pathToGroup, const std::string &nx_attributeVal)
bool hasNXAttributeInDataSet(const std::string &dataSetName, const std::string &attrVal, const FullNXPath &pathToGroup)
bool groupHasNxClass(const std::string &attrVal, const std::string &pathToGroup) const
bool hasDataset(const std::string &dsetName, const FullNXPath &pathToGroup)
double readDoubleFromDataset(const std::string &datasetName, const FullNXPath &pathToGroup)
bool parentNXgroupHasChildNXgroup(const std::string &parentNX_CLASS_TYPE, const std::string &childNX_CLASS_TYPE)
std::string toNXPathString(FullNXPath &path)
void validateStorageType(const H5::DataSet &data)
std::vector< std::string > FullNXPath
Helper class which provides the Collimation Length for SANS instruments.
std::string to_string(const wide_integer< Bits, Signed > &n)