Newer
Older
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
#include "MantidDataHandling/LoadInstrument.h"
Anders Markvardsen
committed
#include "MantidDataHandling/LoadParameterFile.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/ParametrizedComponent.h"
#include "MantidGeometry/Instrument/Component.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"
Gigg, Martyn Anthony
committed
#include "MantidKernel/ConfigService.h"
Anders Markvardsen
committed
#include "MantidKernel/Interpolation.h"
Anders Markvardsen
committed
#include "MantidKernel/UnitFactory.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"
Gigg, Martyn Anthony
committed
#include "Poco/Path.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(), hasParameterElement_beenSet(false),
m_haveDefaultFacing(false), m_deltaOffsets(false)
/// Initialisation method.
void LoadInstrument::init()
{
Peterson, Peter
committed
// reset the logger's name
this->g_log.setName("DataHandling::LoadInstrument");
Peterson, Peter
committed
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" );
Peterson, Peter
committed
declareProperty(new FileProperty("Filename","", FileProperty::Load, ".xml"),
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
Steve Williams
committed
// Get reference to Instrument and set its name
m_instrument = localWorkspace->getBaseInstrument();
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
}
Lynch, Vickie
committed
// Handle used in the singleton constructor for instrument file should append the value
// of the date-time tag inside the file to determine if it is already in memory so that
// changes to the instrument file will cause file to be reloaded.
instrumentFile = instrumentFile + pRootElem->getAttribute("date");
// 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
Steve Williams
committed
readDefaults(pRootElem->getChildElement("defaults"));
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");
Anders Markvardsen
committed
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()) );
Anders Markvardsen
committed
}
pNL_parameter->release();
Anders Markvardsen
committed
hasParameterElement_beenSet = true;
Anders Markvardsen
committed
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, m_instrument->getLogfileCache());
Anders Markvardsen
committed
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 elements contained in component element
Anders Markvardsen
committed
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
{
Anders Markvardsen
committed
g_log.error(std::string("A component element must contain at least one location element") +
" even if it is just an empty location element of the form <location />");
Anders Markvardsen
committed
throw Kernel::Exception::InstrumentDefinitionError(
Anders Markvardsen
committed
std::string("A component element must contain at least one location element") +
" even if it is just an empty location element of the form <location />", m_filename);
Anders Markvardsen
committed
}
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");
if ( pFound == NULL )
{
throw Kernel::Exception::InstrumentDefinitionError(
"No <idlist> with name idname=\"" + idlist + "\" present in instrument definition file.", m_filename);
}
Anders Markvardsen
committed
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
{
Anders Markvardsen
committed
Element* pLocElem = static_cast<Element*>(pNL_location->item(i_loc));
// check if <exclude> sub-elements for this location and create new exclude list to pass on
NodeList* pNLexclude = pLocElem->getElementsByTagName("exclude");
unsigned int numberExcludeEle = pNLexclude->length();
std::vector<std::string> newExcludeList;
Gigg, Martyn Anthony
committed
for (unsigned int i = 0; i < numberExcludeEle; i++)
Anders Markvardsen
committed
{
Element* pExElem = static_cast<Element*>(pNLexclude->item(i));
if ( pExElem->hasAttribute("sub-part") )
newExcludeList.push_back(pExElem->getAttribute("sub-part"));
}
pNLexclude->release();
appendAssembly(m_instrument, pLocElem, idList, newExcludeList);
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") +
" is larger than the number of detectors listed in type = "
+ pElem->getAttribute("type"));
Anders Markvardsen
committed
throw Kernel::Exception::InstrumentDefinitionError(
"Number of IDs listed in idlist (=" + ss1.str() + ") is larger than the 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
Gigg, Martyn Anthony
committed
// If the instrument directory is writable, put them there else use temporary directory
Russell Taylor
committed
std::string cacheFilename(m_filename.begin(),m_filename.end()-3);
Gigg, Martyn Anthony
committed
Russell Taylor
committed
cacheFilename += "vtp";
// check for the geometry cache
Poco::File defFile(m_filename);
Gigg, Martyn Anthony
committed
Poco::File vtkFile(cacheFilename);
Poco::File instrDir(Poco::Path(defFile.path()).parent());
Russell Taylor
committed
bool cacheAvailable = true;
if ((!vtkFile.exists()) || defFile.getLastModified() > vtkFile.getLastModified())
Gigg, Martyn Anthony
committed
{
g_log.information() << "Cache not available at " << cacheFilename << "\n";
Russell Taylor
committed
cacheAvailable = false;
Gigg, Martyn Anthony
committed
}
std::string filestem = Poco::Path(cacheFilename).getFileName();
Poco::Path fallback_dir(Kernel::ConfigService::Instance().getTempDir());
Poco::File fallbackFile = Poco::File(fallback_dir.resolve(filestem));
if( cacheAvailable == false )
{
g_log.warning() << "Trying fallback " << fallbackFile.path() << "\n";
if ((!fallbackFile.exists()) || defFile.getLastModified() > fallbackFile.getLastModified())
{
cacheAvailable = false;
}
else
{
cacheAvailable = true;
cacheFilename = fallbackFile.path();
}
}
Russell Taylor
committed
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;
Gigg, Martyn Anthony
committed
boost::shared_ptr<Mantid::Geometry::vtkGeometryCacheReader>
Gigg, Martyn Anthony
committed
reader(new Mantid::Geometry::vtkGeometryCacheReader(cacheFilename));
Russell Taylor
committed
for (objItr = mapTypeNameToShape.begin(); objItr != mapTypeNameToShape.end(); objItr++)
{
((*objItr).second)->setVtkGeometryCacheReader(reader);
}
Russell Taylor
committed
g_log.information("Geometry cache is not available");
Gigg, Martyn Anthony
committed
try
{
if( !instrDir.canWrite() )
{
cacheFilename = fallbackFile.path();
g_log.warning() << "Instrument directory is read only, writing cache to system temp.\n";
}
}
catch(Poco::FileNotFoundException &)
{
g_log.error() << "Unable to find instrument definition while attempting to write cache.\n";
throw std::runtime_error("Unable to find instrument definition while attempting to write cache.\n");
}
g_log.information() << "Creating cache in " << cacheFilename << "\n";
Russell Taylor
committed
// create a vtk writer
std::map<std::string, boost::shared_ptr<Geometry::Object> >::iterator objItr;
Gigg, Martyn Anthony
committed
boost::shared_ptr<Mantid::Geometry::vtkGeometryCacheWriter>
Gigg, Martyn Anthony
committed
writer(new Mantid::Geometry::vtkGeometryCacheWriter(cacheFilename));
Russell Taylor
committed
for (objItr = mapTypeNameToShape.begin(); objItr != mapTypeNameToShape.end(); objItr++)
{
((*objItr).second)->setVtkGeometryCacheWriter(writer);
}
writer->write();
Anders Markvardsen
committed
Anders Markvardsen
committed
// Add/overwrite any instrument params with values specified in <component-link> XML elements
setComponentLinks(m_instrument, pRootElem);
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
// release XML document
pDoc->release();
Anders Markvardsen
committed
// check if default parameter file is also present
runLoadParameterFile();
Anders Markvardsen
committed
}
/** Reads the contents of the \<defaults\> element to set member variables,
Steve Williams
committed
* requires m_instrument to be already set
* @param defaults points to the data read from the \<defaults\> element
Steve Williams
committed
*/
void LoadInstrument::readDefaults(Poco::XML::Element* defaults)
Steve Williams
committed
{
// Check whether spherical coordinates should be treated as offsets to parents position
std::string offsets;
Element* offsetElement = defaults->getChildElement("offsets");
if (offsetElement) offsets = offsetElement->getAttribute("spherical");
if ( offsets == "delta" ) m_deltaOffsets = true;
// Check whether default facing is set
Element* defaultFacingElement = defaults->getChildElement("components-are-facing");
if (defaultFacingElement)
{
m_haveDefaultFacing = true;
m_defaultFacing = parseFacingElementToV3D(defaultFacingElement);
}
Anders Markvardsen
committed
Steve Williams
committed
// the default view is used by the instrument viewer to decide the angle to display the instrument from on start up
Element* defaultView = defaults->getChildElement("default-view");
if (defaultView)
{
m_instrument->setDefaultViewAxis(defaultView->getAttribute("axis-view"));
}
}
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
Roman Tolchenov
committed
*/
Anders Markvardsen
committed
void LoadInstrument::appendAssembly(Geometry::CompAssembly* parent, Poco::XML::Element* pLocElem, IdList& idList,
const std::vector<std::string> excludeList)
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
ass->setName( getNameOfLocationElement(pLocElem) );
Anders Markvardsen
committed
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, m_instrument->getLogfileCache()); // params specified within <component>
setLogfile(ass, pLocElem, m_instrument->getLogfileCache()); // params specified within specific <location>
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);
Anders Markvardsen
committed
// check if this location is in the exclude list
std::vector<std::string>::const_iterator it = find(excludeList.begin(), excludeList.end(), getNameOfLocationElement(pElem));
if ( it == excludeList.end() )
{
Anders Markvardsen
committed
Anders Markvardsen
committed
std::string typeName = (getParentComponent(pElem))->getAttribute("type");
Anders Markvardsen
committed
Anders Markvardsen
committed
if ( isAssembly(typeName) )
{
// check if <exclude> sub-elements for this location and create new exclude list to pass on
NodeList* pNLexclude = pElem->getElementsByTagName("exclude");
unsigned int numberExcludeEle = pNLexclude->length();
std::vector<std::string> newExcludeList;
Gigg, Martyn Anthony
committed
for (unsigned int i = 0; i < numberExcludeEle; i++)
Anders Markvardsen
committed
{
Element* pExElem = static_cast<Element*>(pNLexclude->item(i));
if ( pExElem->hasAttribute("sub-part") )
newExcludeList.push_back(pExElem->getAttribute("sub-part"));
}
pNLexclude->release();
appendAssembly(ass, pElem, idList, newExcludeList);
}
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,
const std::vector<std::string> excludeList)
Anders Markvardsen
committed
appendAssembly(parent.get(), pLocElem, idList, excludeList);
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
{
Anders Markvardsen
committed
std::string name = getNameOfLocationElement(pLocElem);
Anders Markvardsen
committed
Geometry::Detector* detector = new Geometry::Detector(name, mapTypeNameToShape[typeName], parent);
Anders Markvardsen
committed
// before setting detector ID check that the IDF satisfy the following
if (idList.counted >= static_cast<int>(idList.vec.size()) )
{
std::stringstream ss1, ss2;
ss1 << idList.vec.size(); ss2 << idList.counted;
g_log.error("The number of detector IDs listed in idlist named "
+ idList.idname + " is less then the number of detectors");
throw Kernel::Exception::InstrumentDefinitionError(
"Number of IDs listed in idlist (=" + ss1.str() + ") is less than the number of detectors.", m_filename);
}
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, m_instrument->getLogfileCache()); // params specified within <component>
setLogfile(detector, pLocElem, m_instrument->getLogfileCache()); // params specified within specific <location>
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
}
Anders Markvardsen
committed
std::string name = getNameOfLocationElement(pLocElem);
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, m_instrument->getLogfileCache()); // params specified within <component>
setLogfile(comp, pLocElem, m_instrument->getLogfileCache()); // params specified within specific <location>
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." );
}
Geometry::V3D pos; // position may be defined from values in <location> combined with values from sub-elements <trans>
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
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());
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
pos(x,y,z);
comp->setPos(pos);
}
// Check if sub-elements <trans> present - for now ignore these if m_deltaOffset = true
Element* pRecursive = pElem;
if ( !m_deltaOffsets )
{
bool stillTransElement = true;
Geometry::V3D posTrans;
while ( stillTransElement )
{
Element* tElem = pRecursive->getChildElement("trans");
if (tElem)
{
// Polar coordinates can be labelled as (r,t,p) or (R,theta,phi)
if ( tElem->hasAttribute("r") || tElem->hasAttribute("t") || tElem->hasAttribute("p") ||
tElem->hasAttribute("R") || tElem->hasAttribute("theta") || tElem->hasAttribute("phi") )
{
double R=0.0, theta=0.0, phi=0.0;
if ( tElem->hasAttribute("r") ) R = atof((tElem->getAttribute("r")).c_str());
if ( tElem->hasAttribute("t") ) theta = atof((tElem->getAttribute("t")).c_str());
if ( tElem->hasAttribute("p") ) phi = atof((tElem->getAttribute("p")).c_str());
if ( tElem->hasAttribute("R") ) R = atof((tElem->getAttribute("R")).c_str());
if ( tElem->hasAttribute("theta") ) theta = atof((tElem->getAttribute("theta")).c_str());
if ( tElem->hasAttribute("phi") ) phi = atof((tElem->getAttribute("phi")).c_str());
posTrans.spherical(R,theta,phi);
}
else
{
double x=0.0, y=0.0, z=0.0;
if ( tElem->hasAttribute("x") ) x = atof((tElem->getAttribute("x")).c_str());
if ( tElem->hasAttribute("y") ) y = atof((tElem->getAttribute("y")).c_str());
if ( tElem->hasAttribute("z") ) z = atof((tElem->getAttribute("z")).c_str());
posTrans(x,y,z);
}
pos += posTrans;
pRecursive = tElem; // for recursive action
}
else
stillTransElement = false;
}
// Set potentially updated position
comp->setPos(pos);
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
// loop recursively to see if location element containes (further) rotation instructions
Anders Markvardsen
committed
bool stillRotationElement = true;
while ( stillRotationElement )
{
Element* rotElement = pRecursive->getChildElement("rot");
Anders Markvardsen
committed
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)));
pRecursive = rotElement; // for recursive action
Anders Markvardsen
committed
}
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 to an \<idlist\>
Roman Tolchenov
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." );
}
// set name of idlist
idList.idname = pE->getAttribute("idname");
Anders Markvardsen
committed
// 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
{
// test first if any <id> elements
Anders Markvardsen
committed
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;