Mantid
Loading...
Searching...
No Matches
NexusDescriptor.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2007 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
11
12#include <H5Cpp.h>
13#include <boost/multi_index/detail/index_matcher.hpp>
14
15#include <cstdlib> // malloc, calloc
16#include <cstring> // strcpy
17#include <filesystem>
18#include <stdexcept> // std::invalid_argument
19#include <utility>
20
21using boost::multi_index::detail::index_matcher::entry;
22
23namespace Mantid::Nexus {
24
27namespace {
28
29void getGroup(H5::Group groupID, std::map<std::string, std::set<std::string>> &allEntries,
30 std::pair<std::string, std::string> &firstEntryNameType, size_t level) {
31
35 auto lf_getNxClassAttribute = [&](H5::Group groupID) -> std::string {
36 std::string attribute = UNKNOWN_GROUP_SPEC;
37
38 if (groupID.attrExists(GROUP_CLASS_SPEC)) {
39 const auto attributeID = groupID.openAttribute(GROUP_CLASS_SPEC);
40 attributeID.read(attributeID.getDataType(), attribute);
41 }
42
43 return attribute;
44 };
45
46 // using HDF5 C++ API
47 const std::string groupNameStr = groupID.getObjName();
48 const std::string nxClass = (groupNameStr == "/") ? "" : lf_getNxClassAttribute(groupID);
49
50 if (!nxClass.empty()) {
51 allEntries[nxClass].insert(groupNameStr);
52 }
53
54 for (hsize_t i = 0; i < groupID.getNumObjs(); ++i) {
55
56 H5G_obj_t type = groupID.getObjTypeByIdx(i);
57 H5std_string memberName = groupID.getObjnameByIdx(i);
58
59 if (type == H5G_GROUP) {
60 H5::Group subGroupID = groupID.openGroup(memberName);
61 if (level == 0)
62 firstEntryNameType = std::make_pair(memberName, lf_getNxClassAttribute(subGroupID));
63 getGroup(subGroupID, allEntries, firstEntryNameType, level + 1);
64 } else if (type == H5G_DATASET) {
65 allEntries["SDS"].emplace(groupNameStr + "/" + memberName);
66 }
67 }
68}
69} // namespace
70
71// PUBLIC
72
73NexusDescriptor::NexusDescriptor(std::string const &filename)
74 : m_filename(filename), m_extension(std::filesystem::path(m_filename).extension().string()), m_firstEntryNameType(),
75 m_allEntries(initAllEntries()) {}
76
77NexusDescriptor::NexusDescriptor(std::string const &filename, NXaccess access)
78 : m_filename(filename), m_extension(std::filesystem::path(m_filename).extension().string()),
79 m_firstEntryNameType() {
80 // if we are creating a file and it already exists, then delete it first
81 if (access == NXaccess::CREATE5) {
82 if (std::filesystem::exists(m_filename)) {
83 std::filesystem::remove(m_filename);
84 }
85 }
87}
88
89const std::string &NexusDescriptor::filename() const noexcept { return m_filename; }
90
91bool NexusDescriptor::hasRootAttr(const std::string &name) const { return (m_rootAttrs.count(name) == 1); }
92
93void NexusDescriptor::addRootAttr(const std::string &name) { m_rootAttrs.insert(name); }
94
95void NexusDescriptor::addEntry(const std::string &entryName, const std::string &groupClass) {
96 // simple checks
97 if (entryName.empty())
98 throw Exception("Cannot add empty path", "", m_filename);
99 if (groupClass.empty())
100 throw Exception("Cannot add empty class", "", m_filename);
101 if (!entryName.starts_with("/"))
102 throw Exception("Address must be absolute: " + entryName, "", m_filename);
103
104 // do not add address twice
105 if (this->isEntry(entryName))
106 throw Exception("Cannot add an entry twice: " + entryName, "", m_filename);
107
108 // verify the parent exists
109 const auto lastPos = entryName.rfind("/");
110 const auto parentAddress = entryName.substr(0, lastPos);
111 if (parentAddress != "" && !this->isEntry(parentAddress))
112 throw Exception("Parent address " + parentAddress + " does not exist", "", m_filename);
113
114 // add the address
115 m_allEntries[groupClass].insert(entryName);
116}
117
118// PRIVATE
119std::map<std::string, std::set<std::string>> NexusDescriptor::initAllEntries() {
120
121 std::map<std::string, std::set<std::string>> allEntries;
122
123 // if the file exists read it
124 if (std::filesystem::exists(m_filename)) {
125 // if the file exists but cannot be opened, throw invalid
126 // NOTE must be std::invalid_argument for expected errors to be raised in python API
127 if (!H5::H5File::isAccessible(m_filename, Mantid::Nexus::H5Util::defaultFileAcc())) {
128 throw std::invalid_argument("ERROR: Kernel::NexusDescriptor couldn't open hdf5 file " + m_filename + "\n");
129 }
130
131 H5::H5File fileID(m_filename, H5F_ACC_RDONLY, Mantid::Nexus::H5Util::defaultFileAcc());
132 H5::Group groupID = fileID.openGroup("/");
133
134 // get root attributes
135 for (int i = 0; i < groupID.getNumAttrs(); ++i) {
136 H5::Attribute attr = groupID.openAttribute(i);
137 m_rootAttrs.insert(attr.getName());
138 }
139
140 // scan file recursively starting with root group "/"
141 getGroup(groupID, allEntries, m_firstEntryNameType, 0);
142
143 // handle going out of scope should automatically close
144 fileID.close();
145 } else {
146 // if the file does not exist, then leave allEntries empty
147 }
148
149 // rely on move semantics
150 return allEntries;
151}
152
153bool NexusDescriptor::isEntry(const std::string &entryName, const std::string &groupClass) const noexcept {
154
155 auto itClass = m_allEntries.find(groupClass);
156 if (itClass == m_allEntries.end()) {
157 return false;
158 }
159
160 if (itClass->second.count(entryName) == 1) {
161 return true;
162 }
163
164 return false;
165}
166
167bool NexusDescriptor::isEntry(const std::string &entryName) const noexcept {
168 return std::any_of(m_allEntries.rbegin(), m_allEntries.rend(),
169 [&entryName](const auto &entry) { return entry.second.count(entryName) == 1; });
170}
171
172std::map<std::string, std::string> NexusDescriptor::allAddressesAtLevel(const std::string &level) const {
173 std::map<std::string, std::string> result;
174 for (auto itClass = m_allEntries.cbegin(); itClass != m_allEntries.cend(); itClass++) {
175 for (auto itEntry = itClass->second.cbegin(); itEntry != itClass->second.cend(); itEntry++) {
176 if (itEntry->size() <= level.size()) {
177 continue;
178 }
179 if (itEntry->starts_with(level)) {
180 int offset = (level == "/" ? 0 : 1);
181 std::string address = itEntry->substr(level.size() + offset, itEntry->find("/", level.size() + offset));
182 if (itEntry->ends_with(address)) {
183 result[address] = itClass->first;
184 }
185 }
186 }
187 }
188 return result;
189}
190
191bool NexusDescriptor::classTypeExists(const std::string &classType) const { return m_allEntries.contains(classType); }
192
193std::string NexusDescriptor::classTypeForName(std::string const &entryName) const {
194 std::string groupClass;
195 auto it = m_allEntries.cbegin();
196 for (; it != m_allEntries.cend(); it++) {
197 if (it->second.count(entryName) == 1) {
198 groupClass = it->first;
199 break;
200 }
201 }
202 if (it == m_allEntries.cend()) {
203 throw Exception("Cannot find entry " + entryName, "classTypeForName", m_filename);
204 }
205 return groupClass;
206}
207
208} // namespace Mantid::Nexus
std::string name
Definition Run.cpp:60
NXaccess
Nexus file access codes.
uint64_t hsize_t
Class that provides for a standard Nexus exception.
bool classTypeExists(const std::string &classType) const
Query if a given type exists somewhere in the file.
bool isEntry(const std::string &entryName, const std::string &groupClass) const noexcept
Checks if a full-address entry exists for a particular groupClass in a Nexus dataset.
std::string classTypeForName(std::string const &name) const
std::pair< std::string, std::string > m_firstEntryNameType
First entry name/type.
const std::string & filename() const noexcept
Returns a copy of the current file name.
std::map< std::string, std::set< std::string > > initAllEntries()
Sets m_allEntries, called in HDF5 constructor.
std::string m_filename
Nexus HDF5 file name.
std::map< std::string, std::string > allAddressesAtLevel(const std::string &level) const
void addRootAttr(const std::string &name)
std::unordered_set< std::string > m_rootAttrs
Root attributes.
void addEntry(const std::string &entryName, const std::string &groupClass)
Add an entry to the mapping for the file.
bool hasRootAttr(const std::string &name) const
Query if the given attribute exists on the root node.
std::map< std::string, std::set< std::string > > m_allEntries
All entries metadata.
MANTID_NEXUS_DLL H5::FileAccPropList defaultFileAcc()
Default file access is H5F_CLOSE_STRONG.
Definition H5Util.cpp:119
Header for a base Nexus::Exception.
std::string const UNKNOWN_GROUP_SPEC("NX_UNKNOWN_GROUP")
std::string const GROUP_CLASS_SPEC("NX_class")
STL namespace.