MuonNexusReader.cpp 12.5 KB
Newer Older
1
2
3
// Mantid Repository : https://github.com/mantidproject/mantid
//
// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4
5
//   NScD Oak Ridge National Laboratory, European Spallation Source,
//   Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6
// SPDX - License - Identifier: GPL - 3.0 +
7
#include "MantidNexus/MuonNexusReader.h"
LamarMoore's avatar
LamarMoore committed
8
#include "MantidKernel/System.h"
9
#include <boost/scoped_array.hpp>
10
#include <nexus/NeXusException.hpp>
LamarMoore's avatar
LamarMoore committed
11
12
#include <sstream>
#include <vector>
13

14
15
16
17
18
19
20
21
22
23
24
using std::string;

namespace { // anonymous namespace to keep things in the file
///< Type for NXdata.
const string NXDATA("NXdata");
///< Type for NXentry.
const string NXENTRY("NXentry");
///< Type for NXlog.
const string NXLOG("NXlog");
///< Special string for start time
const string START_TIME("start_time");
Gigg, Martyn Anthony's avatar
Gigg, Martyn Anthony committed
25
26
27

/// logger
Mantid::Kernel::Logger g_log("MuonNexusReader");
LamarMoore's avatar
LamarMoore committed
28
} // namespace
29

30
31
using namespace Mantid;

32
/// Default constructor
33
MuonNexusReader::MuonNexusReader()
Samuel Jones's avatar
Samuel Jones committed
34
35
    : nexus_instrument_name(), nexus_samplename(), nexusLogCount(0), startTime_time_t(), t_nsp1(0), t_ntc1(0),
      t_nper(0), corrected_times(nullptr), counts(nullptr), detectorGroupings(nullptr), numDetectors(0) {}
36
37

/// Destructor deletes temp storage
38
MuonNexusReader::~MuonNexusReader() {
39
40
  delete[] corrected_times;
  delete[] counts;
41
  delete[] detectorGroupings;
42
}
43
44
45
46
47
/**
 * Open the first NXentry of the supplied nexus file.
 *
 * @param handle Object to work on.
 */
48
void MuonNexusReader::openFirstNXentry(NeXus::File &handle) {
49
  std::map<string, string> entries = handle.getEntries();
50
  const auto entry =
Samuel Jones's avatar
Samuel Jones committed
51
      std::find_if(entries.cbegin(), entries.cend(), [](const auto entry) { return entry.second == NXENTRY; });
52
  if (entry == entries.cend())
53
    throw std::runtime_error("Failed to find NXentry");
54
  handle.openGroup(entry->first, NXENTRY);
55
}
56

