Newer
Older
Gigg, Martyn Anthony
committed
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
#include "MantidDataHandling/LoadNexusLogs.h"
Janik Zikovsky
committed
#include "MantidNexusCPP/NeXusException.hpp"
Gigg, Martyn Anthony
committed
#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>
Janik Zikovsky
committed
#include "MantidDataHandling/LoadTOFRawNexus.h"
Gigg, Martyn Anthony
committed
namespace Mantid
{
namespace DataHandling
Gigg, Martyn Anthony
committed
{
// Register the algorithm into the algorithm factory
Gigg, Martyn Anthony
committed
DECLARE_ALGORITHM(LoadNexusLogs)
Gigg, Martyn Anthony
committed
/// Sets documentation strings for this algorithm
Gigg, Martyn Anthony
committed
void LoadNexusLogs::initDocs()
Gigg, Martyn Anthony
committed
{
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
Gigg, Martyn Anthony
committed
LoadNexusLogs::LoadNexusLogs()
Gigg, Martyn Anthony
committed
{}
/// Initialisation method.
Gigg, Martyn Anthony
committed
void LoadNexusLogs::init()
Gigg, Martyn Anthony
committed
{
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
*/
Gigg, Martyn Anthony
committed
void LoadNexusLogs::exec()
Gigg, Martyn Anthony
committed
{
std::string filename = getPropertyValue("Filename");
MatrixWorkspace_sptr workspace = getProperty("Workspace");
Janik Zikovsky
committed
// Find the entry name to use (normally "entry" for SNS, "raw_data_1" for ISIS)
std::string entry_name = LoadTOFRawNexus::getEntryName(filename);
::NeXus::File file(filename);
Gigg, Martyn Anthony
committed
// Find the root entry
try
{
Janik Zikovsky
committed
file.openGroup(entry_name, "NXentry");
Gigg, Martyn Anthony
committed
}
catch(::NeXus::Exception&)
{
Janik Zikovsky
committed
throw std::invalid_argument("Unknown NeXus file format found in file '" + filename + "'");
Gigg, Martyn Anthony
committed
}
Janik Zikovsky
committed
// print out the entry level fields
Gigg, Martyn Anthony
committed
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++)
{
Janik Zikovsky
committed
std::string group_name(it->first);
std::string group_class(it->second);
if( group_name == "DASlogs" || group_class == "IXrunlog" ||
group_class == "IXselog" )
Gigg, Martyn Anthony
committed
{
Janik Zikovsky
committed
loadLogs(file, group_name, group_class, workspace);
Gigg, Martyn Anthony
committed
}
}
file.close();
Gigg, Martyn Anthony
committed
if( !workspace->run().hasProperty("gd_prtn_chrg") )
{
try
{
//Use the DAS logs to integrate the proton charge (if any).
workspace->mutableRun().integrateProtonCharge();
}
catch (Exception::NotFoundError &)
{
//Ignore not found property error.
}
}
Gigg, Martyn Anthony
committed
}
/**
* 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
*/
Gigg, Martyn Anthony
committed
void LoadNexusLogs::loadLogs(::NeXus::File & file, const std::string & entry_name,
Gigg, Martyn Anthony
committed
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;
Gigg, Martyn Anthony
committed
if( log_class == "NXlog" || log_class == "NXpositioner" )
Gigg, Martyn Anthony
committed
{
Gigg, Martyn Anthony
committed
loadNXLog(file, itr->first, log_class, workspace);
Gigg, Martyn Anthony
committed
}
else if( log_class == "IXseblock" )
{
loadSELog(file, itr->first, workspace);
}
}
file.closeGroup();
}
/**
Gigg, Martyn Anthony
committed
* Load an NX log entry a group type that has value and time entries.
Gigg, Martyn Anthony
committed
* @param file :: A reference to the NeXus file handle opened at the parent group
* @param entry_name :: The name of the log entry
Gigg, Martyn Anthony
committed
* @param entry_class :: The type of the entry
Gigg, Martyn Anthony
committed
* @param workspace :: A pointer to the workspace to store the logs
*/
Gigg, Martyn Anthony
committed
void LoadNexusLogs::loadNXLog(::NeXus::File & file, const std::string & entry_name,
Gigg, Martyn Anthony
committed
const std::string & entry_class,
MatrixWorkspace_sptr workspace) const
Gigg, Martyn Anthony
committed
{
Gigg, Martyn Anthony
committed
file.openGroup(entry_name, entry_class);
Gigg, Martyn Anthony
committed
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
// 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
*/
Gigg, Martyn Anthony
committed
void LoadNexusLogs::loadSELog(::NeXus::File & file, const std::string & entry_name,
Gigg, Martyn Anthony
committed
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:
Gigg, Martyn Anthony
committed
// value_log - A time series entry. This can contain a corrupt value entry so if it does use the value one
Gigg, Martyn Anthony
committed
// 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)
{
Gigg, Martyn Anthony
committed
g_log.warning() << "IXseblock entry '" << entry_name << "' gave an error when loading "
<< "a time series:'" << e.what() << "'. Skipping entry\n";
file.closeGroup(); //value_log
file.closeGroup();//entry_name
Gigg, Martyn Anthony
committed
return;
}
}
else if( entries.find("value") != entries.end() )
{
try
{
Gigg, Martyn Anthony
committed
// This may have a larger dimension than 1 bit it has no time field so take the first entry
Gigg, Martyn Anthony
committed
file.openData("value");
Gigg, Martyn Anthony
committed
::NeXus::Info info = file.getInfo();
Gigg, Martyn Anthony
committed
if( info.type == ::NeXus::FLOAT32 )
{
boost::scoped_array<float> value(new float[info.dims[0]]);
file.getData(value.get());
file.closeData();
logValue = new Kernel::PropertyWithValue<double>(propName, static_cast<double>(value[0]), true);
}
else
{
file.closeGroup();
return;
}
Gigg, Martyn Anthony
committed
}
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;
}
}
else
{
g_log.warning() << "IXseblock entry " << entry_name
Gigg, Martyn Anthony
committed
<< " cannot be read, skipping entry.\n";
Gigg, Martyn Anthony
committed
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
*/
Gigg, Martyn Anthony
committed
Kernel::Property * LoadNexusLogs::createTimeSeries(::NeXus::File & file,
Gigg, Martyn Anthony
committed
const std::string & prop_name) const
Gigg, Martyn Anthony
committed
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
{
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();
Gigg, Martyn Anthony
committed
// Check the size
Janik Zikovsky
committed
if( size_t(info.dims[0]) != time_double.size() )
Gigg, Martyn Anthony
committed
{
file.closeData();
throw ::NeXus::Exception("Invalid value entry for time series");
}
Gigg, Martyn Anthony
committed
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
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];
Gigg, Martyn Anthony
committed
const int total_length = nitems*item_length;
boost::scoped_array<char> val_array(new char[total_length]);
Gigg, Martyn Anthony
committed
file.getData(val_array.get());
file.closeData();
Gigg, Martyn Anthony
committed
values = std::string(val_array.get(), total_length);
Gigg, Martyn Anthony
committed
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
}
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