Newer
Older
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
#include "MantidDataHandling/LoadInstrument.h"
#include "MantidDataHandling/ShapeFactory.h"
Dickon Champion
committed
#include "MantidAPI/Instrument.h"
#include "MantidGeometry/Detector.h"
#include "MantidGeometry/CompAssembly.h"
#include "MantidGeometry/Component.h"
Dickon Champion
committed
#include <fstream>
Anders Markvardsen
committed
#include "Poco/DOM/DOMParser.h"
#include "Poco/DOM/Document.h"
#include "Poco/DOM/Element.h"
#include "Poco/DOM/NodeList.h"
Anders Markvardsen
committed
#include "Poco/DOM/NodeIterator.h"
#include "Poco/DOM/NodeFilter.h"
Anders Markvardsen
committed
using Poco::XML::DOMParser;
using Poco::XML::XMLReader;
using Poco::XML::Document;
using Poco::XML::Element;
using Poco::XML::Node;
using Poco::XML::NodeList;
Anders Markvardsen
committed
using Poco::XML::NodeIterator;
using Poco::XML::NodeFilter;
Anders Markvardsen
committed
Dickon Champion
committed
{
DECLARE_ALGORITHM(LoadInstrument)
using namespace Kernel;
Logger& LoadInstrument::g_log = Logger::get("LoadInstrument");
/// Empty default constructor
LoadInstrument::LoadInstrument()
{}
/// Initialisation method.
void LoadInstrument::init()
{
Russell Taylor
committed
// When used as a sub-algorithm the workspace name is not used - hence the "Anonymous" to satisfy the validator
declareProperty(new WorkspaceProperty<Workspace>("Workspace","Anonymous",Direction::InOut));
Russell Taylor
committed
declareProperty("Filename","",new MandatoryValidator<std::string>);
}
/** Executes the algorithm. Reading in the file and creating and populating
* the output workspace
Anders Markvardsen
committed
* @throw FileError Thrown if unable to parse XML file
* @throw InstrumentDefinitionError Thrown if issues with the content of XML instrument file
*/
void LoadInstrument::exec()
{
// Retrieve the filename from the properties
m_filename = getPropertyValue("Filename");
// Get the input workspace
Russell Taylor
committed
const Workspace_sptr localWorkspace = getProperty("Workspace");
Anders Markvardsen
committed
// Set up the DOM parser and parse xml file
DOMParser pParser;
Anders Markvardsen
committed
Document* pDoc;
try
Anders Markvardsen
committed
{
pDoc = pParser.parse(m_filename);
}
catch(...)
{
g_log.error("Unable to XML parse file " + m_filename);
throw Kernel::Exception::FileError("Unable to XML parse File:" , m_filename);
Anders Markvardsen
committed
}
Anders Markvardsen
committed
Anders Markvardsen
committed
// Get pointer to root element
Anders Markvardsen
committed
Element* pRootElem = pDoc->documentElement();
if ( !pRootElem->hasChildNodes() )
{
Anders Markvardsen
committed
g_log.error("XML file: " + m_filename + "contains no root element.");
throw Kernel::Exception::InstrumentDefinitionError("No root element in XML instrument file", m_filename);
Anders Markvardsen
committed
}
// create maps: isTypeAssemply and mapTypeNameToShape
ShapeFactory shapeCreator;
Anders Markvardsen
committed
NodeList* pNL_type = pRootElem->getElementsByTagName("type");
if ( pNL_type->length() == 0 )
{
Anders Markvardsen
committed
g_log.error("XML file: " + m_filename + "contains no type elements.");
throw Kernel::Exception::InstrumentDefinitionError("No type elements in XML instrument file", m_filename);
Anders Markvardsen
committed
}
Russell Taylor
committed
unsigned int numberTypes = pNL_type->length();
Anders Markvardsen
committed
for (unsigned int iType = 0; iType < numberTypes; iType++)
Anders Markvardsen
committed
Element* pTypeElem = static_cast<Element*>(pNL_type->item(iType));
std::string typeName = pTypeElem->getAttribute("name");
getTypeElement[typeName] = pTypeElem;
// identify for now a type to be an assemble by it containing elements
// with tag name 'component'
NodeList* pNL_local = pTypeElem->getElementsByTagName("component");
if (pNL_local->length() == 0)
{
Anders Markvardsen
committed
isTypeAssemply[typeName] = false;
// for now try to create a geometry shape associated with every type
// that is does not contain any component elements
mapTypeNameToShape[typeName] = shapeCreator.createShape(pTypeElem);
}
Anders Markvardsen
committed
else
isTypeAssemply[typeName] = true;
pNL_local->release();
}
pNL_type->release();
// Get reference to Instrument and set its name
if ( pRootElem->hasAttribute("name") )
Anders Markvardsen
committed
instrument->setName( pRootElem->getAttribute("name") );
Anders Markvardsen
committed
// do analysis for each top level compoment element
NodeList* pNL_comp = pRootElem->childNodes(); // here get all child nodes
Russell Taylor
committed
unsigned int pNL_comp_length = pNL_comp->length();
for (unsigned int i = 0; i < pNL_comp_length; i++)
Anders Markvardsen
committed
// we are only interest in the top level component elements hence
// the reason for the if statement below
if ( (pNL_comp->item(i))->nodeType() == Node::ELEMENT_NODE &&
((pNL_comp->item(i))->nodeName()).compare("component") == 0 )
Anders Markvardsen
committed
Element* pElem = static_cast<Element*>(pNL_comp->item(i));
IdList idList; // structure to possibly be populated with detector IDs
Anders Markvardsen
committed
Anders Markvardsen
committed
// get all location element contained in component element
NodeList* pNL_location = pElem->getElementsByTagName("location");
Russell Taylor
committed
unsigned int pNL_location_length = pNL_location->length();
if (pNL_location_length == 0)
Anders Markvardsen
committed
{
g_log.error(std::string("All component elements must contain at least one location element") +
" even it is just an empty location element of the form <location />");
throw Kernel::Exception::InstrumentDefinitionError(
std::string("All component elements must contain at least one location element") +
" even it is just an empty location element of the form <location />", m_filename);
}
Anders Markvardsen
committed
if ( isAssemply(pElem->getAttribute("type")) )
Anders Markvardsen
committed
{
Anders Markvardsen
committed
// read detertor IDs into idlist if required
Anders Markvardsen
committed
if ( pElem->hasAttribute("idlist") )
{
std::string idlist = pElem->getAttribute("idlist");
Element* pFound = pDoc->getElementById(idlist, "idname");
Anders Markvardsen
committed
populateIdList(pFound, idList);
Anders Markvardsen
committed
}
Russell Taylor
committed
for (unsigned int i_loc = 0; i_loc < pNL_location_length; i_loc++)
Anders Markvardsen
committed
{
Anders Markvardsen
committed
appendAssembly(instrument, static_cast<Element*>(pNL_location->item(i_loc)), idList);
Anders Markvardsen
committed
}
Anders Markvardsen
committed
// a check
if (idList.counted != static_cast<int>(idList.vec.size()) )
Anders Markvardsen
committed
{
g_log.error("The number of detector IDs listed in idlist named "
+ pElem->getAttribute("idlist") +
Anders Markvardsen
committed
" is not equal to the number of detectors listed in type = "
+ pElem->getAttribute("type"));
Anders Markvardsen
committed
throw Kernel::Exception::InstrumentDefinitionError(
"Number of IDs listed in idlist does not match number of detectors listed in type = "
+ pElem->getAttribute("type"), m_filename);
}
}
else
{
Russell Taylor
committed
for (unsigned int i_loc = 0; i_loc < pNL_location_length; i_loc++)
Anders Markvardsen
committed
{
Anders Markvardsen
committed
appendLeaf(instrument, static_cast<Element*>(pNL_location->item(i_loc)), idList);
Anders Markvardsen
committed
}
Anders Markvardsen
committed
}
Anders Markvardsen
committed
pNL_location->release();
Anders Markvardsen
committed
pNL_comp->release();
pDoc->release();
Anders Markvardsen
committed
return;
}
Anders Markvardsen
committed
/** Assumes second argument is a XML location element and its parent is a component element
* which is assigned to be an assemble. This method appends the parent component element of
% the location element to the CompAssemply passed as the 1st arg. Note this method may call
Anders Markvardsen
committed
% itself, i.e. it may act recursively.
Anders Markvardsen
committed
*
Anders Markvardsen
committed
* @param parent CompAssembly to append new component to
* @param pLocElem Poco::XML element that points to a location element in an instrument description XML file
Anders Markvardsen
committed
*/
Anders Markvardsen
committed
void LoadInstrument::appendAssembly(Geometry::CompAssembly* parent, Poco::XML::Element* pLocElem, IdList& idList)
Anders Markvardsen
committed
{
Anders Markvardsen
committed
// The location element is required to be a child of a component element. Get this component element
Element* pCompElem = getParentComponent(pLocElem);
Anders Markvardsen
committed
Geometry::CompAssembly *ass = new Geometry::CompAssembly;
ass->setParent(parent);
parent->add(ass);
Anders Markvardsen
committed
// set name of newly added component assembly.
if ( pLocElem->hasAttribute("name") )
ass->setName(pLocElem->getAttribute("name"));
else if ( pCompElem->hasAttribute("name") )
{
Anders Markvardsen
committed
ass->setName(pCompElem->getAttribute("name"));
}
Anders Markvardsen
committed
else
{
Anders Markvardsen
committed
ass->setName(pCompElem->getAttribute("type"));
}
Anders Markvardsen
committed
// set location for this newly added comp.
Anders Markvardsen
committed
Anders Markvardsen
committed
setLocation(ass, pLocElem);
Anders Markvardsen
committed
Anders Markvardsen
committed
// The newly added component is required to have a type. Find out what this
// type is and find all the location elements of this type. Finally loop over these
// location elements
Anders Markvardsen
committed
Element* pType = getTypeElement[pCompElem->getAttribute("type")];
Anders Markvardsen
committed
NodeIterator it(pType, NodeFilter::SHOW_ELEMENT);
Anders Markvardsen
committed
Anders Markvardsen
committed
Node* pNode = it.nextNode();
while (pNode)
Anders Markvardsen
committed
{
Anders Markvardsen
committed
if ( pNode->nodeName().compare("location")==0 )
{
Anders Markvardsen
committed
Element* pElem = static_cast<Element*>(pNode);
std::string typeName = (getParentComponent(pElem))->getAttribute("type");
if ( isAssemply(typeName) )
appendAssembly(ass, pElem, idList);
else
appendLeaf(ass, pElem, idList);
}
Anders Markvardsen
committed
pNode = it.nextNode();
Anders Markvardsen
committed
}
}
Anders Markvardsen
committed
void LoadInstrument::appendAssembly(boost::shared_ptr<Geometry::CompAssembly> parent, Poco::XML::Element* pLocElem, IdList& idList)
Anders Markvardsen
committed
appendAssembly(parent.get(), pLocElem, idList);
Anders Markvardsen
committed
Anders Markvardsen
committed
/** Assumes second argument is pointing to a leaf, which here means the location element (indirectly
* representing a component element) that contains no sub-components. This component is appended
% to the parent (1st argument).
Anders Markvardsen
committed
*
Anders Markvardsen
committed
* @param parent CompAssembly to append component to
* @param pLocElem Poco::XML element that points to the element in the XML doc we want to add
Anders Markvardsen
committed
*
* @throw InstrumentDefinitionError Thrown if issues with the content of XML instrument file
Anders Markvardsen
committed
*/
Anders Markvardsen
committed
void LoadInstrument::appendLeaf(Geometry::CompAssembly* parent, Poco::XML::Element* pLocElem, IdList& idList)
Anders Markvardsen
committed
{
Anders Markvardsen
committed
// The location element is required to be a child of a component element. Get this component element
Element* pCompElem = getParentComponent(pLocElem);
// get the type element of the component element in order to determine if the type
// belong to the catogory: "detector", "SamplePos or "Source".
std::string typeName = pCompElem->getAttribute("type");
Element* pType = getTypeElement[typeName];
Anders Markvardsen
committed
std::string category = "";
if (pType->hasAttribute("is"))
Anders Markvardsen
committed
category = pType->getAttribute("is");
// do stuff a bit differently depending on which category the type belong to
Anders Markvardsen
committed
Anders Markvardsen
committed
if ( category.compare("detector") == 0 )
Anders Markvardsen
committed
{
std::string name;
Anders Markvardsen
committed
if ( pLocElem->hasAttribute("name") )
{
name = pLocElem->getAttribute("name");
}
Anders Markvardsen
committed
else if ( pCompElem->hasAttribute("name") )
{
name = pCompElem->getAttribute("name");
}
Anders Markvardsen
committed
else
{
name = typeName;
}
Anders Markvardsen
committed
Geometry::Detector* detector = new Geometry::Detector(name, mapTypeNameToShape[typeName], parent);
Anders Markvardsen
committed
// set location for this comp
Anders Markvardsen
committed
setLocation(detector, pLocElem);
Anders Markvardsen
committed
// set detector ID and increment it. Finally add the detector to the parent
Anders Markvardsen
committed
detector->setID(idList.vec[idList.counted]);
Anders Markvardsen
committed
idList.counted++;
Russell Taylor
committed
parent->add(detector);
Anders Markvardsen
committed
try
Anders Markvardsen
committed
{
if ( pCompElem->hasAttribute("mark-as") || pLocElem->hasAttribute("mark-as") )
instrument->markAsMonitor(detector);
else
instrument->markAsDetector(detector);
Anders Markvardsen
committed
}
catch(Kernel::Exception::ExistsError&)
{
Anders Markvardsen
committed
std::stringstream convert;
Anders Markvardsen
committed
convert << detector->getID();
Anders Markvardsen
committed
throw Kernel::Exception::InstrumentDefinitionError("Detector with ID = " + convert.str() +
" present more then once in XML instrument file", m_filename);
Anders Markvardsen
committed
}
Anders Markvardsen
committed
}
std::string name;
Anders Markvardsen
committed
if ( pLocElem->hasAttribute("name") )
name = pLocElem->getAttribute("name");
Anders Markvardsen
committed
else if ( pCompElem->hasAttribute("name") )
{
name = pCompElem->getAttribute("name");
}
Anders Markvardsen
committed
else
{
name = typeName;
}
Anders Markvardsen
committed
Geometry::ObjComponent *comp = new Geometry::ObjComponent(name, mapTypeNameToShape[typeName], parent);
parent->add(comp);
Anders Markvardsen
committed
// check if special Source or SamplePos Component
Anders Markvardsen
committed
if ( category.compare("Source") == 0 )
Anders Markvardsen
committed
{
instrument->markAsSource(comp);
}
Anders Markvardsen
committed
if ( category.compare("SamplePos") == 0 )
Anders Markvardsen
committed
{
instrument->markAsSamplePos(comp);
}
Anders Markvardsen
committed
// set location for this comp
Anders Markvardsen
committed
Anders Markvardsen
committed
setLocation(comp, pLocElem);
Anders Markvardsen
committed
}
}
Anders Markvardsen
committed
void LoadInstrument::appendLeaf(boost::shared_ptr<Geometry::CompAssembly> parent, Poco::XML::Element* pLocElem, IdList& idList)
Anders Markvardsen
committed
appendLeaf(parent.get(), pLocElem, idList);
Anders Markvardsen
committed
/** Set location (position) of comp as specified in XML location element.
*
* @param comp To set position/location off
* @param pElem Poco::XML element that points a location element in the XML doc
Anders Markvardsen
committed
*
* @throw logic_error Thrown if second argument is not a pointer to a 'location' XML element
Anders Markvardsen
committed
*/
void LoadInstrument::setLocation(Geometry::Component* comp, Poco::XML::Element* pElem)
{
// Require that pElem points to an element with tag name 'location'
if ( (pElem->tagName()).compare("location") )
{
g_log.error("Second argument to function setLocation must be a pointer to an XML element with tag name location.");
throw std::logic_error( "Second argument to function setLocation must be a pointer to an XML element with tag name location." );
}
if ( pElem->hasAttribute("R") || pElem->hasAttribute("theta") || pElem->hasAttribute("phi") )
Anders Markvardsen
committed
{
double R=0.0, theta=0.0, phi=0.0;
if ( pElem->hasAttribute("R") ) R = atof((pElem->getAttribute("R")).c_str());
if ( pElem->hasAttribute("theta") ) theta = atof((pElem->getAttribute("theta")).c_str());
if ( pElem->hasAttribute("phi") ) phi = atof((pElem->getAttribute("phi")).c_str());
Anders Markvardsen
committed
Geometry::V3D pos;
pos.spherical(R,theta,phi);
comp->setPos(pos);
}
else if ( pElem->hasAttribute("r") || pElem->hasAttribute("t") ||
pElem->hasAttribute("p") )
Anders Markvardsen
committed
// This is alternative way a user may specify sphecical coordinates
// which may be preferred in the long run to the more verbose of
Anders Markvardsen
committed
// using R, theta and phi.
{
double R=0.0, theta=0.0, phi=0.0;
if ( pElem->hasAttribute("r") )
R = atof((pElem->getAttribute("r")).c_str());
if ( pElem->hasAttribute("t") )
theta = atof((pElem->getAttribute("t")).c_str());
if ( pElem->hasAttribute("p") )
phi = atof((pElem->getAttribute("p")).c_str());
Anders Markvardsen
committed
Geometry::V3D pos;
pos.spherical(R,theta,phi);
comp->setPos(pos);
}
Anders Markvardsen
committed
else
{
double x=0.0, y=0.0, z=0.0;
if ( pElem->hasAttribute("x") ) x = atof((pElem->getAttribute("x")).c_str());
if ( pElem->hasAttribute("y") ) y = atof((pElem->getAttribute("y")).c_str());
if ( pElem->hasAttribute("z") ) z = atof((pElem->getAttribute("z")).c_str());
Anders Markvardsen
committed
comp->setPos(Geometry::V3D(x,y,z));
Anders Markvardsen
committed
/** Get parent component element of location element.
*
* @param pLocElem Poco::XML element that points a location element in the XML doc
* @return Parent XML element to a location XML element
Anders Markvardsen
committed
*
* @throw logic_error Thrown if argument is not a child of component element
*/
Poco::XML::Element* LoadInstrument::getParentComponent(Poco::XML::Element* pLocElem)
{
if ( (pLocElem->tagName()).compare("location") )
{
g_log.error("Argument to function getParentComponent must be a pointer to an XML element with tag name location.");
throw std::logic_error( "Argument to function getParentComponent must be a pointer to an XML element with tag name location." );
}
// The location element is required to be a child of a component element. Get this component element
Node* pCompNode = pLocElem->parentNode();
Anders Markvardsen
committed
Element* pCompElem;
if (pCompNode->nodeType() == 1)
{
pCompElem = static_cast<Element*>(pCompNode);
if ( (pCompElem->tagName()).compare("component") )
{
g_log.error("Argument to function getParentComponent must be a XML element sitting inside a component element.");
throw std::logic_error( "Argument to function getParentComponent must be a XML element sitting inside a component element." );
Anders Markvardsen
committed
}
}
else
{
g_log.error("Argument to function getParentComponent must be a XML element whos parent is an element.");
throw std::logic_error( "Argument to function getParentComponent must be a XML element whos parent is an element." );
Anders Markvardsen
committed
}
return pCompElem;
}
Anders Markvardsen
committed
/** Method for populating IdList.
*
* @param pE Poco::XML element that points a idlist element in the XML doc
Anders Markvardsen
committed
* @param idList The structure to populate with detector ID numbers
*
* @throw logic_error Thrown if argument is not a child of component element
* @throw InstrumentDefinitionError Thrown if issues with the content of XML instrument file
*/
void LoadInstrument::populateIdList(Poco::XML::Element* pE, IdList& idList)
Anders Markvardsen
committed
{
if ( (pE->tagName()).compare("idlist") )
{
g_log.error("Argument to function createIdList must be a pointer to an XML element with tag name idlist.");
throw std::logic_error( "Argument to function createIdList must be a pointer to an XML element with tag name idlist." );
}
// If idname element has start and end attributes then just use those to populate idlist.
// Otherwise id sub-elements
if ( pE->hasAttribute("start") )
{
int startID = atoi( (pE->getAttribute("start")).c_str() );
Anders Markvardsen
committed
int endID;
if ( pE->hasAttribute("end") )
endID = atoi( (pE->getAttribute("end")).c_str() );
Anders Markvardsen
committed
else
endID = startID;
for (int i = startID; i <= endID; i++)
idList.vec.push_back(i);
}
else
{
Anders Markvardsen
committed
// test first if any id elements
Anders Markvardsen
committed
NodeList* pNL = pE->getElementsByTagName("id");
if ( pNL->length() == 0 )
{
throw Kernel::Exception::InstrumentDefinitionError("No id subelement of idlist element in XML instrument file", m_filename);
Anders Markvardsen
committed
}
Anders Markvardsen
committed
pNL->release();
Anders Markvardsen
committed
Anders Markvardsen
committed
// get id numbers
Anders Markvardsen
committed
Anders Markvardsen
committed
NodeIterator it(pE, NodeFilter::SHOW_ELEMENT);
Node* pNode = it.nextNode();
while (pNode)
{
if ( pNode->nodeName().compare("id")==0 )
Anders Markvardsen
committed
{
Anders Markvardsen
committed
Element* pIDElem = static_cast<Element*>(pNode);
if ( pIDElem->hasAttribute("val") )
{
int valID = atoi( (pIDElem->getAttribute("val")).c_str() );
Anders Markvardsen
committed
idList.vec.push_back(valID);
}
else if ( pIDElem->hasAttribute("start") )
{
int startID = atoi( (pIDElem->getAttribute("start")).c_str() );
Anders Markvardsen
committed
Anders Markvardsen
committed
int endID;
if ( pIDElem->hasAttribute("end") )
endID = atoi( (pIDElem->getAttribute("end")).c_str() );
Anders Markvardsen
committed
else
endID = startID;
for (int i = startID; i <= endID; i++)
idList.vec.push_back(i);
}
else
{
throw Kernel::Exception::InstrumentDefinitionError("id subelement of idlist " +
std::string("element wrongly specified in XML instrument file"), m_filename);
}
Anders Markvardsen
committed
}
Anders Markvardsen
committed
pNode = it.nextNode();
} // end while loop
Anders Markvardsen
committed
}
Anders Markvardsen
committed
}
/** Method for populating IdList.
*
* @param type name of the type of a component in XML instrument definition
*
* @throw InstrumentDefinitionError Thrown if type not defined in XML definition
*/
bool LoadInstrument::isAssemply(std::string type)
{
std::map<std::string,bool>::iterator it;
it = isTypeAssemply.find(type);
if ( it == isTypeAssemply.end() )
{
throw Kernel::Exception::InstrumentDefinitionError("type with name = " + type +
" not defined.", m_filename);
Anders Markvardsen
committed
}
Anders Markvardsen
committed
Anders Markvardsen
committed
return isTypeAssemply[type];
Anders Markvardsen
committed
}