Newer
Older
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
#include "MantidDataHandling/LoadInstrument.h"
Dickon Champion
committed
#include "MantidAPI/Instrument.h"
#include "MantidAPI/InstrumentDataService.h"
Roman Tolchenov
committed
#include "MantidAPI/XMLlogfile.h"
#include "MantidAPI/Progress.h"
#include "MantidGeometry/Instrument/Detector.h"
#include "MantidGeometry/Rendering/vtkGeometryCacheReader.h"
#include "MantidGeometry/Rendering/vtkGeometryCacheWriter.h"
Anders Markvardsen
committed
#include "MantidKernel/PhysicalConstants.h"
Gigg, Martyn Anthony
committed
#include "MantidKernel/FileProperty.h"
Anders Markvardsen
committed
#include "MantidKernel/Interpolation.h"
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"
#include "MantidKernel/ArrayProperty.h"
Anders Markvardsen
committed
#include <sstream>
Anders Markvardsen
committed
using Poco::XML::DOMParser;
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;
/// Empty default constructor
Russell Taylor
committed
LoadInstrument::LoadInstrument() : Algorithm(), m_haveDefaultFacing(false), m_deltaOffsets(false)
/// 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
Steve Williams
committed
declareProperty(
new WorkspaceProperty<MatrixWorkspace>("Workspace","Anonymous",Direction::InOut),
"The name of the workspace to load the instrument definition into" );
Gigg, Martyn Anthony
committed
std::vector<std::string> exts;
exts.push_back("XML");
exts.push_back("xml");
Gigg, Martyn Anthony
committed
declareProperty(new FileProperty("Filename","", FileProperty::Load, exts),
Russell Taylor
committed
"The filename (including its full or relative path) of an instrument\n"
"definition file");
declareProperty("MonitorList", std::vector<int>(), new NullValidator< std::vector<int> >,
"List of detector ids of monitors loaded in to the workspace", Direction::Output);
}
/** 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
Roman Tolchenov
committed
const MatrixWorkspace_sptr localWorkspace = getProperty("Workspace");
Anders Markvardsen
committed
Russell Taylor
committed
// Clear off any existing instrument for this workspace
localWorkspace->setInstrument(boost::shared_ptr<Instrument>(new Instrument));
Russell Taylor
committed
// Remove the path from the filename for use with the InstrumentDataService
const int stripPath = m_filename.find_last_of("\\/");
std::string instrumentFile = m_filename.substr(stripPath+1,m_filename.size());
Anders Markvardsen
committed
// Check whether the instrument is already in the InstrumentDataService
if ( InstrumentDataService::Instance().doesExist(instrumentFile) )
{
// If it does, just use the one from the one stored there
localWorkspace->setInstrument(InstrumentDataService::Instance().retrieve(instrumentFile));
// Get reference to Instrument
m_instrument = localWorkspace->getBaseInstrument();
//get list of monitors and set the property
std::vector<int>monitordetIdList=m_instrument->getMonitors();
setProperty("MonitorList",monitordetIdList);
return;
}
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(...)
{
Anders Markvardsen
committed
g_log.error("Unable to parse file " + m_filename);
throw Kernel::Exception::FileError("Unable to parse File:" , m_filename);
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
}
Russell Taylor
committed
// Check whether spherical coordinates should be treated as offsets to parents position
std::string offsets;
Element* offsetElement = pRootElem->getChildElement("defaults")->getChildElement("offsets");
if (offsetElement) offsets = offsetElement->getAttribute("spherical");
if ( offsets == "delta" ) m_deltaOffsets = true;
Anders Markvardsen
committed
Anders Markvardsen
committed
// Check whether default facing is set
Element* defaultFacingElement = pRootElem->getChildElement("defaults")->getChildElement("components-are-facing");
Russell Taylor
committed
if (defaultFacingElement)
Anders Markvardsen
committed
{
m_haveDefaultFacing = true;
Anders Markvardsen
committed
m_defaultFacing = parseFacingElementToV3D(defaultFacingElement);
Anders Markvardsen
committed
}
Russell Taylor
committed
// create maps: isTypeAssembly and mapTypeNameToShape
Geometry::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");
Anders Markvardsen
committed
// Each type in the IDF must be uniquely named, hence return error if type
// has already been defined
if ( getTypeElement.find(typeName) != getTypeElement.end() )
{
g_log.error("XML file: " + m_filename + "contains more than one type element named " + typeName);
throw Kernel::Exception::InstrumentDefinitionError("XML instrument file contains more than one type element named " + typeName, m_filename);
}
Anders Markvardsen
committed
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)
{
Russell Taylor
committed
isTypeAssembly[typeName] = false;
// for now try to create a geometry shape associated with every type
Anders Markvardsen
committed
// that does not contain any component elements
mapTypeNameToShape[typeName] = shapeCreator.createShape(pTypeElem);
}
Anders Markvardsen
committed
else
Russell Taylor
committed
isTypeAssembly[typeName] = true;
Anders Markvardsen
committed
pNL_local->release();
}
pNL_type->release();
Anders Markvardsen
committed
// create hasParameterElement
NodeList* pNL_parameter = pRootElem->getElementsByTagName("parameter");
unsigned int numParameter = pNL_parameter->length();
for (unsigned int i = 0; i < numParameter; i++)
{
Element* pParameterElem = static_cast<Element*>(pNL_parameter->item(i));
hasParameterElement.push_back( static_cast<Element*>(pParameterElem->parentNode()) );
}
pNL_parameter->release();
Anders Markvardsen
committed
// Get reference to Instrument and set its name
m_instrument = localWorkspace->getBaseInstrument();
Anders Markvardsen
committed
// We don't want the name taken out of the XML file itself, it should come from the filename
// Strip off "_Definition.xml"
const size_t underScore = instrumentFile.find_first_of("_");
m_instrument->setName( instrumentFile.substr(0,underScore) );
Anders Markvardsen
committed
Anders Markvardsen
committed
// See if any parameters set at instrument level
setLogfile(m_instrument.get(), pRootElem);
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();
API::Progress prog(this,0,1,pNL_comp_length);
Russell Taylor
committed
for (unsigned int i = 0; i < pNL_comp_length; i++)
prog.report();
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
Anders Markvardsen
committed
// read detertor IDs into idlist if required
if ( pElem->hasAttribute("idlist") )
{
std::string idlist = pElem->getAttribute("idlist");
Element* pFound = pDoc->getElementById(idlist, "idname");
populateIdList(pFound, idList);
}
Anders Markvardsen
committed
Anders Markvardsen
committed
if ( isAssembly(pElem->getAttribute("type")) )
{
Russell Taylor
committed
for (unsigned int i_loc = 0; i_loc < pNL_location_length; i_loc++)
Anders Markvardsen
committed
{
Russell Taylor
committed
appendAssembly(m_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
{
Anders Markvardsen
committed
std::stringstream ss1, ss2;
ss1 << idList.vec.size(); ss2 << idList.counted;
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(
Anders Markvardsen
committed
"Number of IDs listed in idlist (=" + ss1.str() + ") does not match number of detectors listed in type = "
+ pElem->getAttribute("type") + " (=" + ss2.str() + ")", m_filename);
Anders Markvardsen
committed
}
}
else
{
for (unsigned int i_loc = 0; i_loc < pNL_location_length; i_loc++)
Anders Markvardsen
committed
{
appendLeaf(m_instrument, static_cast<Element*>(pNL_location->item(i_loc)), idList);
}
Anders Markvardsen
committed
}
std::vector<int>monitordetIdList=m_instrument->getMonitors();
setProperty("MonitorList",monitordetIdList);
Anders Markvardsen
committed
pNL_location->release();
Anders Markvardsen
committed
pNL_comp->release();
Russell Taylor
committed
// Don't need this anymore (if it was even used) so empty it out to save memory
m_tempPosHolder.clear();
Anders Markvardsen
committed
Anders Markvardsen
committed
pDoc->release();
Russell Taylor
committed
std::string cacheFilename(m_filename.begin(),m_filename.end()-3);
cacheFilename += "vtp";
// check for the geometry cache
Poco::File defFile(m_filename);
Poco::File vtkFile(cacheFilename);
Russell Taylor
committed
bool cacheAvailable = true;
if ((!vtkFile.exists()) || defFile.getLastModified() > vtkFile.getLastModified())
cacheAvailable = false;
if (cacheAvailable)
Russell Taylor
committed
g_log.information("Loading geometry cache from " + cacheFilename);
// create a vtk reader
std::map<std::string, boost::shared_ptr<Geometry::Object> >::iterator objItr;
boost::shared_ptr<Mantid::Geometry::vtkGeometryCacheReader> reader(
new Mantid::Geometry::vtkGeometryCacheReader(cacheFilename));
for (objItr = mapTypeNameToShape.begin(); objItr != mapTypeNameToShape.end(); objItr++)
{
((*objItr).second)->setVtkGeometryCacheReader(reader);
}
Russell Taylor
committed
g_log.information("Geometry cache is not available");
// create a vtk writer
std::map<std::string, boost::shared_ptr<Geometry::Object> >::iterator objItr;
boost::shared_ptr<Mantid::Geometry::vtkGeometryCacheWriter> writer(
new Mantid::Geometry::vtkGeometryCacheWriter(cacheFilename));
for (objItr = mapTypeNameToShape.begin(); objItr != mapTypeNameToShape.end(); objItr++)
{
((*objItr).second)->setVtkGeometryCacheWriter(writer);
}
writer->write();
Anders Markvardsen
committed
// populate parameter map of workspace
localWorkspace->populateInstrumentParameters();
// Add the instrument to the InstrumentDataService
InstrumentDataService::Instance().add(instrumentFile,m_instrument);
Anders Markvardsen
committed
}
Roman Tolchenov
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 CompAssembly passed as the 1st arg. Note this method may call
% itself, i.e. it may act recursively.
*
* @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
* @param idList The current IDList
*/
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
Anders Markvardsen
committed
// set location for this newly added comp and set facing if specified in instrument def. file. Also
// check if any logfiles are referred to through the <parameter> element.
Anders Markvardsen
committed
Anders Markvardsen
committed
setLocation(ass, pLocElem);
Anders Markvardsen
committed
setFacing(ass, pLocElem);
Anders Markvardsen
committed
setLogfile(ass, pCompElem);
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");
Russell Taylor
committed
if ( isAssembly(typeName) )
Anders Markvardsen
committed
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
Roman Tolchenov
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).
*
* @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
* @param idList The current IDList
*
* @throw InstrumentDefinitionError Thrown if issues with the content of XML instrument file
*/
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
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
// set location for this newly added comp and set facing if specified in instrument def. file. Also
// check if any logfiles are referred to through the <parameter> element.
Russell Taylor
committed
setLocation(detector, pLocElem);
Anders Markvardsen
committed
setFacing(detector, pLocElem);
Anders Markvardsen
committed
setLogfile(detector, pCompElem);
Russell Taylor
committed
Anders Markvardsen
committed
try
Russell Taylor
committed
{
Anders Markvardsen
committed
if ( pCompElem->hasAttribute("mark-as") || pLocElem->hasAttribute("mark-as") )
Russell Taylor
committed
m_instrument->markAsMonitor(detector);
Anders Markvardsen
committed
else
Russell Taylor
committed
m_instrument->markAsDetector(detector);
Anders Markvardsen
committed
Anders Markvardsen
committed
}
catch(Kernel::Exception::ExistsError&)
{
Roman Tolchenov
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
// Add all monitors and detectors to 'facing component' container. This is only used if the
// "facing" elements are defined in the instrument definition file
m_facingComponent.push_back(detector);
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
{
Russell Taylor
committed
m_instrument->markAsSource(comp);
Anders Markvardsen
committed
}
Anders Markvardsen
committed
if ( category.compare("SamplePos") == 0 )
Anders Markvardsen
committed
{
Russell Taylor
committed
m_instrument->markAsSamplePos(comp);
Anders Markvardsen
committed
}
Anders Markvardsen
committed
// set location for this newly added comp and set facing if specified in instrument def. file. Also
// check if any logfiles are referred to through the <parameter> element.
Anders Markvardsen
committed
Anders Markvardsen
committed
setLocation(comp, pLocElem);
Anders Markvardsen
committed
setFacing(comp, pLocElem);
Anders Markvardsen
committed
setLogfile(comp, pCompElem);
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
Anders Markvardsen
committed
Roman Tolchenov
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
*
* @throw logic_error Thrown if second argument is not a pointer to a 'location' XML element
*/
void LoadInstrument::setLocation(Geometry::Component* comp, Poco::XML::Element* pElem)
Anders Markvardsen
committed
{
// 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." );
}
Russell Taylor
committed
// Polar coordinates can be labelled as (r,t,p) or (R,theta,phi)
if ( pElem->hasAttribute("r") || pElem->hasAttribute("t") || pElem->hasAttribute("p") ||
pElem->hasAttribute("R") || pElem->hasAttribute("theta") || pElem->hasAttribute("phi") )
Anders Markvardsen
committed
{
double R=0.0, theta=0.0, phi=0.0;
Russell Taylor
committed
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());
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
Russell Taylor
committed
if ( m_deltaOffsets )
{
Russell Taylor
committed
// In this case, locations given are radial offsets to the (radial) position of the parent,
Russell Taylor
committed
// so need to do some extra calculation before they're stored internally as x,y,z offsets.
Anders Markvardsen
committed
Russell Taylor
committed
// Temporary vector to hold the parent's absolute position (will be 0,0,0 if no parent)
Russell Taylor
committed
Geometry::V3D parentPos;
Russell Taylor
committed
// Get the parent's absolute position (if the component has a parent)
Russell Taylor
committed
if ( comp->getParent() )
{
SphVec parent = m_tempPosHolder[comp->getParent().get()];
Russell Taylor
committed
// Add to the current component to get its absolute position
R += parent.r;
theta += parent.theta;
phi += parent.phi;
// Set the temporary V3D with the parent's absolute position
parentPos.spherical(parent.r,parent.theta,parent.phi);
Russell Taylor
committed
}
Russell Taylor
committed
// Create a temporary vector that holds the absolute r,theta,phi position
// Needed to make things work in situation when a parent object has a phi value but a theta of zero
SphVec tmp(R,theta,phi);
// Add it to the map with the pointer to the Component object as key
m_tempPosHolder[comp] = tmp;
Russell Taylor
committed
// Create a V3D and set its position to be the child's absolute position
Russell Taylor
committed
Geometry::V3D absPos;
absPos.spherical(R,theta,phi);
// Subtract the two V3D's to get what we want (child's relative position in x,y,z)
Geometry::V3D relPos;
relPos = absPos - parentPos;
comp->setPos(relPos);
}
else
{
// In this case, the value given represents a vector from the parent to the child
Geometry::V3D pos;
pos.spherical(R,theta,phi);
comp->setPos(pos);
}
Anders Markvardsen
committed
}
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
// Rotate coordinate system of this component
if ( pElem->hasAttribute("rot") )
{
double rotAngle = atof( (pElem->getAttribute("rot")).c_str() ); // assumed to be in degrees
double axis_x = 0.0;
double axis_y = 0.0;
double axis_z = 1.0;
if ( pElem->hasAttribute("axis-x") )
axis_x = atof( (pElem->getAttribute("axis-x")).c_str() );
if ( pElem->hasAttribute("axis-y") )
axis_y = atof( (pElem->getAttribute("axis-y")).c_str() );
if ( pElem->hasAttribute("axis-z") )
axis_z = atof( (pElem->getAttribute("axis-z")).c_str() );
comp->rotate(Geometry::Quat(rotAngle, Geometry::V3D(axis_x,axis_y,axis_z)));
}
Anders Markvardsen
committed
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
// loop recursively to see if location element containes (further) rotation instructions
bool stillRotationElement = true;
while ( stillRotationElement )
{
Element* rotElement = pElem->getChildElement("rot");
if (rotElement)
{
double rotAngle = atof( (rotElement->getAttribute("val")).c_str() ); // assumed to be in degrees
double axis_x = 0.0;
double axis_y = 0.0;
double axis_z = 1.0;
if ( rotElement->hasAttribute("axis-x") )
axis_x = atof( (rotElement->getAttribute("axis-x")).c_str() );
if ( rotElement->hasAttribute("axis-y") )
axis_y = atof( (rotElement->getAttribute("axis-y")).c_str() );
if ( rotElement->hasAttribute("axis-z") )
axis_z = atof( (rotElement->getAttribute("axis-z")).c_str() );
comp->rotate(Geometry::Quat(rotAngle, Geometry::V3D(axis_x,axis_y,axis_z)));
pElem = rotElement; // for recursive action
}
else
stillRotationElement = false;
}
Anders Markvardsen
committed
Roman Tolchenov
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
*
* @throw logic_error Thrown if argument is not a child of component element
*/
Anders Markvardsen
committed
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;
}
Roman Tolchenov
committed
/** Method for populating IdList.
*
* @param pE Poco::XML element that points a idlist element in the XML doc
* @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;
Russell Taylor
committed
int increment = 1;
if ( pE->hasAttribute("step") ) increment = atoi( (pE->getAttribute("step")).c_str() );
Russell Taylor
committed
for (int i = startID; i != endID+increment; i += increment)
Anders Markvardsen
committed
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;
Russell Taylor
committed
int increment = 1;
if ( pIDElem->hasAttribute("step") ) increment = atoi( (pIDElem->getAttribute("step")).c_str() );
Russell Taylor
committed
for (int i = startID; i != endID+increment; i += increment)
Anders Markvardsen
committed
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
}
Roman Tolchenov
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
Anders Markvardsen
committed
*/
Russell Taylor
committed
bool LoadInstrument::isAssembly(std::string type)
Anders Markvardsen
committed
{
std::map<std::string,bool>::iterator it;
Russell Taylor
committed
it = isTypeAssembly.find(type);
Anders Markvardsen
committed
Russell Taylor
committed
if ( it == isTypeAssembly.end() )
Anders Markvardsen
committed
{
throw Kernel::Exception::InstrumentDefinitionError("type with name = " + type +
" not defined.", m_filename);
Anders Markvardsen
committed
}
Anders Markvardsen
committed
Russell Taylor
committed
return isTypeAssembly[type];
Anders Markvardsen
committed
}
Roman Tolchenov
committed
/** Make the shape defined in 1st argument face the component in the second argument,
* by rotating the z-axis of the component passed in 1st argument so that it points in the
* direction: from the component as specified 2nd argument to the component as specified in 1st argument.
*
* @param in Component to be rotated
* @param facing Object to face
Anders Markvardsen
committed
*/
Anders Markvardsen
committed
void LoadInstrument::makeXYplaneFaceComponent(Geometry::Component* &in, const Geometry::ObjComponent* facing)
Anders Markvardsen
committed
{
Roman Tolchenov
committed
const Geometry::V3D facingPoint = facing->getPos();
makeXYplaneFaceComponent(in, facingPoint);
}
/** Make the shape defined in 1st argument face the position in the second argument,
* by rotating the z-axis of the component passed in 1st argument so that it points in the
* direction: from the position (as specified 2nd argument) to the component (1st argument).
*
* @param in Component to be rotated
* @param facingPoint position to face
Anders Markvardsen
committed
*/
void LoadInstrument::makeXYplaneFaceComponent(Geometry::Component* &in, const Geometry::V3D& facingPoint)
{
Geometry::V3D pos = in->getPos();
Anders Markvardsen
committed
Anders Markvardsen
committed
// vector from facing object to component we want to rotate
Anders Markvardsen
committed
Anders Markvardsen
committed
Geometry::V3D facingDirection = pos - facingPoint;
Anders Markvardsen
committed
facingDirection.normalize();
Anders Markvardsen
committed
Anders Markvardsen
committed
if ( facingDirection.norm() == 0.0 ) return;
Anders Markvardsen
committed
Anders Markvardsen
committed
// now aim to rotate shape such that the z-axis of of the object we want to rotate
// points in the direction of facingDirection. That way the XY plane faces the 'facing object'.
Anders Markvardsen
committed
Geometry::V3D z = Geometry::V3D(0,0,1);
Geometry::Quat R = in->getRotation();
R.inverse();
R.rotate(facingDirection);
Geometry::V3D normal = facingDirection.cross_prod(z);
Anders Markvardsen
committed
normal.normalize();
double theta = (180.0/M_PI)*facingDirection.angle(z);
Anders Markvardsen
committed
Anders Markvardsen
committed
if ( normal.norm() > 0.0 )
in->rotate(Geometry::Quat(-theta, normal));
else
{
// To take into account the case where the facing direction is in the (0,0,1)
// or (0,0,-1) direction.
in->rotate(Geometry::Quat(-theta, Geometry::V3D(0,1,0)));
}
}
Anders Markvardsen
committed
Anders Markvardsen
committed
Roman Tolchenov
committed
/** Parse position of facing element to V3D
*
* @param pElem Facing type element to parse
* @return Return parsed position as a V3D
Anders Markvardsen
committed
*/
Anders Markvardsen
committed
Geometry::V3D LoadInstrument::parseFacingElementToV3D(Poco::XML::Element* pElem)
Anders Markvardsen
committed
{
Anders Markvardsen
committed
Anders Markvardsen
committed
Geometry::V3D retV3D;
Anders Markvardsen
committed
Anders Markvardsen
committed
// Polar coordinates can be labelled as (r,t,p) or (R,theta,phi)
if ( pElem->hasAttribute("r") || pElem->hasAttribute("t") || pElem->hasAttribute("p") ||
pElem->hasAttribute("R") || pElem->hasAttribute("theta") || pElem->hasAttribute("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());
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());
Russell Taylor
committed
Anders Markvardsen
committed
retV3D.spherical(R,theta,phi);
Anders Markvardsen
committed
}
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());
retV3D(x,y,z);
Anders Markvardsen
committed
}
Anders Markvardsen
committed
return retV3D;
Anders Markvardsen
committed
}
Roman Tolchenov
committed
/** Set facing of comp as specified in XML facing element (which must be sub-element of a location element).
*
* @param comp To set facing of
* @param pElem Poco::XML element that points a location element in the XML doc
*
* @throw logic_error Thrown if second argument is not a pointer to a 'location' XML element
*/
void LoadInstrument::setFacing(Geometry::Component* comp, Poco::XML::Element* pElem)
Anders Markvardsen
committed
{
// 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." );
}
Element* facingElem = pElem->getChildElement("facing");
if (facingElem)
{
Anders Markvardsen
committed
// check if user want to rotate about z-axis before potentially applying facing
if ( facingElem->hasAttribute("rot") )
{
double rotAngle = atof( (facingElem->getAttribute("rot")).c_str() ); // assumed to be in degrees
comp->rotate(Geometry::Quat(rotAngle, Geometry::V3D(0,0,1)));
}
Anders Markvardsen
committed
// For now assume that if has val attribute it means facing = none. This option only has an
Russell Taylor
committed
// effect when a default facing setting is set. In which case this then means "ignore the
Anders Markvardsen
committed
// default facing setting" for this component
Russell Taylor
committed
if ( facingElem->hasAttribute("val") )
Anders Markvardsen
committed
return;
Anders Markvardsen
committed
// Face the component to the x,y,z or r,t,p coordinates of the facing component
makeXYplaneFaceComponent(comp, parseFacingElementToV3D(facingElem));
}
else // so if no facing element associated with location element apply default facing if set
if (m_haveDefaultFacing)
makeXYplaneFaceComponent(comp, m_defaultFacing);
}
Anders Markvardsen
committed
Anders Markvardsen
committed
/** Set parameter/logfile info (if any) associated with component
*
* @param comp Some component
* @param pElem Associated Poco::XML element to component that may hold a \<parameter\> element
Anders Markvardsen
committed
*
* @throw InstrumentDefinitionError Thrown if issues with the content of XML instrument file
*/
void LoadInstrument::setLogfile(Geometry::Component* comp, Poco::XML::Element* pElem)
{
Anders Markvardsen
committed
// check first if pElem contains any <parameter> child elements
Anders Markvardsen
committed
Anders Markvardsen
committed
if ( hasParameterElement.end() == std::find(hasParameterElement.begin(),hasParameterElement.end(),pElem) ) return;
NodeList* pNL = pElem->getElementsByTagName("parameter");
Anders Markvardsen
committed
unsigned int numberParam = pNL->length();
// Get logfile-cache from instrument
Roman Tolchenov
committed
std::multimap<std::string, boost::shared_ptr<API::XMLlogfile> >& logfileCache = m_instrument->getLogfileCache();
Anders Markvardsen
committed
for (unsigned int i = 0; i < numberParam; i++)
{
Element* pParamElem = static_cast<Element*>(pNL->item(i));
if ( !pParamElem->hasAttribute("name") )
throw Kernel::Exception::InstrumentDefinitionError("XML element with name or type = " + comp->getName() +
" contain <parameter> element with no name attribute in XML instrument file", m_filename);
Anders Markvardsen
committed
std::string paramName = pParamElem->getAttribute("name");
if ( paramName.compare("rot")==0 || paramName.compare("pos")==0 )
{
g_log.error() << "XML element with name or type = " << comp->getName() <<
" contains <parameter> element with name=\"" << paramName << "\"." <<
" This is a reserved Mantid keyword. Please use other name, " <<
"and see www.mantidproject.org/InstrumentDefinitionFile for list of reserved keywords." <<
" This parameter is ignored";
continue;
}
Anders Markvardsen
committed