Mantid
Loading...
Searching...
No Matches
PropertyNexus.cpp
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 +
8
9// clang-format off
10#include <nexus/NeXusFile.hpp>
11#include <nexus/NeXusException.hpp>
12// clang-format on
13
19
20// PropertyWithValue implementation
21#include "MantidKernel/PropertyWithValue.tcc"
22
23#include <boost/algorithm/string/split.hpp>
24
25#include <memory>
26
27using namespace Mantid::Kernel;
28using namespace ::NeXus;
29
30#ifdef _WIN32
31#pragma warning(push)
32#pragma warning(disable : 4805)
33#endif
34
36
37namespace {
38//----------------------------------------------------------------------------------------------
46template <typename NumT>
47std::unique_ptr<Property> makeProperty(::NeXus::File *file, const std::string &name,
48 const std::vector<Types::Core::DateAndTime> &times) {
49 std::vector<NumT> values;
50 file->getData(values);
51 if (times.empty()) {
52 if (values.size() == 1) {
53 return std::make_unique<PropertyWithValue<NumT>>(name, values[0]);
54 } else {
55 return std::make_unique<ArrayProperty<NumT>>(name, std::move(values));
56 }
57 } else {
58 auto prop = std::make_unique<TimeSeriesProperty<NumT>>(name);
59 prop->addValues(times, values);
60 return std::unique_ptr<Property>(std::move(prop));
61 }
62}
63
71std::unique_ptr<Property> makeTimeSeriesBoolProperty(::NeXus::File *file, const std::string &name,
72 const std::vector<Types::Core::DateAndTime> &times) {
73 std::vector<uint8_t> savedValues;
74 file->getData(savedValues);
75 const size_t nvals = savedValues.size();
76 std::vector<bool> realValues(nvals);
77 for (size_t i = 0; i < nvals; ++i) {
78 realValues[i] = (savedValues[i] != 0);
79 }
80 auto prop = std::make_unique<TimeSeriesProperty<bool>>(name);
81 prop->addValues(times, realValues);
82 return std::unique_ptr<Property>(std::move(prop));
83}
84
86std::unique_ptr<Property> makeStringProperty(::NeXus::File *file, const std::string &name,
87 const std::vector<Types::Core::DateAndTime> &times) {
88 std::vector<std::string> values;
89 if (times.empty()) {
90 std::string bigString = file->getStrData();
91 return std::make_unique<PropertyWithValue<std::string>>(name, bigString);
92 } else {
93 if (file->getInfo().dims.size() != 2)
94 throw std::runtime_error("NXlog loading failed on field " + name + ". Expected rank 2.");
95 int64_t numStrings = file->getInfo().dims[0];
96 int64_t span = file->getInfo().dims[1];
97 auto data = std::make_unique<char[]>(numStrings * span);
98 file->getData(data.get());
99 values.reserve(static_cast<size_t>(numStrings));
100 for (int64_t i = 0; i < numStrings; i++)
101 values.emplace_back(data.get() + i * span);
102
103 auto prop = std::make_unique<TimeSeriesProperty<std::string>>(name);
104 prop->addValues(times, values);
105 return std::unique_ptr<Property>(std::move(prop));
106 }
107}
111void getTimeAndStart(::NeXus::File *file, std::vector<double> &timeSec, std::string &startStr) {
112 file->openData("time");
113 file->getData(timeSec);
114 // Optionally get a start
115 try {
116 file->getAttr("start", startStr);
117 } catch (::NeXus::Exception &) {
118 }
119 file->closeData();
120}
121
131std::unique_ptr<Property> loadPropertyCommon(::NeXus::File *file, const std::string &group,
132 const std::vector<double> &timeSec, std::string &startStr) {
133 std::vector<Types::Core::DateAndTime> times;
134 if (!timeSec.empty()) {
135 // Use a default start time
136 if (startStr.empty())
137 startStr = "2000-01-01T00:00:00";
138 // Convert time in seconds to DateAndTime
139 Types::Core::DateAndTime start(startStr);
140 times.reserve(timeSec.size());
141 std::transform(timeSec.cbegin(), timeSec.cend(), std::back_inserter(times),
142 [&start](const auto &time) { return start + time; });
143 }
144
145 file->openData("value");
146 std::unique_ptr<Property> retVal = nullptr;
147 switch (file->getInfo().type) {
148 case ::NeXus::FLOAT32:
149 retVal = makeProperty<float>(file, group, times);
150 break;
151 case ::NeXus::FLOAT64:
152 retVal = makeProperty<double>(file, group, times);
153 break;
154 case ::NeXus::INT32:
155 retVal = makeProperty<int32_t>(file, group, times);
156 break;
157 case ::NeXus::UINT32:
158 retVal = makeProperty<uint32_t>(file, group, times);
159 break;
160 case ::NeXus::INT64:
161 retVal = makeProperty<int64_t>(file, group, times);
162 break;
163 case ::NeXus::UINT64:
164 retVal = makeProperty<uint64_t>(file, group, times);
165 break;
166 case ::NeXus::CHAR:
167 retVal = makeStringProperty(file, group, times);
168 break;
169 case ::NeXus::UINT8: {
170 // Check the type at the group level. Boolean stored as UINT8
171 file->closeData();
172 const bool typeIsBool = file->hasAttr("boolean");
173 file->openData("value");
174
175 if (typeIsBool)
176 retVal = makeTimeSeriesBoolProperty(file, group, times);
177 break;
178 }
179 case ::NeXus::INT8:
180 case ::NeXus::INT16:
181 case ::NeXus::UINT16:
182 retVal = nullptr;
183 break;
184 }
185
186 // verifying that the attribute exists makes this very slow in NeXus v4.4.3
187 // because of the change in how nxgetnextattr is implemented
188 std::string unitsStr;
189 try {
190 file->getAttr("units", unitsStr);
191 } catch (::NeXus::Exception &) {
192 // let it drop on the floor
193 }
194
195 file->closeData();
196 file->closeGroup();
197 // add units
198 if (retVal)
199 retVal->setUnits(unitsStr);
200 return retVal;
201}
202
203} // namespace
204//----------------------------------------------------------------------------------------------
205
206std::unique_ptr<Property> loadProperty(::NeXus::File *file, const std::string &group,
207 const Mantid::Kernel::NexusHDF5Descriptor &fileInfo, const std::string &prefix) {
208 file->openGroup(group, "NXlog");
209
210 // Times in second offsets
211 std::vector<double> timeSec;
212 std::string startStr;
213
214 // Check if the "time" field is present
215 if (fileInfo.isEntry(prefix + "/" + group + "/time")) {
216 getTimeAndStart(file, timeSec, startStr);
217 }
218
219 return loadPropertyCommon(file, group, timeSec, startStr);
220}
221
222//----------------------------------------------------------------------------------------------
230std::unique_ptr<Property> loadProperty(::NeXus::File *file, const std::string &group) {
231 file->openGroup(group, "NXlog");
232
233 // Times in second offsets
234 std::vector<double> timeSec;
235 std::string startStr;
236
237 // Get the entries so that you can check if the "time" field is present
238 std::map<std::string, std::string> entries = file->getEntries();
239 if (entries.find("time") != entries.end()) {
240 getTimeAndStart(file, timeSec, startStr);
241 }
242
243 return loadPropertyCommon(file, group, timeSec, startStr);
244}
245
246#ifdef _WIN32
247#pragma warning(pop)
248#endif
249
250} // namespace Mantid::Kernel::PropertyNexus
bool isEntry(const std::string &entryName, const std::string &groupClass) const noexcept
Checks if a full-path entry exists for a particular groupClass in a Nexus dataset.
Namespace with helper methods for loading and saving Property's (logs) to NXS files.
Definition: PropertyNexus.h:30
DLLExport std::unique_ptr< Property > loadProperty(::NeXus::File *file, const std::string &group, const Mantid::Kernel::NexusHDF5Descriptor &fileInfo, const std::string &prefix)
Opens a NXlog group in a nexus file and creates the correct Property object from it.