57
58
59
60
// Basic NeXus Muon file reader - simple version based on contents of test
// files.
// Read the given Nexus file into temp storage. Following the approach of
// ISISRAW
61
// which does not use namespace.
62
63
// This reader is only used by LoadMuonNexus - the NexusProcessed files are
// dealt with by
64
65
66
67
68
69
70
71
// NexusFileIO.cpp
//
// Expected content of Nexus file is:
//     Entry: "run" (first entry opened, whatever name is)
//       Group: "histogram_data_1" (first NXdata section read, whatever name is)
//         Data: "counts"  (2D integer array)
//         Data: "corrected time" (1D float array)
//
72
// @param filename ::  name of existing NeXus Muon file to read
73
void MuonNexusReader::readFromFile(const string &filename) {
74
75
76
77
78
79
  NeXus::File handle(filename, NXACC_READ);
  openFirstNXentry(handle);

  // find all of the NXdata in the entry
  std::vector<string> nxdataname;
  std::map<string, string> entries = handle.getEntries();
80
81
  for (auto &entry : entries) {
    if (entry.second == NXDATA) {
82
      nxdataname.emplace_back(entry.first);
83
    }
84
  }
85
86
87
88
89
  handle.openGroup(nxdataname.front(), NXDATA);

  // reused local variable
  NeXus::Info info;

90
  // open NXdata section
91
92
93
94
  handle.openData("counts");
  info = handle.getInfo();
  t_ntc1 = static_cast<int>(info.dims[1]);
  t_nsp1 = static_cast<int>(info.dims[0]);
95
  counts = new int[t_ntc1 * t_nsp1];
96
97
98
  handle.getData(counts);
  handle.closeData();

99
100
  // Get groupings
  try {
101
102
103
104
105
106
    handle.openData("grouping");
    info = handle.getInfo();
    numDetectors = static_cast<int>(info.dims[0]);
    detectorGroupings = new int[numDetectors];
    handle.getData(detectorGroupings);
    handle.closeData();
107
  } catch (...) {
108
109
    g_log.debug("Muon nexus file does not contain grouping info");
  }
110

111
  // read corrected time
112
113
114
115
116
117
  handle.openData("corrected_time");
  info = handle.getInfo();
  corrected_times = new float[info.dims[0]];
  handle.getData(corrected_times);
  handle.closeData();

118
  // assume only one data set in file
119
  t_nper = 1;
120
121
  handle.closeGroup();

122
  // get instrument name
123
  handle.openGroup("instrument", "NXinstrument");
124
  handle.readData("name", nexus_instrument_name);
125
126

  // Try to read in period information
Matt Cumber's avatar
Matt Cumber committed
127
  std::vector<std::string> periodInformation{
128
129
      "period_sequences",  "period_labels",       "period_type",  "frames_period_requested",
      "frames_period_raw", "total_counts_period", "period_output"};
130
131
132
133
134
  try {
    handle.openGroup("beam", "NXbeam");
    for (const auto &info : periodInformation) {
      try {
        std::string tempString;
Matt Cumber's avatar
Matt Cumber committed
135
        if (info == "period_sequences") { // Int data
136
137
138
          int temp = 0;
          handle.readData(info, temp);
          tempString = std::to_string(temp);
139
        } else if (info == "frames_period_requested" || info == "frames_period_raw" || info == "period_type" ||
Matt Cumber's avatar
Matt Cumber committed
140
                   info == "period_output") { // Int array data
141
142
143
144
          std::vector<int> temp;
          handle.readData(info, temp);
          for (const auto &value : temp)
            tempString += std::to_string(value) + ";";
Matt Cumber's avatar
Matt Cumber committed
145
          tempString.erase(tempString.length() - 1); // Remove final ;
146
        } else if (info == "total_counts_period") {  // Float array data
Matt Cumber's avatar
Matt Cumber committed
147
148
149
150
151
          std::vector<float> temp;
          handle.readData(info, temp);
          for (const auto &value : temp)
            tempString += std::to_string(value) + ";";
          tempString.erase(tempString.length() - 1); // Remove final ;
152
        } else {                                     // String data
153
154
155
156
157
158
159
160
161
162
163
164
165
166
          handle.readData(info, tempString);
        }
        m_periodInformation.emplace_back(tempString);
      } catch (...) {
        g_log.debug("Muon nexus file does not contain " + info);
        m_periodInformation.emplace_back(std::string{""});
      }
    }
    handle.closeGroup();
  } catch (...) {
    g_log.debug("Muon nexus file does not contain beam info");
  }

  handle.closeGroup(); // Close instrument group
167

168
169
  // Get number of switching states if available. Take this as number of periods
  // If not available set as one period.
170
  entries = handle.getEntries();
171
  t_nper = 1;
Samuel Jones's avatar
Samuel Jones committed
172
173
  if (std::any_of(entries.cbegin(), entries.cend(),
                  [](const auto &entry) { return entry.first == "switching_states"; })) {
174
175
176
177
178
179
    int ssPeriods;
    handle.readData("switching_states", ssPeriods);
    t_nper = abs(ssPeriods);
    // assume that number of spectra in multiperiod file should be divided by
    // periods
    t_nsp1 /= t_nper;
180
  }
181
  // file will close on leaving the function
182
183
184
}

