Mantid
Loading...
Searching...
No Matches
MuonNexusReader.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 +
9#include <boost/scoped_array.hpp>
10#include <nexus/NeXusException.hpp>
11#include <sstream>
12#include <vector>
13
14using std::string;
15
16namespace { // anonymous namespace to keep things in the file
18const string NXDATA("NXdata");
20const string NXENTRY("NXentry");
22const string NXLOG("NXlog");
24const string START_TIME("start_time");
25
27Mantid::Kernel::Logger g_log("MuonNexusReader");
28
30const std::string PERIOD_SEQUENCES("period_sequences");
31const std::string PERIOD_TYPE("period_type");
32const std::string FRAMES_PERIOD_REQUESTED("frames_period_requested");
33const std::string FRAMES_PERIOD_RAW("frames_period_raw");
34const std::string PERIOD_OUTPUT("period_output");
35const std::string TOTAL_COUNTS_PERIOD("total_counts_period");
36const std::string PERIOD_LABELS("period_labels");
37
39template <class T> std::string convertVectorToString(T values, const std::string &delim) {
40 std::string result("");
41 if (!values.empty()) {
42 for (const auto &value : values)
43 result += std::to_string(value) + delim;
44 result.erase(result.length() - 1); // Remove final delim
45 }
46 return result;
47}
48} // namespace
49
50using namespace Mantid;
51
57void MuonNexusReader::openFirstNXentry(NeXus::File &handle) {
58 std::map<string, string> entries = handle.getEntries();
59 const auto entry =
60 std::find_if(entries.cbegin(), entries.cend(), [](const auto entry) { return entry.second == NXENTRY; });
61 if (entry == entries.cend())
62 throw std::runtime_error("Failed to find NXentry");
63 handle.openGroup(entry->first, NXENTRY);
64}
65
66// Basic NeXus Muon file reader - simple version based on contents of test
67// files.
68// Read the given Nexus file into temp storage. Following the approach of
69// ISISRAW
70// which does not use namespace.
71// This reader is only used by LoadMuonNexus - the NexusProcessed files are
72// dealt with by
73// NexusFileIO.cpp
74//
75// Expected content of Nexus file is:
76// Entry: "run" (first entry opened, whatever name is)
77// Group: "histogram_data_1" (first NXdata section read, whatever name is)
78// Data: "counts" (2D integer array)
79// Data: "corrected time" (1D float array)
80//
81// @param filename :: name of existing NeXus Muon file to read
82void MuonNexusReader::readFromFile(const string &filename) {
83 NeXus::File handle(filename, NXACC_READ);
84 openFirstNXentry(handle);
85
86 // find all of the NXdata in the entry
87 std::vector<string> nxdataname;
88 std::map<string, string> entries = handle.getEntries();
89 for (auto &entry : entries) {
90 if (entry.second == NXDATA) {
91 nxdataname.emplace_back(entry.first);
92 }
93 }
94 handle.openGroup(nxdataname.front(), NXDATA);
95
96 // reused local variable
97 NeXus::Info info;
98
99 // open NXdata section
100 handle.openData("counts");
101 info = handle.getInfo();
102 t_ntc1 = static_cast<int>(info.dims[1]);
103 t_nsp1 = static_cast<int>(info.dims[0]);
104 handle.getData(m_counts);
105 handle.closeData();
106
107 // Get groupings
108 try {
109 handle.openData("grouping");
110 info = handle.getInfo();
111 m_numDetectors = static_cast<int>(info.dims[0]);
112 handle.getData(m_detectorGroupings);
113 handle.closeData();
114 } catch (...) {
115 g_log.debug("Muon nexus file does not contain grouping info");
116 }
117
118 // read corrected time
119 handle.openData("corrected_time");
120 info = handle.getInfo();
121 handle.getData(m_correctedTimes);
122 handle.closeData();
123
124 // assume only one data set in file
125 t_nper = 1;
126 handle.closeGroup();
127
128 // get instrument name
129 handle.openGroup("instrument", "NXinstrument");
130 handle.readData("name", m_nexusInstrumentName);
131
132 // Try to read in period information
133 try {
134 handle.openGroup("beam", "NXbeam");
135 readPeriodInfo(handle);
136 handle.closeGroup();
137 } catch (...) {
138 g_log.debug("Muon nexus file does not contain beam info");
139 }
140
141 handle.closeGroup(); // Close instrument group
142
143 // Get number of switching states if available. Take this as number of periods
144 // If not available set as one period.
145 entries = handle.getEntries();
146 t_nper = 1;
147 if (std::any_of(entries.cbegin(), entries.cend(),
148 [](const auto &entry) { return entry.first == "switching_states"; })) {
149 int ssPeriods;
150 handle.readData("switching_states", ssPeriods);
151 t_nper = abs(ssPeriods);
152 // assume that number of spectra in multiperiod file should be divided by
153 // periods
154 t_nsp1 /= t_nper;
155 }
156 // file will close on leaving the function
157}
158
170void MuonNexusReader::readPeriodInfo(NeXus::File &handle) {
171 auto parsePeriod = [&handle](const std::string &logName, auto &destAttr) {
172 try {
173 std::remove_reference_t<decltype(destAttr)> tempAttr;
174 handle.readData(logName, tempAttr);
175 destAttr = tempAttr;
176 } catch (...) {
177 g_log.debug("Muon nexus file does not contain " + logName);
178 }
179 };
180
181 auto parseIntVector = [&handle](const std::string &logName, auto &destAttr) {
182 try {
183 std::vector<int> tempIntVector;
184 handle.readData(logName, tempIntVector);
185 destAttr = convertVectorToString(tempIntVector, ";");
186 } catch (...) {
187 g_log.debug("Muon nexus file does not contain " + logName);
188 }
189 };
190
191 auto parseFloatVector = [&handle](const std::string &logName, auto &destAttr) {
192 try {
193 std::vector<float> tempFloatVector;
194 handle.readData(logName, tempFloatVector);
195 destAttr = convertVectorToString(tempFloatVector, ";");
196 } catch (...) {
197 g_log.debug("Muon nexus file does not contain " + logName);
198 }
199 };
200
201 parsePeriod(PERIOD_LABELS, m_periodNames);
202 parsePeriod(PERIOD_SEQUENCES, m_numPeriodSequences);
203 parseFloatVector(TOTAL_COUNTS_PERIOD, m_periodsCounts);
204 parseIntVector(PERIOD_TYPE, m_periodTypes);
205 parseIntVector(FRAMES_PERIOD_REQUESTED, m_framesPeriodsRequested);
206 parseIntVector(FRAMES_PERIOD_RAW, m_framesPeriodsRaw);
207 parseIntVector(PERIOD_OUTPUT, m_periodsOutput);
208}
209
210// Get time boundary data as in ISISRAW. Simpler here as NeXus stores real times
211// Not clear if corrected_time is what is wanted. Assume that values are bin
212// centre
213// times and that bin boundary values are wanted, as ISISRAW.
214// @param timebnds float pointer for time values to be stored
215// @param ndnbs int count of expected points
216void MuonNexusReader::getTimeChannels(float *timebnds, const int &nbnds) const {
217 // assume constant time bin width given by difference of first two values
218 float binHalfWidth = (m_correctedTimes[1] - m_correctedTimes[0]) / float(2.0);
219 for (int i = 0; i < nbnds - 1; i++)
220 timebnds[i] = m_correctedTimes[i] - binHalfWidth;
221 timebnds[nbnds - 1] = timebnds[nbnds - 2] + float(2.0) * binHalfWidth;
222}
223
225
226// NeXus Muon file reader for NXlog data.
227// Read the given Nexus file into temp storage.
228//
229// Expected content of Nexus file is:
230// NXentry: "run" (or any name, ignored at present)
231// Zero or more NXlog entries which are of the form: <time>,<value>
232// <time> is 32bit float time wrt start_time and <value> either 32bit
233// float
234// or string.
235//
236// @param filename :: name of existing NeXus Muon file to read
237void MuonNexusReader::readLogData(const string &filename) {
238 // reset the count of logs
239 m_nexusLogCount = 0;
240
241 NeXus::File handle(filename, NXACC_READ);
242 openFirstNXentry(handle);
243
244 // read nexus fields at this level looking for NXlog and loading these into
245 // memory
246 // Also get the start_time string needed to change these times into ISO times
247 std::map<string, string> entries = handle.getEntries();
248 for (auto &entrie : entries) {
249 string nxname = entrie.first;
250 string nxclass = entrie.second;
251
252 if (nxclass == NXLOG) {
253 handle.openGroup(nxname, nxclass);
254
255 if (readMuonLogData(handle)) {
257 }
258
259 handle.closeGroup();
260 }
261 if (nxclass == "NXSample" || nxclass == "NXsample") // NXSample should be NXsample
262 {
263 handle.openGroup(nxname, nxclass);
264 handle.readData("name", m_nexusSampleName);
265 handle.closeGroup();
266 }
267 if (nxname == START_TIME) {
268 handle.readData(START_TIME, m_startTime);
269 if ((m_startTime.find('T')) != string::npos)
270 m_startTime.replace(m_startTime.find('T'), 1, " ");
271 boost::posix_time::ptime pt = boost::posix_time::time_from_string(m_startTime);
273 }
274 }
275
276 // file will close on leaving the function
277}
278
279bool MuonNexusReader::readMuonLogData(NeXus::File &handle) {
280 const string NAME("name");
281 const string VALUES("values");
282 const string TIME("time");
283
284 // read name of Log data
285 string dataName;
286 handle.readData(NAME, dataName);
287
288 // read data values
289 try {
290 handle.openData(VALUES);
291 } catch (NeXus::Exception &) {
292 g_log.warning() << "No " << VALUES << " set in " << handle.getPath() << "\n";
293 return false;
294 }
295
296 std::vector<float> values;
297 std::vector<std::string> stringValues;
298 bool isNumeric(false);
299 std::string units = "";
300
301 NeXus::Info info = handle.getInfo();
302 if (info.type == NX_FLOAT32 && info.dims.size() == 1) {
303 isNumeric = true;
304 boost::scoped_array<float> dataVals(new float[info.dims[0]]);
305 handle.getAttr("units", units);
306 handle.getData(dataVals.get());
307 values.assign(dataVals.get(), dataVals.get() + info.dims[0]);
308 stringValues.resize(info.dims[0]); // Leave empty
309 } else if (info.type == NX_CHAR && info.dims.size() == 2) {
310 boost::scoped_array<char> dataVals(new char[info.dims[0] * info.dims[1] + 1]);
311 handle.getAttr("units", units);
312 handle.getData(dataVals.get());
313 dataVals[info.dims[0] * info.dims[1]] = 0;
314 for (int i = 0; i < info.dims[0]; ++i) {
315 std::string str(&dataVals[i * info.dims[1]], &dataVals[(i + 1) * info.dims[1]]);
316 stringValues.emplace_back(str);
317 }
318 values.resize(info.dims[0]); // Leave empty
319 } else {
320 // Leave both empty
321 values.resize(info.dims[0]);
322 stringValues.resize(info.dims[0]);
323 }
324 handle.closeData();
325
326 // read time values
327 try {
328 handle.openData(TIME);
329 } catch (NeXus::Exception &) {
330 g_log.warning() << "No " << TIME << " set in " << handle.getPath() << "\n";
331 return false;
332 }
333
334 info = handle.getInfo();
335 boost::scoped_array<float> timeVals(new float[info.dims[0]]);
336 if (info.type == NX_FLOAT32 && info.dims.size() == 1) {
337 handle.getData(timeVals.get());
338 } else {
339 throw std::runtime_error("Error in MuonNexusReader: expected float array for log times");
340 }
341 handle.closeData();
342
343 // Add loaded values to vectors
344
345 m_logNames.emplace_back(dataName);
346
347 std::vector<float> tmp(timeVals.get(), timeVals.get() + info.dims[0]);
348 m_logTimes.emplace_back(tmp);
349 m_logUnits.emplace_back(units);
350 m_logType.emplace_back(isNumeric);
351 m_logValues.emplace_back(values);
352 m_logStringValues.emplace_back(stringValues);
353
354 return true;
355}
356
357void MuonNexusReader::getLogValues(const int &logNumber, const int &logSequence, std::time_t &logTime, double &value) {
358 // for the given log find the logTime and value at given sequence in log
359 double time = m_logTimes[logNumber][logSequence];
360 // boost::posix_time::ptime pt=boost::posix_time::time_from_string(startTime);
361 // std::time_t atime=to_time_t(pt);
362 // atime+=time;
363 logTime = static_cast<std::time_t>(time) + m_startTime_time_t;
364 // DateAndTime="2008-08-12T09:00:01"; //test
365 value = m_logValues[logNumber][logSequence];
366}
367
368void MuonNexusReader::getLogStringValues(const int &logNumber, const int &logSequence, std::time_t &logTime,
369 string &value) {
370 // for the given log find the logTime and value at given sequence in log
371 const double time = m_logTimes[logNumber][logSequence];
372 logTime = static_cast<std::time_t>(time) + m_startTime_time_t;
373 const std::vector<string> &strings = m_logStringValues[logNumber];
374 if (logSequence < int(strings.size())) {
375 value = strings[logSequence];
376 } else {
377 value = "";
378 }
379}
380
382
383int MuonNexusReader::getLogLength(const int i) const { return (static_cast<int>(m_logTimes[i].size())); }
384
385std::string MuonNexusReader::logUnits(const int i) const { return (m_logUnits[i]); }
386
387bool MuonNexusReader::logTypeNumeric(const int i) const { return (m_logType[i]); }
388
393string MuonNexusReader::getLogName(const int i) const { return (m_logNames[i]); }
gsl_vector * tmp
double value
The value of the point.
Definition: FitMW.cpp:51
The Logger class is in charge of the publishing messages from the framework through various channels.
Definition: Logger.h:52
void debug(const std::string &msg)
Logs at debug level.
Definition: Logger.cpp:114
void warning(const std::string &msg)
Logs at warning level.
Definition: Logger.cpp:86
std::string m_framesPeriodsRaw
std::vector< std::string > m_logNames
stores name read from file
std::vector< std::vector< std::string > > m_logStringValues
array of string values for i'th NXlog section
std::string m_periodsOutput
int numberOfLogs() const
Number of NXlog sections read from file.
std::string m_nexusSampleName
sample name read from Nexus
std::string m_framesPeriodsRequested
std::vector< std::vector< float > > m_logTimes
arrys of times for i'th NXlog section
std::time_t to_time_t(const boost::posix_time::ptime &t)
std::string logUnits(const int i) const
std::string m_periodNames
int m_nexusLogCount
number of NXlog sections read from file
bool readMuonLogData(NeXus::File &handle)
method to read the fields of open NXlog section
std::time_t m_startTime_time_t
file to base all NXlog times on
std::vector< float > m_correctedTimes
temp store for corrected times
void readLogData(const std::string &filename)
read log data
void getTimeChannels(float *timebnds, const int &nbnds) const
get time bin boundaries return sample name
std::vector< bool > m_logType
true if i'th log is numeric
int t_nper
number of periods in file (=1 at present)
void readFromFile(const std::string &filename)
read histogram data
int getLogLength(const int i) const
Lenght of i'th log.
std::vector< int > m_detectorGroupings
detector grouping info
bool logTypeNumeric(const int i) const
true if i'th log is of numeric type
std::string m_periodTypes
void openFirstNXentry(NeXus::File &handle)
Open the first NXentry of the supplied nexus file.
std::vector< std::string > m_logUnits
std::string m_nexusInstrumentName
name read from nexus file
std::string m_startTime
string startTime which must be read from Nexus
int t_ntc1
number of time channels in time regime 1
void readPeriodInfo(NeXus::File &handle)
Try to read in vairous peices of period information.
int t_nsp1
number of spectra in time regime 1
void getLogValues(const int &logNumber, const int &logSequence, std::time_t &logTime, double &value)
get logSequence pair of logNumber log
std::string getInstrumentName() const
return instrument name
std::string m_periodsCounts
std::vector< int > m_counts
temp store of histogram data
int m_numDetectors
detector count
std::string getLogName(const int i) const
Name of i'th log.
std::vector< std::vector< float > > m_logValues
array of values for i'th NXlog section
void getLogStringValues(const int &logNumber, const int &logSequence, std::time_t &logTime, std::string &value)
get logSequence pair of logNumber string log
Kernel::Logger g_log("ExperimentInfo")
static logger object
Helper class which provides the Collimation Length for SANS instruments.
std::string to_string(const wide_integer< Bits, Signed > &n)