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 = "";
37
38 if (groupID.attrExists("NX_class")) {
39 const auto attributeID = groupID.openAttribute("NX_class");
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
93const std::map<std::string, std::set<std::string>> &NexusDescriptor::getAllEntries() const noexcept {
94 return m_allEntries;
95}
96
97void NexusDescriptor::addRootAttr(const std::string &name) { m_rootAttrs.insert(name); }
98
99void NexusDescriptor::addEntry(const std::string &entryName, const std::string &groupClass) {
100 // simple checks
101 if (entryName.empty())
102 throw Exception("Cannot add empty path", "", m_filename);
103 if (groupClass.empty())
104 throw Exception("Cannot add empty class", "", m_filename);
105 if (!entryName.starts_with("/"))
106 throw Exception("Address must be absolute: " + entryName, "", m_filename);
107
108 // do not add address twice
109 if (this->isEntry(entryName))
110 throw Exception("Cannot add an entry twice: " + entryName, "", m_filename);
111
112 // verify the parent exists
113 const auto lastPos = entryName.rfind("/");
114 const auto parentAddress = entryName.substr(0, lastPos);
115 if (parentAddress != "" && !this->isEntry(parentAddress))
116 throw Exception("Parent address " + parentAddress + " does not exist", "", m_filename);
117
118 // add the address
119 m_allEntries[groupClass].insert(entryName);
120}
121
122// PRIVATE
123std::map<std::string, std::set<std::string>> NexusDescriptor::initAllEntries() {
124
125 std::map<std::string, std::set<std::string>> allEntries;
126
127 // if the file exists read it
128 if (std::filesystem::exists(m_filename)) {
129 // if the file exists but cannot be opened, throw invalid
130 // NOTE must be std::invalid_argument for expected errors to be raised in python API
131 if (!H5::H5File::isAccessible(m_filename, Mantid::Nexus::H5Util::defaultFileAcc())) {
132 throw std::invalid_argument("ERROR: Kernel::NexusDescriptor couldn't open hdf5 file " + m_filename + "\n");
133 }
134
135 H5::H5File fileID(m_filename, H5F_ACC_RDONLY, Mantid::Nexus::H5Util::defaultFileAcc());
136 H5::Group groupID = fileID.openGroup("/");
137
138 // get root attributes
139 for (int i = 0; i < groupID.getNumAttrs(); ++i) {
140 H5::Attribute attr = groupID.openAttribute(i);
141 m_rootAttrs.insert(attr.getName());
142 }
143
144 // scan file recursively starting with root group "/"
145 getGroup(groupID, allEntries, m_firstEntryNameType, 0);
146
147 // handle going out of scope should automatically close
148 fileID.close();
149 } else {
150 // if the file does not exist, then leave allEntries empty
151 }
152
153 // rely on move semantics
154 return allEntries;
155}
156
157bool NexusDescriptor::isEntry(const std::string &entryName, const std::string &groupClass) const noexcept {
158
159 auto itClass = m_allEntries.find(groupClass);
160 if (itClass == m_allEntries.end()) {
161 return false;
162 }
163
164 if (itClass->second.count(entryName) == 1) {
165 return true;
166 }
167
168 return false;
169}
170
171bool NexusDescriptor::isEntry(const std::string &entryName) const noexcept {
172 return std::any_of(m_allEntries.rbegin(), m_allEntries.rend(),
173 [&entryName](const auto &entry) { return entry.second.count(entryName) == 1; });
174}
175
176std::vector<std::string> NexusDescriptor::allAddressesOfType(const std::string &type) const {
177 std::vector<std::string> result;
178 if (auto itClass = m_allEntries.find(type); itClass != m_allEntries.end()) {
179 result.assign(itClass->second.begin(), itClass->second.end());
180 }
181
182 return result;
183}
184
185std::map<std::string, std::string> NexusDescriptor::allAddressesAtLevel(const std::string &level) const {
186 std::map<std::string, std::string> result;
187 for (auto itClass = m_allEntries.cbegin(); itClass != m_allEntries.cend(); itClass++) {
188 for (auto itEntry = itClass->second.cbegin(); itEntry != itClass->second.cend(); itEntry++) {
189 if (itEntry->size() <= level.size()) {
190 continue;
191 }
192 if (itEntry->starts_with(level)) {
193 int offset = (level == "/" ? 0 : 1);
194 std::string address = itEntry->substr(level.size() + offset, itEntry->find("/", level.size() + offset));
195 if (itEntry->ends_with(address)) {
196 result[address] = itClass->first;
197 }
198 }
199 }
200 }
201 return result;
202}
203
204bool NexusDescriptor::classTypeExists(const std::string &classType) const { return m_allEntries.contains(classType); }
205
206std::string NexusDescriptor::classTypeForName(std::string const &entryName) const {
207 std::string groupClass;
208 auto it = m_allEntries.cbegin();
209 for (; it != m_allEntries.cend(); it++) {
210 if (it->second.count(entryName) == 1) {
211 groupClass = it->first;
212 break;
213 }
214 }
215 if (it == m_allEntries.cend()) {
216 throw Exception("Cannot find entry " + entryName, "classTypeForName", m_filename);
217 }
218 return groupClass;
219}
220
221} // 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.
const std::map< std::string, std::set< std::string > > & getAllEntries() const noexcept
Returns a const reference of the internal map holding all entries in the Nexus HDF5 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.
std::vector< std::string > allAddressesOfType(const std::string &type) const
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.
STL namespace.