Newer
Older
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
#include "MantidDataHandling/LoadCanSAS1D.h"
Federico Montesino Pouzols
committed
#include "MantidAPI/Axis.h"
Gigg, Martyn Anthony
committed
#include "MantidAPI/FileProperty.h"
#include "MantidAPI/RegisterFileLoader.h"
Steve Williams
committed
#include "MantidAPI/WorkspaceGroup.h"
#include "MantidAPI/WorkspaceFactory.h"
Federico Montesino Pouzols
committed
#include "MantidDataObjects/Workspace2D.h"
#include "MantidKernel/UnitFactory.h"
#include "MantidKernel/ConfigService.h"
Federico Montesino Pouzols
committed
#include <Poco/AutoPtr.h>
Federico Montesino Pouzols
committed
#include <Poco/DOM/DOMParser.h>
#include <Poco/SAX/InputSource.h>
Federico Montesino Pouzols
committed
Steve Williams
committed
#include <boost/lexical_cast.hpp>
//-----------------------------------------------------------------------
using Poco::XML::DOMParser;
using Poco::XML::Document;
using Poco::XML::Element;
using Poco::XML::NodeList;
using Poco::XML::Node;
Steve Williams
committed
using namespace Mantid::Kernel;
Steve Williams
committed
using namespace Mantid::API;
using namespace Mantid::DataObjects;
Steve Williams
committed
namespace Mantid {
namespace DataHandling {
DECLARE_FILELOADER_ALGORITHM(LoadCanSAS1D)
Sofia Antony
committed
/// constructor
LoadCanSAS1D::LoadCanSAS1D() : m_groupNumber(0) {}
/// destructor
/**
* Return the confidence with with this algorithm can load the file
* @param descriptor A descriptor for the file
* @returns An integer specifying the confidence level. 0 indicates it will not
* be used
int LoadCanSAS1D::confidence(Kernel::FileDescriptor &descriptor) const {
const std::string &extn = descriptor.extension();
if (extn.compare(".xml") != 0)
return 0;
std::istream &is = descriptor.data();
Poco::XML::InputSource src(is);
// Set up the DOM parser and parse xml file
DOMParser pParser;
Poco::AutoPtr<Document> pDoc;
pDoc = pParser.parse(&src);
} catch (...) {
throw Kernel::Exception::FileError("Unable to parse File:",
descriptor.filename());
}
// Get pointer to root element
Element *pRootElem = pDoc->documentElement();
if (pRootElem) {
if (pRootElem->tagName().compare("SASroot") == 0) {
confidence = 80;
}
}
return confidence;
}
/// Overwrites Algorithm Init method.
void LoadCanSAS1D::init() {
declareProperty(
new API::FileProperty("Filename", "", API::FileProperty::Load, ".xml"),
"The name of the CanSAS1D file to load");
Steve Williams
committed
declareProperty(new WorkspaceProperty<Workspace>("OutputWorkspace", "",
Kernel::Direction::Output),
"The name to use for the output workspace");
Steve Williams
committed
/** Overwrites Algorithm exec method
* @throw FileError if the file isn't valid xml
* @throw NotFoundError if any expected elements couldn't be read
* @throw NotImplementedError if any SASentry doesn't contain exactly one run
*/
Steve Williams
committed
const std::string fileName = getPropertyValue("Filename");
// Set up the DOM parser and parse xml file
DOMParser pParser;
Poco::AutoPtr<Document> pDoc;
pDoc = pParser.parse(fileName);
Steve Williams
committed
throw Exception::FileError("Unable to parse File:", fileName);
}
// Get pointer to root element
Element *pRootElem = pDoc->documentElement();
if (!pRootElem->hasChildNodes()) {
throw Kernel::Exception::NotFoundError(
"No root element in CanSAS1D XML file", fileName);
// there can be multiple <SASentry> elements, each one contains a period which
// will go into a workspace group if there are more than one of them
Poco::AutoPtr<NodeList> entryList =
pRootElem->getElementsByTagName("SASentry");
size_t numEntries = entryList->length();
Steve Williams
committed
Workspace_sptr outputWork;
MatrixWorkspace_sptr WS;
Steve Williams
committed
std::string runName;
switch (numEntries) {
case 0:
throw Exception::NotFoundError("No <SASentry>s were found in the file",
fileName);
case 1:
// the value of the string runName is unused in this case
WS = loadEntry(entryList->item(0), runName);
WS->mutableRun().addProperty("Filename", fileName);
outputWork = WS;
break;
default:
auto group = boost::make_shared<WorkspaceGroup>();
for (unsigned int i = 0; i < numEntries; ++i) {
std::string runName;
MatrixWorkspace_sptr newWork = loadEntry(entryList->item(i), runName);
newWork->mutableRun().addProperty("Filename", fileName);
appendDataToOutput(newWork, runName, group);
}
outputWork = group;
Steve Williams
committed
}
setProperty("OutputWorkspace", outputWork);
}
/** Load an individual "<SASentry>" element into a new workspace
* @param[in] workspaceData points to a "<SASentry>" element
* @param[out] runName the name this workspace should take
* @return dataWS this workspace will be filled with data
* @throw NotFoundError if any expected elements couldn't be read
* @throw NotImplementedError if the entry doesn't contain exactly one run
*/
MatrixWorkspace_sptr
LoadCanSAS1D::loadEntry(Poco::XML::Node *const workspaceData,
std::string &runName) {
Element *workspaceElem = dynamic_cast<Element *>(workspaceData);
Steve Williams
committed
check(workspaceElem, "<SASentry>");
runName = workspaceElem->getAttribute("name");
Poco::AutoPtr<NodeList> runs = workspaceElem->getElementsByTagName("Run");
if (runs->length() != 1) {
throw Exception::NotImplementedError("<SASentry>s containing multiple "
"runs, or no runs, are not currently "
"supported");
Steve Williams
committed
}
Element *sasDataElem = workspaceElem->getChildElement("SASdata");
Steve Williams
committed
check(sasDataElem, "<SASdata>");
// getting number of Idata elements in the xml file
Poco::AutoPtr<NodeList> idataElemList =
sasDataElem->getElementsByTagName("Idata");
size_t nBins = idataElemList->length();
Steve Williams
committed
MatrixWorkspace_sptr dataWS =
WorkspaceFactory::Instance().create("Workspace2D", 1, nBins, nBins);
Steve Williams
committed
createLogs(workspaceElem, dataWS);
Steve Williams
committed
Steve Williams
committed
Element *titleElem = workspaceElem->getChildElement("Title");
check(titleElem, "<Title>");
dataWS->setTitle(titleElem->innerText());
dataWS->isDistribution(true);
dataWS->setYUnit("");
// load workspace data
MantidVec &X = dataWS->dataX(0);
MantidVec &Y = dataWS->dataY(0);
MantidVec &E = dataWS->dataE(0);
MantidVec &Dx = dataWS->dataDx(0);
int vecindex = 0;
std::string yUnit = "";
bool isCommon = true;
// iterate through each Idata element and get the values of "Q",
//"I" and "Idev" text nodes and fill X,Y,E vectors
for (unsigned long index = 0; index < nBins; ++index) {
Node *idataElem = idataElemList->item(index);
Element *elem = dynamic_cast<Element *>(idataElem);
if (elem) {
// setting X vector
std::string nodeVal;
Element *qElem = elem->getChildElement("Q");
Steve Williams
committed
check(qElem, "Q");
nodeVal = qElem->innerText();
std::stringstream x(nodeVal);
double d;
x >> d;
X[vecindex] = d;
// setting dX vector [optional]
Element *dqElem = elem->getChildElement("Qdev");
if (dqElem) {
nodeVal = dqElem->innerText();
std::stringstream dx(nodeVal);
dx >> d;
Dx[vecindex] = d;
}
// setting Y vector
Element *iElem = elem->getChildElement("I");
Steve Williams
committed
check(qElem, "I");
const std::string unit = iElem->getAttribute("unit");
if (index == 0)
yUnit = unit;
else if (unit != yUnit)
isCommon = false;
nodeVal = iElem->innerText();
std::stringstream y(nodeVal);
y >> d;
Y[vecindex] = d;
// setting the error vector
Element *idevElem = elem->getChildElement("Idev");
Steve Williams
committed
check(qElem, "Idev");
nodeVal = idevElem->innerText();
std::stringstream e(nodeVal);
e >> d;
E[vecindex] = d;
++vecindex;
}
}
Element *instrElem = workspaceElem->getChildElement("SASinstrument");
Steve Williams
committed
check(instrElem, "SASinstrument");
std::string instname;
Element *nameElem = instrElem->getChildElement("name");
Steve Williams
committed
check(nameElem, "name");
instname = nameElem->innerText();
// run load instrument
Steve Williams
committed
runLoadInstrument(instname, dataWS);
if (isCommon == true)
dataWS->setYUnitLabel(yUnit);
Steve Williams
committed
return dataWS;
}
/* This method throws not found error if a element is not found in the xml file
* @param[in] toCheck pointer to element
* @param[in] name element name
Janik Zikovsky
committed
* @throw NotFoundError if the pointer is NULL
Steve Williams
committed
*/
void LoadCanSAS1D::check(const Poco::XML::Element *const toCheck,
const std::string &name) const {
if (!toCheck) {
Steve Williams
committed
std::string fileName = getPropertyValue("Filename");
throw Kernel::Exception::NotFoundError(
"<" + name + "> element not found in CanSAS1D XML file", fileName);
Steve Williams
committed
}
}
/** Appends the first workspace to the second workspace. The second workspace
* will became a group unless
* it was initially empty, in that situation it becames a copy of the first
* workspace
Steve Williams
committed
* @param[in] newWork the new data to add
* @param[in] newWorkName the name that the new workspace will take
* @param[out] container the data will be added to this group
* @throw ExistsError if a workspace with this name had already been added
*/
void LoadCanSAS1D::appendDataToOutput(API::MatrixWorkspace_sptr newWork,
const std::string &newWorkName,
API::WorkspaceGroup_sptr container) {
// the name of the property, like the workspace name must be different for
// each workspace. Add "_run" at the end to stop problems with names like
// "outputworkspace"
std::string propName = newWorkName + "_run";
// the following code registers the workspace with the AnalysisDataService and
// with the workspace group, I'm taking this oone trust I don't know why it's
// done this way sorry, Steve
Steve Williams
committed
declareProperty(new WorkspaceProperty<MatrixWorkspace>(propName, newWorkName,
Direction::Output));
container->addWorkspace(newWork);
Steve Williams
committed
setProperty(propName, newWork);
/** Run the Child Algorithm LoadInstrument (as for LoadRaw)
Janik Zikovsky
committed
* @param inst_name :: The name written in the Nexus file
* @param localWorkspace :: The workspace to insert the instrument into
void LoadCanSAS1D::runLoadInstrument(const std::string &inst_name,
API::MatrixWorkspace_sptr localWorkspace) {
API::IAlgorithm_sptr loadInst = createChildAlgorithm("LoadInstrument");
// Now execute the Child Algorithm. Catch and log any error, but don't stop.
loadInst->setPropertyValue("InstrumentName", inst_name);
loadInst->setProperty<API::MatrixWorkspace_sptr>("Workspace",
localWorkspace);
loadInst->setProperty("RewriteSpectraMap",
Mantid::Kernel::OptionalBool(true));
loadInst->execute();
} catch (std::invalid_argument &) {
g_log.information("Invalid argument to LoadInstrument Child Algorithm");
} catch (std::runtime_error &) {
g_log.information(
"Unable to successfully run LoadInstrument Child Algorithm");
Steve Williams
committed
/** Loads data into the run log
* @param[in] sasEntry the entry corresponding to the passed workspace
* @param[in] wSpace the log will be created in this workspace
Steve Williams
committed
*/
void LoadCanSAS1D::createLogs(const Poco::XML::Element *const sasEntry,
API::MatrixWorkspace_sptr wSpace) const {
Gigg, Martyn Anthony
committed
API::Run &run = wSpace->mutableRun();
Element *runText = sasEntry->getChildElement("Run");
Steve Williams
committed
check(runText, "Run");
run.addLogData(
new PropertyWithValue<std::string>("run_number", runText->innerText()));
Steve Williams
committed
Element *process = sasEntry->getChildElement("SASprocess");
if (process) {
Poco::AutoPtr<NodeList> terms = process->getElementsByTagName("term");
for (unsigned int i = 0; i < terms->length(); ++i) {
Node *term = terms->item(i);
Element *elem = dynamic_cast<Element *>(term);
if (elem) {
Steve Williams
committed
const std::string termName = elem->getAttribute("name");
Steve Williams
committed
std::string file = elem->innerText();
run.addLogData(new PropertyWithValue<std::string>("UserFile", file));
break;
}
}
}
}
Steve Williams
committed
}