// Get time boundary data as in ISISRAW. Simpler here as NeXus stores real times
185
186
// Not clear if corrected_time is what is wanted. Assume that values are bin
// centre
187
188
189
// times and that bin boundary values are wanted, as ISISRAW.
// @param  timebnds  float pointer for time values to be stored
// @param  ndnbs     int count of expected points
190
void MuonNexusReader::getTimeChannels(float *timebnds, const int &nbnds) const {
191
  // assume constant time bin width given by difference of first two values
192
193
194
195
  float binHalfWidth = (corrected_times[1] - corrected_times[0]) / float(2.0);
  for (int i = 0; i < nbnds - 1; i++)
    timebnds[i] = corrected_times[i] - binHalfWidth;
  timebnds[nbnds - 1] = timebnds[nbnds - 2] + float(2.0) * binHalfWidth;
196
197
}

Samuel Jones's avatar
Samuel Jones committed
198
string MuonNexusReader::getInstrumentName() const { return (nexus_instrument_name); }
199
200
201
202
203
204
205

// NeXus Muon file reader for NXlog data.
// Read the given Nexus file into temp storage.
//
// Expected content of Nexus file is:
//     NXentry: "run" (or any name, ignored at present)
//            Zero or more NXlog entries which are of the form: <time>,<value>
206
207
//            <time> is 32bit float time wrt start_time and <value> either 32bit
//            float
Tom Perkins's avatar
Tom Perkins committed
208
//            or string.
209
//
210
// @param filename ::  name of existing NeXus Muon file to read
211
void MuonNexusReader::readLogData(const string &filename) {
212
  // reset the count of logs
213
  nexusLogCount = 0;
214
215
216
217

  NeXus::File handle(filename, NXACC_READ);
  openFirstNXentry(handle);

218
219
220
  // read nexus fields at this level looking for NXlog and loading these into
  // memory
  // Also get the start_time string needed to change these times into ISO times
221
  std::map<string, string> entries = handle.getEntries();
222
223
224
  for (auto &entrie : entries) {
    string nxname = entrie.first;
    string nxclass = entrie.second;
225

226
    if (nxclass == NXLOG) {
227
      handle.openGroup(nxname, nxclass);
228

229
      if (readMuonLogData(handle)) {
230
231
232
        nexusLogCount++;
      }

233
      handle.closeGroup();
234
    }
Samuel Jones's avatar
Samuel Jones committed
235
    if (nxclass == "NXSample" || nxclass == "NXsample") // NXSample should be NXsample
236
    {
237
238
239
      handle.openGroup(nxname, nxclass);
      handle.readData("name", nexus_samplename);
      handle.closeGroup();
240
    }
241
    if (nxname == START_TIME) {
242
      handle.readData(START_TIME, startTime);
243
244
      if ((startTime.find('T')) != string::npos)
        startTime.replace(startTime.find('T'), 1, " ");
Samuel Jones's avatar
Samuel Jones committed
245
      boost::posix_time::ptime pt = boost::posix_time::time_from_string(startTime);
246
      startTime_time_t = to_time_t(pt);
247
248
    }
  }
249

250
  // file will close on leaving the function
251
252
}

