Newer
Older
Gigg, Martyn Anthony
committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
#include "MantidNexus/LoadRunLogs.h"
#include "MantidNexus/NeXusException.hpp"
#include "MantidKernel/TimeSeriesProperty.h"
#include "MantidAPI/FileProperty.h"
#include <cctype>
#include <Poco/Path.h>
#include <Poco/DateTimeFormatter.h>
#include <Poco/DateTimeParser.h>
#include <Poco/DateTimeFormat.h>
#include <boost/scoped_array.hpp>
namespace Mantid
{
namespace NeXus
{
// Register the algorithm into the algorithm factory
DECLARE_ALGORITHM(LoadRunLogs)
/// Sets documentation strings for this algorithm
void LoadRunLogs::initDocs()
{
this->setWikiSummary("Loads run logs (temperature, pulse charges, etc.) from a NeXus file and adds it to the run information in a [[workspace]].");
this->setOptionalMessage("Loads run logs (temperature, pulse charges, etc.) from a NeXus file and adds it to the run information in a workspace.");
}
using namespace Kernel;
using API::WorkspaceProperty;
using API::MatrixWorkspace;
using API::MatrixWorkspace_sptr;
using API::FileProperty;
using std::size_t;
/// Empty default constructor
LoadRunLogs::LoadRunLogs()
{}
/// Initialisation method.
void LoadRunLogs::init()
{
declareProperty(new WorkspaceProperty<MatrixWorkspace>("Workspace","Anonymous",Direction::InOut));
std::vector<std::string> exts;
exts.push_back(".nxs");
exts.push_back(".n*");
declareProperty(new FileProperty("Filename", "", FileProperty::Load, exts),
"The name of the Nexus file to load" );
declareProperty(new PropertyWithValue<bool>("OverwriteLogs", true, Direction::Input));
}
/** Executes the algorithm. Reading in the file and creating and populating
* the output workspace
*
* @throw Exception::FileError If the Nexus file cannot be found/opened
* @throw std::invalid_argument If the optional properties are set to invalid values
*/
void LoadRunLogs::exec()
{
std::string filename = getPropertyValue("Filename");
::NeXus::File file(filename);
MatrixWorkspace_sptr workspace = getProperty("Workspace");
// Find the root entry
try
{
file.openGroup("entry", "NXentry"); // SNS style root
}
catch(::NeXus::Exception&)
{
try
{
file.openGroup("raw_data_1", "NXentry"); //ISIS style root
}
catch(::NeXus::Exception&)
{
throw std::invalid_argument("Unknown NeXus file format found in file '" + filename + "'");
}
}
// print out the entry level fields
std::map<std::string,std::string> entries = file.getEntries();
std::map<std::string,std::string>::const_iterator iend = entries.end();
for(std::map<std::string,std::string>::const_iterator it = entries.begin();
it != iend; it++)
{
std::string entry_name(it->first);
std::string entry_class(it->second);
if( entry_name == "DASlogs" || entry_class == "IXrunlog" ||
entry_class == "IXselog" )
{
loadLogs(file, entry_name, entry_class, workspace);
}
}
file.close();
}
/**
* Load log entries from the given group
* @param file :: A reference to the NeXus file handle opened such that the
* next call can be to open the named group
* @param entry_name :: The name of the log entry
* @param_entry_class :: The class type of the log entry
* @param workspace :: A pointer to the workspace to store the logs
*/
void LoadRunLogs::loadLogs(::NeXus::File & file, const std::string & entry_name,
const std::string & entry_class,
MatrixWorkspace_sptr workspace) const
{
file.openGroup(entry_name, entry_class);
std::map<std::string,std::string> entries = file.getEntries();
std::map<std::string,std::string>::const_iterator iend = entries.end();
for(std::map<std::string,std::string>::const_iterator itr = entries.begin();
itr != iend; itr++)
{
std::string log_class = itr->second;
if( log_class == "NXlog" || entry_class == "NXpositioner" )
{
loadNXLog(file, itr->first, workspace);
}
else if( log_class == "IXseblock" )
{
loadSELog(file, itr->first, workspace);
}
}
file.closeGroup();
}
/**
* Load an NX log entry
* @param file :: A reference to the NeXus file handle opened at the parent group
* @param entry_name :: The name of the log entry
* @param workspace :: A pointer to the workspace to store the logs
*/
void LoadRunLogs::loadNXLog(::NeXus::File & file, const std::string & entry_name,
MatrixWorkspace_sptr workspace) const
{
file.openGroup(entry_name, "NXlog");
// Validate the NX log class.
std::map<std::string, std::string> entries = file.getEntries();
if ((entries.find("value") == entries.end()) ||
(entries.find("time") == entries.end()) )
{
g_log.warning() << "Invalid NXlog entry " << entry_name
<< " found. Did not contain 'value' and 'time'.\n";
file.closeGroup();
return;
}
// whether or not to overwrite logs on workspace
bool overwritelogs = this->getProperty("OverwriteLogs");
try
{
Kernel::Property *logValue = createTimeSeries(file, entry_name);
workspace->mutableRun().addProperty(logValue, overwritelogs);
}
catch(::NeXus::Exception &e)
{
g_log.warning() << "NXlog entry " << entry_name
<< " gave an error when loading:'" << e.what() << "'.\n";
}
file.closeGroup();
}
/**
* Load an SE log entry
* @param file :: A reference to the NeXus file handle opened at the parent group
* @param entry_name :: The name of the log entry
* @param workspace :: A pointer to the workspace to store the logs
*/
void LoadRunLogs::loadSELog(::NeXus::File & file, const std::string & entry_name,
MatrixWorkspace_sptr workspace) const
{
// Open the entry
file.openGroup(entry_name, "IXseblock");
std::string propName = entry_name;
if (workspace->run().hasProperty(propName))
{
propName = "selog_" + propName;
}
// There are two possible entries:
// value_log - A time series entry
// value - A single value float entry
Kernel::Property *logValue(NULL);
std::map<std::string, std::string> entries = file.getEntries();
if( entries.find("value_log") != entries.end() )
{
try
{
try
{
file.openGroup("value_log", "NXlog");
}
catch(::NeXus::Exception&)
{
file.closeGroup();
throw;
}
logValue = createTimeSeries(file, propName);
file.closeGroup();
}
catch(::NeXus::Exception& e)
{
g_log.warning() << "IXseblock entry " << entry_name << " gave an error when loading "
<< "a time series:'" << e.what() << "'.\n";
file.closeGroup();
return;
}
}
else if( entries.find("value") != entries.end() )
{
float val_array[1];
try
{
file.openData("value");
file.getData(val_array);
file.closeData();
}
catch(::NeXus::Exception& e)
{
g_log.warning() << "IXseblock entry " << entry_name << " gave an error when loading "
<< "a single value:'" << e.what() << "'.\n";
file.closeData();
file.closeGroup();
return;
}
logValue = new Kernel::PropertyWithValue<double>(propName, static_cast<double>(val_array[0]), true);
}
else
{
g_log.warning() << "IXseblock entry " << entry_name
<< " does not contain a value or value_log field, skipping entry.";
file.closeGroup();
return;
}
workspace->mutableRun().addProperty(logValue);
file.closeGroup();
}
/**
* Creates a time series property from the currently opened log entry. It is assumed to
* have been checked to have a time field and the value entry's name is given as an argument
* @param file :: A reference to the file handle
* @param prop_name :: The name of the property
* @returns A pointer to a new property containing the time series
*/
Kernel::Property * LoadRunLogs::createTimeSeries(::NeXus::File & file,
const std::string & prop_name) const
{
file.openData("time");
//----- Start time is an ISO8601 string date and time. ------
std::string start;
try
{
file.getAttr("start", start);
}
catch (::NeXus::Exception &)
{
//Some logs have "offset" instead of start
try
{
file.getAttr("offset", start);
}
catch (::NeXus::Exception &)
{
g_log.warning() << "Log entry has no start time indicated.\n";
file.closeData();
throw;
}
}
//Convert to date and time
Kernel::DateAndTime start_time = Kernel::DateAndTime(start);
std::string time_units;
file.getAttr("units", time_units);
if( time_units.find("second") != 0 && time_units != "minutes" )
{
file.closeData();
throw ::NeXus::Exception("Unsupported time unit '" + time_units + "'");
}
//--- Load the seconds into a double array ---
std::vector<double> time_double;
try
{
file.getDataCoerce(time_double);
}
catch (::NeXus::Exception &e)
{
g_log.warning() << "Log entry 's time field could not be loaded: '" << e.what() << "'.\n";
file.closeData();
throw;
}
file.closeData(); // Close time data
// Convert to seconds if needed
if( time_units == "minutes" )
{
std::transform(time_double.begin(),time_double.end(), time_double.begin(),
std::bind2nd(std::multiplies<double>(),60.0));
}
// Now the values: Could be a string, int or double
file.openData("value");
// Get the units of the property
std::string value_units("");
try
{
file.getAttr("units", value_units);
}
catch (::NeXus::Exception &)
{
//Ignore missing units field.
value_units = "";
}
// Now the actual data
::NeXus::Info info = file.getInfo();
if( file.isDataInt() ) // Int type
{
std::vector<int> values;
try
{
file.getDataCoerce(values);
file.closeData();
}
catch(::NeXus::Exception&)
{
file.closeData();
throw;
}
//Make an int TSP
TimeSeriesProperty<int> * tsp = new TimeSeriesProperty<int>(prop_name);
tsp->create(start_time, time_double, values);
tsp->setUnits(value_units);
return tsp;
}
else if( info.type == ::NeXus::CHAR )
{
std::string values;
const int item_length = info.dims[1];
try
{
const int nitems = info.dims[0];
boost::scoped_array<char> val_array(new char[nitems*item_length]);
file.getData(val_array.get());
file.closeData();
values = std::string(val_array.get());
}
catch(::NeXus::Exception&)
{
file.closeData();
throw;
}
// The string may contain non-printable (i.e. control) characters, replace these
std::replace_if(values.begin(), values.end(), iscntrl, ' ');
TimeSeriesProperty<std::string> * tsp = new TimeSeriesProperty<std::string>(prop_name);
std::vector<DateAndTime> times;
DateAndTime::createVector(start_time, time_double, times);
const size_t ntimes = times.size();
for(size_t i = 0; i < ntimes; ++i)
{
std::string value_i = std::string(values.data() + i*item_length, item_length);
tsp->addValue(times[i], value_i);
}
tsp->setUnits(value_units);
return tsp;
}
else if( info.type == ::NeXus::FLOAT32 || info.type == ::NeXus::FLOAT64 )
{
std::vector<double> values;
try
{
file.getDataCoerce(values);
file.closeData();
}
catch(::NeXus::Exception&)
{
file.closeData();
throw;
}
TimeSeriesProperty<double> * tsp = new TimeSeriesProperty<double>(prop_name);
tsp->create(start_time, time_double, values);
tsp->setUnits(value_units);
return tsp;
}
else
{
throw ::NeXus::Exception("Invalid value type for time series. Only int, double or strings are "
"supported");
}
}
} // namespace DataHandling
} // namespace Mantid