Newer
Older
Janik Zikovsky
committed
/*WIKI*
*WIKI*/
// Includes
Gigg, Martyn Anthony
committed
#include "MantidAPI/FileProperty.h"
Janik Zikovsky
committed
#include "MantidAPI/LoadAlgorithmFactory.h"
#include "MantidAPI/MatrixWorkspace.h"
#include "MantidDataHandling/LoadEventNexus.h"
#include "MantidDataHandling/LoadTOFRawNexus.h"
#include "MantidKernel/ArrayProperty.h"
#include "MantidKernel/BoundedValidator.h"
Janik Zikovsky
committed
#include "MantidKernel/cow_ptr.h"
#include "nexus/NeXusFile.hpp"
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 )
Janik Zikovsky
committed
DECLARE_LOADALGORITHM(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
}
Janik Zikovsky
committed
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
//-------------------------------------------------------------------------------------------------
/**
* Do a quick file type check by looking at the first 100 bytes of the file
* @param filePath :: path of the file including name.
* @param nread :: no.of bytes read
* @param header :: The first 100 bytes of the file as a union
* @return true if the given file is of type which can be loaded by this algorithm
*/
bool LoadTOFRawNexus::quickFileCheck(const std::string& filePath,size_t nread, const file_header& header)
{
std::string ext = this->extension(filePath);
// If the extension is nxs then give it a go
if( ext.compare("nxs") == 0 ) return true;
// If not then let's see if it is a HDF file by checking for the magic cookie
if ( nread >= sizeof(int32_t) && (ntohl(header.four_bytes) == g_hdf_cookie) ) return true;
return false;
}
//-------------------------------------------------------------------------------------------------
/**
* Checks the file by opening it and reading few lines
* @param filePath :: name of the file inluding its path
* @return an integer value how much this algorithm can load the file
*/
int LoadTOFRawNexus::fileCheck(const std::string& filePath)
{
int confidence(0);
typedef std::map<std::string,std::string> string_map_t;
bool hasEventData = false;
bool hasEntry = false;
bool hasData = false;
try
{
string_map_t::const_iterator it;
::NeXus::File file = ::NeXus::File(filePath);
string_map_t entries = file.getEntries();
for(string_map_t::const_iterator it = entries.begin(); it != entries.end(); ++it)
{
if ( ((it->first == "entry") || (it->first == "entry-state0") || (it->first == "raw_data_1")) && (it->second == "NXentry") )
{
// Has an entry - is ok sign
hasEntry = true;
file.openGroup(it->first, it->second);
string_map_t entries2 = file.getEntries();
for(string_map_t::const_iterator it2 = entries2.begin(); it2 != entries2.end(); ++it2)
{
if (it2->second == "NXevent_data")
hasEventData = true;
if (it2->second == "NXdata")
hasData = true;
}
file.closeGroup();
}
}
}
catch(::NeXus::Exception&)
{
}
if (hasEntry)
{
if (hasData && hasEventData)
// Event data = this is event NXS
confidence = 20;
else if (hasData && !hasEventData)
// No event data = this is the one
confidence = 80;
else
// No data ?
confidence = 10;
}
else
confidence = 0;
return confidence;
}
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");
Janik Zikovsky
committed
// Also pop into the instrument
file->openGroup("instrument", "NXinstrument");
Janik Zikovsky
committed
// 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")
{
// OK, this is some bank data
file->openGroup(name, it->second);
Janik Zikovsky
committed
// -------------- 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)
{
Janik Zikovsky
committed
if (it->second == "SDS")
Janik Zikovsky
committed
{
Janik Zikovsky
committed
file->openData(it->first);
if (file->hasAttr("signal"))
Janik Zikovsky
committed
{
Janik Zikovsky
committed
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
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;
} // Data has a 'signal' attribute
}// Yes, it is a data field
file->closeData();
} // each entry in the group
Janik Zikovsky
committed
}
}
Janik Zikovsky
committed
file->closeGroup();
} // bankX name
}
} // each entry
Janik Zikovsky
committed
Janik Zikovsky
committed
if (m_dataField.empty())
throw std::runtime_error("Your chosen signal number, " + Strings::toString(m_signal) + ", was not found in any of the data fields of any 'bankX' group. Cannot load file.");
Janik Zikovsky
committed
Janik Zikovsky
committed
for (it = entries.begin(); it != entries.end(); it++)
{
std::string name = it->first;
if (name.size() > 4)
{
if (name.substr(0, 4) == "bank")
{
// OK, this is some bank data
file->openGroup(name, it->second);
std::map<std::string, std::string> entries = file->getEntries();
Janik Zikovsky
committed
Janik Zikovsky
committed
if (entries.find("pixel_id") != entries.end())
Janik Zikovsky
committed
{
Janik Zikovsky
committed
bankNames.push_back(name);
// Count how many pixels in the bank
file->openData("pixel_id");
std::vector<int64_t> dims = file->getInfo().dims;
Janik Zikovsky
committed
file->closeData();
size_t newPixels = 1;
if (dims.size() > 0)
{
for (size_t i=0; i < dims.size(); i++)
newPixels *= dims[i];
numPixels += newPixels;
}
Janik Zikovsky
committed
}
Janik Zikovsky
committed
if (entries.find(m_axisField) != entries.end())
{
// Get the size of the X vector
file->openData(m_axisField);
std::vector<int64_t> 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
file->closeData();
if (dims.size() > 0)
numBins = dims[0] - 1;
}
Janik Zikovsky
committed
file->closeGroup();
Janik Zikovsky
committed
} // bankX name
Janik Zikovsky
committed
}
Janik Zikovsky
committed
} // each entry
Janik Zikovsky
committed
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
{
Janik Zikovsky
committed
g_log.debug() << "Loading bank " << bankName << std::endl;
Janik Zikovsky
committed
// To avoid segfaults on RHEL5/6 and Fedora
m_fileMutex.lock();
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)
Janik Zikovsky
committed
{ file->close(); m_fileMutex.unlock(); g_log.warning() << "Invalid pixel_id data in " << bankName << std::endl; return; }
Janik Zikovsky
committed
// 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(); m_fileMutex.unlock(); 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(); m_fileMutex.unlock(); g_log.warning() << "Invalid size of '" << m_dataField << "' data in " << bankName << std::endl; return; }
Janik Zikovsky
committed
if (hasErrors && (errors.size() != numBins * numPixels))
Janik Zikovsky
committed
{ file->close(); m_fileMutex.unlock(); g_log.warning() << "Invalid size of '" << errorsField << "' errors in " << bankName << std::endl; return; }
// Have all the data I need
m_fileMutex.unlock();
file->close();
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!
}
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)
Janik Zikovsky
committed
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