253
bool MuonNexusReader::readMuonLogData(NeXus::File &handle) {
254
255
256
257
  const string NAME("name");
  const string VALUES("values");
  const string TIME("time");

258
  // read name of Log data
259
260
261
  string dataName;
  handle.readData(NAME, dataName);

262
  // read data values
263
  try {
264
    handle.openData(VALUES);
265
  } catch (NeXus::Exception &) {
Samuel Jones's avatar
Samuel Jones committed
266
    g_log.warning() << "No " << VALUES << " set in " << handle.getPath() << "\n";
267
268
269
270
271
272
273
    return false;
  }

  std::vector<float> values;
  std::vector<std::string> stringValues;
  bool isNumeric(false);

274
  NeXus::Info info = handle.getInfo();
275
  if (info.type == NX_FLOAT32 && info.dims.size() == 1) {
276
    isNumeric = true;
277
278
    boost::scoped_array<float> dataVals(new float[info.dims[0]]);
    handle.getData(dataVals.get());
279
    values.assign(dataVals.get(), dataVals.get() + info.dims[0]);
280
    stringValues.resize(info.dims[0]); // Leave empty
281
  } else if (info.type == NX_CHAR && info.dims.size() == 2) {
Samuel Jones's avatar
Samuel Jones committed
282
    boost::scoped_array<char> dataVals(new char[info.dims[0] * info.dims[1] + 1]);
283
    handle.getData(dataVals.get());
284
285
    dataVals[info.dims[0] * info.dims[1]] = 0;
    for (int i = 0; i < info.dims[0]; ++i) {
Samuel Jones's avatar
Samuel Jones committed
286
      std::string str(&dataVals[i * info.dims[1]], &dataVals[(i + 1) * info.dims[1]]);
287
      stringValues.emplace_back(str);
288
    }
289
    values.resize(info.dims[0]); // Leave empty
290
  } else {
291
292
293
    // Leave both empty
    values.resize(info.dims[0]);
    stringValues.resize(info.dims[0]);
294
  }
295
296
  handle.closeData();

297
  // read time values
298
  try {
299
    handle.openData(TIME);
300
  } catch (NeXus::Exception &) {
301
302
303
304
    g_log.warning() << "No " << TIME << " set in " << handle.getPath() << "\n";
    return false;
  }

305
306
  info = handle.getInfo();
  boost::scoped_array<float> timeVals(new float[info.dims[0]]);
307
  if (info.type == NX_FLOAT32 && info.dims.size() == 1) {
308
    handle.getData(timeVals.get());
309
  } else {
Samuel Jones's avatar
Samuel Jones committed
310
    throw std::runtime_error("Error in MuonNexusReader: expected float array for log times");
311
  }
312
313
314
315
  handle.closeData();

  // Add loaded values to vectors

316
  logNames.emplace_back(dataName);
317

318
  std::vector<float> tmp(timeVals.get(), timeVals.get() + info.dims[0]);
319
  logTimes.emplace_back(tmp);
320

321
322
323
  logType.emplace_back(isNumeric);
  logValues.emplace_back(values);
  logStringValues.emplace_back(stringValues);
324
325

  return true;
326
}
327

Samuel Jones's avatar
Samuel Jones committed
328
void MuonNexusReader::getLogValues(const int &logNumber, const int &logSequence, std::time_t &logTime, double &value) {
329
  // for the given log find the logTime and value at given sequence in log
330
331
332
333
334
335
336
  double time = logTimes[logNumber][logSequence];
  // boost::posix_time::ptime pt=boost::posix_time::time_from_string(startTime);
  // std::time_t atime=to_time_t(pt);
  // atime+=time;
  logTime = static_cast<std::time_t>(time) + startTime_time_t;
  // DateAndTime="2008-08-12T09:00:01"; //test
  value = logValues[logNumber][logSequence];
337
}
338

Samuel Jones's avatar
Samuel Jones committed
339
340
void MuonNexusReader::getLogStringValues(const int &logNumber, const int &logSequence, std::time_t &logTime,
                                         string &value) {
341
  // for the given log find the logTime and value at given sequence in log
342
343
344
345
  double time = logTimes[logNumber][logSequence];
  logTime = static_cast<std::time_t>(time) + startTime_time_t;
  std::vector<string> &strings = logStringValues[logNumber];
  if (logSequence < int(strings.size())) {
346
    value = strings[logSequence];
347
  } else {
348
349
    value = "";
  }
350
351
}

352
int MuonNexusReader::numberOfLogs() const { return (nexusLogCount); }
353

Samuel Jones's avatar
Samuel Jones committed
354
int MuonNexusReader::getLogLength(const int i) const { return (static_cast<int>(logTimes[i].size())); }
355

356
bool MuonNexusReader::logTypeNumeric(const int i) const { return (logType[i]); }
357

358
359
360
361
/** return log name of i'th NXlog section
 * @param i :: the number of the NXlog section find name of.
 * @return the log name at the given index
 */
362
string MuonNexusReader::getLogName(const int i) const { return (logNames[i]); }
363

364
std::vector<std::string> MuonNexusReader::getPeriodInfo() const { return m_periodInformation; }