Newer
Older
// Includes
#include "MantidDataHandling/LoadTOFRawNexus.h"
Gigg, Martyn Anthony
committed
#include "MantidAPI/FileProperty.h"
#include "MantidKernel/ArrayProperty.h"
#include "MantidKernel/BoundedValidator.h"
Janik Zikovsky
committed
#include "MantidDataHandling/LoadEventNexus.h"
Janik Zikovsky
committed
#include "MantidNexusCPP/NeXusFile.hpp"
Janik Zikovsky
committed
#include "MantidAPI/MatrixWorkspace.h"
#include "MantidKernel/cow_ptr.h"
Janik Zikovsky
committed
#include <boost/algorithm/string/detail/classification.hpp>
#include <boost/algorithm/string/split.hpp>
namespace Mantid
{
namespace DataHandling
{
// Register the algorithm into the algorithm factory
Janik Zikovsky
committed
DECLARE_ALGORITHM( LoadTOFRawNexus )
using namespace Kernel;
using namespace API;
Janik Zikovsky
committed
using namespace DataObjects;
LoadTOFRawNexus::LoadTOFRawNexus()
{
}
Janik Zikovsky
committed
//-------------------------------------------------------------------------------------------------
/// Initialisation method.
void LoadTOFRawNexus::init()
{
std::vector < std::string > exts;
exts.push_back(".nxs");
declareProperty(new FileProperty("Filename", "", FileProperty::Load, exts),
"The name of the NeXus file to load");
Janik Zikovsky
committed
declareProperty(new WorkspaceProperty<MatrixWorkspace>("OutputWorkspace", "", Direction::Output));
declareProperty("Signal", 1,
"Number of the signal to load from the file. Default is 1 = time_of_flight.\n"
"Some NXS files have multiple data fields giving binning in other units (e.g. d-spacing or momentum).\n"
"Enter the right signal number for your desired field.");
Janik Zikovsky
committed
}
//-------------------------------------------------------------------------------------------------
/** Goes thoguh a histogram NXS file and counts the number of pixels.
* It also determines the name of the data field and axis to load
Janik Zikovsky
committed
*
* @param nexusfilename :: nxs file path
* @param entry_name :: name of the entry
* @param numPixels :: returns # of pixels
* @param numBins :: returns # of bins (length of Y vector, add one to get the number of X points)
* @param bankNames :: returns the list of bank names
*/
void LoadTOFRawNexus::countPixels(const std::string &nexusfilename, const std::string & entry_name,
std::vector<std::string> & bankNames)
Janik Zikovsky
committed
{
numPixels = 0;
numBins = 0;
Janik Zikovsky
committed
m_dataField = "";
m_axisField = "";
Janik Zikovsky
committed
bankNames.clear();
// Create the root Nexus class
::NeXus::File * file = new ::NeXus::File(nexusfilename);
// Open the default data group 'entry'
file->openGroup(entry_name, "NXentry");
// Look for all the banks
std::map<std::string, std::string> entries = file->getEntries();
std::map<std::string, std::string>::iterator it;
for (it = entries.begin(); it != entries.end(); it++)
{
std::string name = it->first;
if (name.size() > 4)
{
if (name.substr(0, 4) == "bank")
{
bankNames.push_back(name);
// OK, this is some bank data
file->openGroup(name, it->second);
Janik Zikovsky
committed
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
// -------------- Find the data field name ----------------------------
if (m_dataField.empty())
{
std::map<std::string, std::string> entries = file->getEntries();
std::map<std::string, std::string>::iterator it;
for (it = entries.begin(); it != entries.end(); ++it)
{
file->openData(it->first);
if (file->hasAttr("signal"))
{
int signal = 0;
file->getAttr("signal", signal);
if (signal == m_signal)
{
// That's the right signal!
m_dataField = it->first;
// Find the corresponding X axis
if (!file->hasAttr("axes"))
throw std::runtime_error("Your chosen signal number, " + Strings::toString(m_signal) + ", corresponds to the data field '" +
m_dataField + "' has no 'axes' attribute specifying.");
std::string axes;
file->getAttr("axes", axes);
std::vector<std::string> allAxes;
boost::split( allAxes, axes, boost::algorithm::detail::is_any_ofF<char>(","));
if (allAxes.size() != 3)
throw std::runtime_error("Your chosen signal number, " + Strings::toString(m_signal) + ", corresponds to the data field '" +
m_dataField + "' which has only " + Strings::toString(allAxes.size()) + " dimension. Expected 3 dimensions.");
m_axisField = allAxes.back();
g_log.information() << "Loading signal " << m_signal << ", " << m_dataField << " with axis " << m_axisField << std::endl;
file->closeData();
break;
}
}
file->closeData();
}
}
Janik Zikovsky
committed
// Count how many pixels in the bank
file->openData("pixel_id");
std::vector<int> dims = file->getInfo().dims;
file->closeData();
size_t newPixels = 1;
if (dims.size() > 0)
{
for (size_t i=0; i < dims.size(); i++)
newPixels *= dims[i];
numPixels += newPixels;
}
// Get the size of the X vector
Janik Zikovsky
committed
file->openData(m_axisField);
Janik Zikovsky
committed
dims = file->getInfo().dims;
Janik Zikovsky
committed
// Find the units, if available
if (file->hasAttr("units"))
file->getAttr("units", m_xUnits);
else
m_xUnits = "microsecond"; //use default
Janik Zikovsky
committed
file->closeData();
if (dims.size() > 0)
numBins = dims[0] - 1;
file->closeGroup();
}
}
}
file->close();
delete file;
}
/** Load a single bank into the workspace
*
* @param nexusfilename :: file to open
* @param entry_name :: NXentry name
* @param bankName :: NXdata bank name
* @param WS :: workspace to modify
* @param workspaceIndex :: workspaceIndex of the first spectrum. This will be incremented by the # of pixels in the bank.
*/
void LoadTOFRawNexus::loadBank(const std::string &nexusfilename, const std::string & entry_name,
Janik Zikovsky
committed
const std::string &bankName, Mantid::API::MatrixWorkspace_sptr WS)
Janik Zikovsky
committed
{
// Navigate to the point in the file
::NeXus::File * file = new ::NeXus::File(nexusfilename);
file->openGroup(entry_name, "NXentry");
Janik Zikovsky
committed
file->openGroup("instrument", "NXinstrument");
file->openGroup(bankName, "NXdetector");
Janik Zikovsky
committed
// Load the pixel IDs
std::vector<uint32_t> pixel_id;
file->readData("pixel_id", pixel_id);
size_t numPixels = pixel_id.size();
if (numPixels == 0)
{ file->close(); g_log.warning() << "Invalid pixel_id data in " << bankName << std::endl; return; }
// Load the TOF vector
std::vector<float> tof;
Janik Zikovsky
committed
file->readData(m_axisField, tof);
Janik Zikovsky
committed
size_t numBins = tof.size() - 1;
if (tof.size() <= 1)
Janik Zikovsky
committed
{ file->close(); g_log.warning() << "Invalid " << m_axisField << " data in " << bankName << std::endl; return; }
Janik Zikovsky
committed
// Make a shared pointer
MantidVecPtr Xptr;
MantidVec & X = Xptr.access();
X.resize( tof.size(), 0);
X.assign( tof.begin(), tof.end() );
Janik Zikovsky
committed
// Load the data. Coerce ints into double.
Janik Zikovsky
committed
std::string errorsField = "";
Janik Zikovsky
committed
std::vector<double> data;
file->openData(m_dataField);
file->getDataCoerce(data);
Janik Zikovsky
committed
if (file->hasAttr("errors"))
file->getAttr("errors", errorsField);
Janik Zikovsky
committed
file->closeData();
Janik Zikovsky
committed
// Load the errors
Janik Zikovsky
committed
bool hasErrors = !errorsField.empty();
std::vector<double> errors;
if (hasErrors)
{
Janik Zikovsky
committed
try
{
file->openData(errorsField);
file->getDataCoerce(errors);
file->closeData();
}
catch (...)
{
g_log.information() << "Error loading the errors field, '" << errorsField << "' for bank " << bankName << ". Will use sqrt(counts). " << std::endl;
hasErrors = false;
}
Janik Zikovsky
committed
}
Janik Zikovsky
committed
Janik Zikovsky
committed
if (data.size() != numBins * numPixels)
Janik Zikovsky
committed
{ file->close(); g_log.warning() << "Invalid size of '" << m_dataField << "' data in " << bankName << std::endl; return; }
Janik Zikovsky
committed
if (hasErrors && (errors.size() != numBins * numPixels))
{ file->close(); g_log.warning() << "Invalid size of '" << errorsField << "' errors in " << bankName << std::endl; return; }
Janik Zikovsky
committed
for (size_t i=0; i<numPixels; i++)
{
Janik Zikovsky
committed
// Find the workspace index for this detector
detid_t pixelID = pixel_id[i];
size_t wi = (*id_to_wi)[pixelID];
Janik Zikovsky
committed
// Set the basic info of that spectrum
ISpectrum * spec = WS->getSpectrum(wi);
Gigg, Martyn Anthony
committed
spec->setSpectrumNo( specid_t(wi+1) );
Janik Zikovsky
committed
spec->setDetectorID( pixel_id[i] );
// Set the shared X pointer
spec->setX(X);
// Extract the Y
MantidVec & Y = spec->dataY();
Y.assign( data.begin() + i * numBins, data.begin() + (i+1) * numBins );
MantidVec & E = spec->dataE();
Janik Zikovsky
committed
if (hasErrors)
{
// Copy the errors from the loaded document
E.assign( errors.begin() + i * numBins, errors.begin() + (i+1) * numBins );
}
else
{
// Now take the sqrt(Y) to give E
E = Y;
std::transform(E.begin(), E.end(), E.begin(), (double(*)(double)) sqrt);
}
Janik Zikovsky
committed
}
// Done!
file->close();
}
Janik Zikovsky
committed
//-------------------------------------------------------------------------------------------------
Janik Zikovsky
committed
/** @return the name of the entry that we will load */
std::string LoadTOFRawNexus::getEntryName(const std::string & filename)
Janik Zikovsky
committed
std::string entry_name = "entry";
::NeXus::File * file = new ::NeXus::File(filename);
std::map<std::string,std::string> entries = file->getEntries();
file->close();
Janik Zikovsky
committed
delete file;
if (entries.size() == 0)
throw std::runtime_error("No entries in the NXS file!");
// name "entry" is normal, but "entry-state0" is the name of the real state for live nexus files.
if (entries.find(entry_name) == entries.end())
entry_name = "entry-state0";
// If that doesn't exist, just take the first entry.
if (entries.find(entry_name) == entries.end())
entry_name = entries.begin()->first;
Janik Zikovsky
committed
// // Tell the user
// if (entries.size() > 1)
// g_log.notice() << "There are " << entries.size() << " NXentry's in the file. Loading entry '" << entry_name << "' only." << std::endl;
Janik Zikovsky
committed
return entry_name;
}
//-------------------------------------------------------------------------------------------------
/** 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 LoadTOFRawNexus::exec()
{
// The input properties
std::string filename = getPropertyValue("Filename");
m_signal = getProperty("Signal");
Janik Zikovsky
committed
Janik Zikovsky
committed
// Find the entry name we want.
Janik Zikovsky
committed
std::string entry_name = LoadTOFRawNexus::getEntryName(filename);
Janik Zikovsky
committed
// Count pixels and other setup
Progress * prog = new Progress(this, 0.0, 1.0, 10);
Janik Zikovsky
committed
prog->doReport("Counting pixels");
std::vector<std::string> bankNames;
Janik Zikovsky
committed
g_log.debug() << "Workspace found to have " << numPixels << " pixels and " << numBins << " bins" << std::endl;
prog->setNumSteps(bankNames.size() + 5);
prog->doReport("Creating workspace");
// Start with a dummy WS just to hold the logs and load the instrument
MatrixWorkspace_sptr WS = WorkspaceFactory::Instance().create(
"Workspace2D", numPixels, numBins+1, numBins);
// Load the logs
prog->doReport("Loading DAS logs");
LoadEventNexus::runLoadNexusLogs(filename, WS, pulseTimes, this);
// Load the instrument
prog->report("Loading instrument");
LoadEventNexus::runLoadInstrument(filename, WS, entry_name, this);
Janik Zikovsky
committed
// Load the meta data, but don't stop on errors
prog->report("Loading metadata");
Janik Zikovsky
committed
try
{ LoadEventNexus::loadEntryMetadata(filename, WS, entry_name);
}
catch (std::exception & e)
{ g_log.warning() << "Error while loading meta data: " << e.what() << std::endl; }
Janik Zikovsky
committed
// Set the spectrum number/detector ID at each spectrum. This is consistent with LoadEventNexus for non-ISIS files.
prog->report("Building Spectra Mapping");
WS->rebuildSpectraMapping(false);
// And map ID to WI
id_to_wi = WS->getDetectorIDToWorkspaceIndexMap(false);
Janik Zikovsky
committed
// Load each bank sequentially
Janik Zikovsky
committed
PARALLEL_FOR1(WS)
for (int i=0; i<int(bankNames.size()); i++)
Janik Zikovsky
committed
{
Janik Zikovsky
committed
PARALLEL_START_INTERUPT_REGION
Janik Zikovsky
committed
std::string bankName = bankNames[i];
prog->report("Loading bank " + bankName);
Janik Zikovsky
committed
loadBank(filename, entry_name, bankName, WS);
Janik Zikovsky
committed
PARALLEL_END_INTERUPT_REGION
Janik Zikovsky
committed
}
Janik Zikovsky
committed
PARALLEL_CHECK_INTERUPT_REGION
Janik Zikovsky
committed
Janik Zikovsky
committed
if (m_xUnits == "Ang")
WS->getAxis(0)->setUnit("dSpacing");
else if(m_xUnits == "invAng")
WS->getAxis(0)->setUnit("MomentumTransfer");
else
// Default to TOF for any other string
WS->getAxis(0)->setUnit("TOF");
Janik Zikovsky
committed
WS->setYUnit("Counts");
Janik Zikovsky
committed
// Method that will eventually go away.
WS->generateSpectraMap();
// Set to the output
setProperty("OutputWorkspace", WS);
Janik Zikovsky
committed
delete prog;
Janik Zikovsky
committed
delete id_to_wi;
}
} // namespace DataHandling
} // namespace Mantid