Newer
Older
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
#include "MantidDataHandling/LoadInstrument.h"
#include "MantidDataHandling/LoadInstCompsIntoOneShape.h"
Anders Markvardsen
committed
#include "MantidDataHandling/LoadParameterFile.h"
Russell Taylor
committed
#include "MantidGeometry/Instrument.h"
#include "MantidAPI/InstrumentDataService.h"
Gigg, Martyn Anthony
committed
#include "MantidGeometry/Instrument/XMLlogfile.h"
#include "MantidAPI/Progress.h"
#include "MantidGeometry/Instrument/RectangularDetector.h"
Roman Tolchenov
committed
#include "MantidGeometry/Instrument/ObjCompAssembly.h"
#include "MantidGeometry/Rendering/vtkGeometryCacheReader.h"
#include "MantidGeometry/Rendering/vtkGeometryCacheWriter.h"
Anders Markvardsen
committed
#include "MantidKernel/PhysicalConstants.h"
Gigg, Martyn Anthony
committed
#include "MantidAPI/FileProperty.h"
Gigg, Martyn Anthony
committed
#include "MantidKernel/ConfigService.h"
Anders Markvardsen
committed
#include "MantidKernel/Interpolation.h"
Anders Markvardsen
committed
#include "MantidKernel/UnitFactory.h"
#include "MantidKernel/DateAndTime.h"
#include "MantidDataHandling/LoadInstrumentHelper.h"
#include "MantidKernel/ArrayProperty.h"
#include <Poco/DOM/DOMParser.h>
#include <Poco/DOM/Document.h>
#include <Poco/DOM/Element.h>
#include <Poco/DOM/NodeList.h>
#include <Poco/DOM/NodeIterator.h>
#include <Poco/DOM/NodeFilter.h>
#include <Poco/File.h>
#include <Poco/Path.h>
Anders Markvardsen
committed
#include <Poco/Exception.h>
Anders Markvardsen
committed
#include <sstream>
#include <fstream>
#include <iostream>
Anders Markvardsen
committed
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
/// For debugging...
Janik Zikovsky
committed
static bool VERBOSE = false;
Anders Markvardsen
committed
Michael Whitty
committed
namespace DataHandling
{
Anders Markvardsen
committed
Michael Whitty
committed
DECLARE_ALGORITHM(LoadInstrument)
Janik Zikovsky
committed
/// Sets documentation strings for this algorithm
void LoadInstrument::initDocs()
{
this->setWikiSummary(" Loads an Instrument Definition File ([[InstrumentDefinitionFile|IDF]]) into a [[workspace]]. After the IDF has been read this algorithm will attempt to run the sub-algorithm [[LoadParameterFile]]; where if IDF filename is of the form IDENTIFIER_Definition.xml then the instrument parameters in the file named IDENTIFIER_Parameters.xml would be loaded (in the directory specified by the parameterDefinition.directory [[Properties_File|Mantid property]]). ");
this->setOptionalMessage("Loads an Instrument Definition File (IDF) into a workspace. After the IDF has been read this algorithm will attempt to run the sub-algorithm LoadParameterFile; where if IDF filename is of the form IDENTIFIER_Definition.xml then the instrument parameters in the file named IDENTIFIER_Parameters.xml would be loaded (in the directory specified by the parameterDefinition.directory Mantid property).");
}
Russell Taylor
committed
Michael Whitty
committed
using namespace Kernel;
using namespace API;
Gigg, Martyn Anthony
committed
using namespace Geometry;
Anders Markvardsen
committed
Michael Whitty
committed
/// Empty default constructor
LoadInstrument::LoadInstrument() : Algorithm(), hasParameterElement_beenSet(false),
Russell Taylor
committed
m_haveDefaultFacing(false), m_deltaOffsets(false), m_angleConvertConst(1.0),
m_indirectPositions(false)
Michael Whitty
committed
{}
Janik Zikovsky
committed
//------------------------------------------------------------------------------------------------------------------------------
Michael Whitty
committed
/// Initialisation method.
void LoadInstrument::init()
Anders Markvardsen
committed
{
Michael Whitty
committed
// When used as a sub-algorithm the workspace name is not used - hence the "Anonymous" to satisfy the validator
declareProperty(
new WorkspaceProperty<MatrixWorkspace>("Workspace","Anonymous",Direction::InOut),
"The name of the workspace to load the instrument definition into" );
declareProperty(new FileProperty("Filename","", FileProperty::OptionalLoad, ".xml"),
Michael Whitty
committed
"The filename (including its full or relative path) of an instrument\n"
"definition file");
declareProperty(new ArrayProperty<detid_t>("MonitorList"),
"List of detector ids of monitors loaded in to the workspace");
declareProperty( "InstrumentName", "",
Anders Markvardsen
committed
"Name of instrument. Can be used instead of Filename to specify an IDF" );
Gigg, Martyn Anthony
committed
declareProperty("RewriteSpectraMap", true, "If true then the spectra-detector mapping "
"for the input workspace will be overwritten with a 1:1 map of spectrum "
"number to detector ID");
declareProperty("XMLText", "",
"Optional: Enter the full XML contents of the Instrument Definition File, instead of a"
Janik Zikovsky
committed
"loading it from the file. You still need to specify the instrument name.");
Anders Markvardsen
committed
}
Anders Markvardsen
committed
Janik Zikovsky
committed
Janik Zikovsky
committed
/** Execute the algorithm with the parameters set (either manually or from inputs) */
void LoadInstrument::execManually()
{
Michael Whitty
committed
// Retrieve the filename from the properties
Janik Zikovsky
committed
if ( m_filename.empty() && m_xmlText.empty() )
{
// look to see if an Instrument name provided in which case create
// IDF filename on the fly
Janik Zikovsky
committed
if ( m_instName.empty() )
{
g_log.error("Either the InstrumentName or Filename property of LoadInstrument most be specified");
throw Kernel::Exception::FileError("Either the InstrumentName or Filename property of LoadInstrument most be specified to load an IDF" , m_filename);
}
else
{
Janik Zikovsky
committed
const std::string date = m_workspace->getWorkspaceStartDate();
m_filename = ExperimentInfo::getInstrumentFilename(m_instName,date);
Anders Markvardsen
committed
Janik Zikovsky
committed
if (!m_filename.empty())
{
// Remove the path from the filename for use with the InstrumentDataService
const std::string::size_type stripPath = m_filename.find_last_of("\\/");
std::string instrumentFile = m_filename.substr(stripPath+1,m_filename.size());
// Strip off "_Definition.xml"
m_instName = instrumentFile.substr(0,instrumentFile.find_first_of("_"));
}
// Load the XML text into a string
Janik Zikovsky
committed
if (m_xmlText.empty())
{
std::string str;
std::ifstream in;
in.open(m_filename.c_str());
getline(in,str);
while ( in ) {
Janik Zikovsky
committed
m_xmlText += str + "\n";
getline(in,str);
}
Janik Zikovsky
committed
in.close();
Janik Zikovsky
committed
// We don't want the instrument name taken out of the XML file itself, it should come from the filename (or the property)
m_instrument = Instrument_sptr(new Instrument(m_instName));
// Save the XML file path and contents
m_instrument->setFilename(m_filename);
Janik Zikovsky
committed
m_instrument->setXmlText(m_xmlText);
Michael Whitty
committed
// Set up the DOM parser and parse xml file
DOMParser pParser;
Document* pDoc;
try
{
Janik Zikovsky
committed
pDoc = pParser.parseString(m_xmlText);
Michael Whitty
committed
}
Anders Markvardsen
committed
catch(Poco::Exception& exc)
{
throw Kernel::Exception::FileError(exc.displayText() + ". Unable to parse File:", m_filename);
}
Michael Whitty
committed
catch(...)
{
throw Kernel::Exception::FileError("Unable to parse File:" , m_filename);
}
// Get pointer to root element
Element* pRootElem = pDoc->documentElement();
if ( !pRootElem->hasChildNodes() )
{
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
Michael Whitty
committed
// Handle used in the singleton constructor for instrument file should append the value
// of the last-modified tag inside the file to determine if it is already in memory so that
Michael Whitty
committed
// changes to the instrument file will cause file to be reloaded.
Janik Zikovsky
committed
std::string instrumentNameMangled = Poco::Path(m_filename).getFileName() + pRootElem->getAttribute("last-modified");
Anders Markvardsen
committed
Michael Whitty
committed
// Check whether the instrument is already in the InstrumentDataService
Janik Zikovsky
committed
if ( InstrumentDataService::Instance().doesExist(instrumentNameMangled) )
Michael Whitty
committed
{
// If it does, just use the one from the one stored there
Janik Zikovsky
committed
m_instrument = InstrumentDataService::Instance().retrieve(instrumentNameMangled);
Michael Whitty
committed
}
else
{
setValidityRange(pRootElem);
Michael Whitty
committed
readDefaults(pRootElem->getChildElement("defaults"));
Anders Markvardsen
committed
Michael Whitty
committed
// create maps: isTypeAssembly and mapTypeNameToShape
Geometry::ShapeFactory shapeCreator;
Anders Markvardsen
committed
Michael Whitty
committed
NodeList* pNL_type = pRootElem->getElementsByTagName("type");
if ( pNL_type->length() == 0 )
{
g_log.error("XML file: " + m_filename + "contains no type elements.");
throw Kernel::Exception::InstrumentDefinitionError("No type elements in XML instrument file", m_filename);
}
// Collect some information about types for later use including:
// * populate directory getTypeElement
// * populate directory isTypeAssemply
// * create shapes for all none assemply components and store in mapTyepNameToShape
// * If 'Outline' attribute set for assemply add attribute object_created=no to tell
// create shape for such assemply also later
unsigned long numberTypes = pNL_type->length();
for (unsigned long iType = 0; iType < numberTypes; iType++)
Michael Whitty
committed
{
Element* pTypeElem = static_cast<Element*>(pNL_type->item(iType));
std::string typeName = pTypeElem->getAttribute("name");
Anders Markvardsen
committed
// check if contain <combine-components-into-one-shape>. If this then such
// types are adjusted after this loop has completed
NodeList* pNL_type_combine_into_one_shape = pTypeElem->getElementsByTagName("combine-components-into-one-shape");
if ( pNL_type_combine_into_one_shape->length() )
continue;
pNL_type_combine_into_one_shape->release();
Michael Whitty
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);
}
getTypeElement[typeName] = pTypeElem;
Anders Markvardsen
committed
Michael Whitty
committed
// 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)
{
isTypeAssembly[typeName] = false;
Anders Markvardsen
committed
Michael Whitty
committed
// for now try to create a geometry shape associated with every type
// that does not contain any component elements
mapTypeNameToShape[typeName] = shapeCreator.createShape(pTypeElem);
mapTypeNameToShape[typeName]->setName(static_cast<int>(iType));
Michael Whitty
committed
}
else
Roman Tolchenov
committed
{
Michael Whitty
committed
isTypeAssembly[typeName] = true;
Roman Tolchenov
committed
if (pTypeElem->hasAttribute("outline"))
{
pTypeElem->setAttribute("object_created","no");
}
}
Michael Whitty
committed
pNL_local->release();
}
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
// Deal with adjusting types containing <combine-components-into-one-shape>
for (unsigned long iType = 0; iType < numberTypes; iType++)
{
Element* pTypeElem = static_cast<Element*>(pNL_type->item(iType));
std::string typeName = pTypeElem->getAttribute("name");
// In this loop only interested in types containing <combine-components-into-one-shape>
NodeList* pNL_type_combine_into_one_shape = pTypeElem->getElementsByTagName("combine-components-into-one-shape");
if ( pNL_type_combine_into_one_shape->length() == 0 )
continue;
pNL_type_combine_into_one_shape->release();
// 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);
}
getTypeElement[typeName] = pTypeElem;
LoadInstCompsIntoOneShape helper;
helper.adjust(pTypeElem, isTypeAssembly, getTypeElement);
isTypeAssembly[typeName] = false;
mapTypeNameToShape[typeName] = shapeCreator.createShape(pTypeElem);
mapTypeNameToShape[typeName]->setName(static_cast<int>(iType));
}
Michael Whitty
committed
pNL_type->release();
Anders Markvardsen
committed
Michael Whitty
committed
// create hasParameterElement
NodeList* pNL_parameter = pRootElem->getElementsByTagName("parameter");
Anders Markvardsen
committed
unsigned long numParameter = pNL_parameter->length();
Russell Taylor
committed
hasParameterElement.reserve(numParameter);
for (unsigned long i = 0; i < numParameter; i++)
Michael Whitty
committed
{
Element* pParameterElem = static_cast<Element*>(pNL_parameter->item(i));
hasParameterElement.push_back( static_cast<Element*>(pParameterElem->parentNode()) );
}
pNL_parameter->release();
hasParameterElement_beenSet = true;
// See if any parameters set at instrument level
setLogfile(m_instrument.get(), pRootElem, m_instrument->getLogfileCache());
// do analysis for each top level compoment element
NodeList* pNL_comp = pRootElem->childNodes(); // here get all child nodes
unsigned long pNL_comp_length = pNL_comp->length();
API::Progress prog(this,0,1,static_cast<int>(pNL_comp_length));
for (unsigned long i = 0; i < pNL_comp_length; i++)
Michael Whitty
committed
{
Janik Zikovsky
committed
if (VERBOSE) std::cout << "exec(): Node = "<< pNL_comp->item(i)->nodeName() << "\n";
Michael Whitty
committed
prog.report();
// we are only interest in the top level component elements hence
// the reason for the if statement below
Anders Markvardsen
committed
Michael Whitty
committed
if ( (pNL_comp->item(i))->nodeType() == Node::ELEMENT_NODE &&
((pNL_comp->item(i))->nodeName()).compare("component") == 0 )
{
Element* pElem = static_cast<Element*>(pNL_comp->item(i));
IdList idList; // structure to possibly be populated with detector IDs
// get all location elements contained in component element
NodeList* pNL_location = pElem->getElementsByTagName("location");
unsigned long pNL_location_length = pNL_location->length();
Michael Whitty
committed
if (pNL_location_length == 0)
{
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 />");
throw Kernel::Exception::InstrumentDefinitionError(
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);
}
if ( isAssembly(pElem->getAttribute("type")) )
Janik Zikovsky
committed
{
if (VERBOSE) std::cout << "exec(): This element has a type that is an assembly\n";
for (unsigned long i_loc = 0; i_loc < pNL_location_length; i_loc++)
Michael Whitty
committed
{
Element* pLocElem = static_cast<Element*>(pNL_location->item(i_loc));
Janik Zikovsky
committed
if (VERBOSE) std::cout << "exec(): AppendAssembly of " << pLocElem->nodeName() << "\n";
appendAssembly(m_instrument.get(), pLocElem, idList);
Michael Whitty
committed
}
// a check
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 "
+ pElem->getAttribute("idlist") +
" is larger than the number of detectors listed in type = "
+ pElem->getAttribute("type"));
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);
}
}
else
Gigg, Martyn Anthony
committed
{
Janik Zikovsky
committed
for (unsigned long i_loc = 0; i_loc < pNL_location_length; i_loc++)
Michael Whitty
committed
{
appendLeaf(m_instrument.get(), static_cast<Element*>(pNL_location->item(i_loc)), idList);
Michael Whitty
committed
}
}
pNL_location->release();
}
}
Michael Whitty
committed
pNL_comp->release();
// Don't need this anymore (if it was even used) so empty it out to save memory
LoadInstrumentHelper::m_tempPosHolder.clear();
// Read in or create the geometry cache file
setupGeometryCache();
Michael Whitty
committed
// Add/overwrite any instrument params with values specified in <component-link> XML elements
setComponentLinks(m_instrument, pRootElem);
Russell Taylor
committed
Russell Taylor
committed
if ( m_indirectPositions ) createNeutronicInstrument();
Russell Taylor
committed
Michael Whitty
committed
// Add the instrument to the InstrumentDataService
Janik Zikovsky
committed
InstrumentDataService::Instance().add(instrumentNameMangled,m_instrument);
Anders Markvardsen
committed
}
Gigg, Martyn Anthony
committed
// release XML document
pDoc->release();
// Add the instrument to the workspace
m_workspace->setInstrument(m_instrument);
Michael Whitty
committed
// populate parameter map of workspace
Gigg, Martyn Anthony
committed
m_workspace->populateInstrumentParameters();
Michael Whitty
committed
Janik Zikovsky
committed
// check if default parameter file is also present, unless loading from
if (!m_filename.empty())
runLoadParameterFile();
Gigg, Martyn Anthony
committed
Janik Zikovsky
committed
}
//------------------------------------------------------------------------------------------------------------------------------
/** Executes the algorithm. Reading in the file and creating and populating
* the output workspace
*
* @throw FileError Thrown if unable to parse XML file
* @throw InstrumentDefinitionError Thrown if issues with the content of XML instrument file
*/
void LoadInstrument::exec()
{
// Get the input workspace
MatrixWorkspace_sptr ws = getProperty("Workspace");
m_workspace = boost::dynamic_pointer_cast<MatrixWorkspace>(ws);
m_filename = getPropertyValue("Filename");
m_instName = getPropertyValue("InstrumentName");
m_xmlText = getPropertyValue("XMLText");
Janik Zikovsky
committed
m_xmlText = Strings::strip(m_xmlText);
if (!m_xmlText.empty() && m_instName.empty())
throw std::invalid_argument("LoadInstrument: If you specify XMLText, you also need to specify the InstrumentName parameter.");
if (!m_xmlText.empty() && m_filename.empty())
throw std::invalid_argument("LoadInstrument: If you specify XMLText, you still need to specify the Filename parameter (to find the geometry cache file).");
Janik Zikovsky
committed
execManually();
// Set the monitors output property
setProperty("MonitorList",m_instrument->getMonitors());
Gigg, Martyn Anthony
committed
// Rebuild the spectra map for this workspace so that it matches the instrument
// if required
const bool rewriteSpectraMap = getProperty("RewriteSpectraMap");
Janik Zikovsky
committed
if( rewriteSpectraMap && ws )
ws->rebuildSpectraMapping();
Russell Taylor
committed
}
Janik Zikovsky
committed
Janik Zikovsky
committed
//------------------------------------------------------------------------------------------------------------------------------
/** To manually set the parameters
*
* @param ei :: ExperimentInfo sptr, e.g. a workspace
* @param filename :: IDF .xml path (full)
* @param instName :: name of the instrument
* @param xmlText :: XML contents of IDF
*/
void LoadInstrument::setParametersManually(Mantid::API::ExperimentInfo_sptr ei, const std::string & filename, const std::string & instName, const std::string & xmlText)
{
m_workspace = ei;
m_filename = filename;
m_instName = instName;
m_xmlText = xmlText;
}
//------------------------------------------------------------------------------------------------------------------------------
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
/** Checks the validity range in the IDF and adds it to the instrument object
* @param pRootElem A pointer to the root element of the instrument definition
*/
void LoadInstrument::setValidityRange(const Poco::XML::Element* pRootElem)
{
// check if IDF has valid-from and valid-to tags defined
if ( !pRootElem->hasAttribute("valid-from") )
{
throw Kernel::Exception::InstrumentDefinitionError("<instrument> element must contain a valid-from tag", m_filename);
}
else
{
try
{
DateAndTime d(pRootElem->getAttribute("valid-from"));
m_instrument->setValidFromDate(d);
}
catch(...)
{
throw Kernel::Exception::InstrumentDefinitionError("The valid-from <instrument> tag must be a ISO8601 string", m_filename);
}
}
if ( !pRootElem->hasAttribute("valid-to") )
{
DateAndTime d = DateAndTime::get_current_time();
m_instrument->setValidToDate(d);
// Ticket #2335: no required valid-to date.
//throw Kernel::Exception::InstrumentDefinitionError("<instrument> element must contain a valid-to tag", m_filename);
}
else
{
try
{
DateAndTime d(pRootElem->getAttribute("valid-to"));
m_instrument->setValidToDate(d);
}
catch(...)
{
throw Kernel::Exception::InstrumentDefinitionError("The valid-to <instrument> tag must be a ISO8601 string", m_filename);
}
}
}
Janik Zikovsky
committed
//-----------------------------------------------------------------------------------------------------------------------
Michael Whitty
committed
/** Reads the contents of the \<defaults\> element to set member variables,
* requires m_instrument to be already set
Janik Zikovsky
committed
* @param defaults :: points to the data read from the \<defaults\> element
Michael Whitty
committed
*/
void LoadInstrument::readDefaults(Poco::XML::Element* defaults)
Gigg, Martyn Anthony
committed
{
Michael Whitty
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)
Gigg, Martyn Anthony
committed
{
Michael Whitty
committed
m_haveDefaultFacing = true;
m_defaultFacing = parseFacingElementToV3D(defaultFacingElement);
}
// 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"));
Gigg, Martyn Anthony
committed
}
Anders Markvardsen
committed
// check if angle=radian has been set
Element* angleUnit = defaults->getChildElement("angle");
if (angleUnit)
{
if (angleUnit->getAttribute("unit") == "radian")
{
m_angleConvertConst = 180.0/M_PI;
std::map<std::string, std::string>& units = m_instrument->getLogfileUnit();
units["angle"] = "radian";
}
}
Russell Taylor
committed
// Check if the IDF specifies that this is an indirect geometry instrument that includes
// both physical and 'neutronic' postions.
// Any neutronic position tags will be ignored if this tag is missing
if ( defaults->getChildElement("indirect-neutronic-positions") )
m_indirectPositions = true;
Gigg, Martyn Anthony
committed
}
Janik Zikovsky
committed
Russell Taylor
committed
std::vector<std::string> LoadInstrument::buildExcludeList(const Poco::XML::Element* const location)
{
// check if <exclude> sub-elements for this location and create new exclude list to pass on
NodeList* pNLexclude = location->getElementsByTagName("exclude");
unsigned long numberExcludeEle = pNLexclude->length();
std::vector<std::string> newExcludeList;
for (unsigned long i = 0; i < numberExcludeEle; i++)
{
Element* pExElem = static_cast<Element*>(pNLexclude->item(i));
if ( pExElem->hasAttribute("sub-part") )
newExcludeList.push_back(pExElem->getAttribute("sub-part"));
}
pNLexclude->release();
return newExcludeList;
}
Janik Zikovsky
committed
Janik Zikovsky
committed
//-----------------------------------------------------------------------------------------------------------------------
Michael Whitty
committed
/** Assumes second argument is a XML location element and its parent is a component element
Janik Zikovsky
committed
* which is assigned to be an assembly. 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.
Michael Whitty
committed
*
Janik Zikovsky
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
* @param idList :: The current IDList
Michael Whitty
committed
*/
Russell Taylor
committed
void LoadInstrument::appendAssembly(Geometry::ICompAssembly* parent, Poco::XML::Element* pLocElem, IdList& idList)
Russell Taylor
committed
{
Janik Zikovsky
committed
if (VERBOSE) std::cout << "appendAssembly() starting for parent " << parent->getName() << "\n";
Janik Zikovsky
committed
// The location element is required to be a child of a component element. Get this component element
Element* pCompElem = LoadInstrumentHelper::getParentComponent(pLocElem);
Anders Markvardsen
committed
Janik Zikovsky
committed
// Read detector IDs into idlist if required
Anders Markvardsen
committed
// Note idlist may be defined for any component
Russell Taylor
committed
// Note any new idlist found will take precedence.
Anders Markvardsen
committed
if ( pCompElem->hasAttribute("idlist") )
{
std::string idlist = pCompElem->getAttribute("idlist");
if ( idlist.compare(idList.idname) )
{
Element* pFound = pCompElem->ownerDocument()->getElementById(idlist, "idname");
if ( pFound == NULL )
{
throw Kernel::Exception::InstrumentDefinitionError(
"No <idlist> with name idname=\"" + idlist + "\" present in instrument definition file.", m_filename);
}
idList.reset();
populateIdList(pFound, idList);
}
}
Janik Zikovsky
committed
//Create the assembly that will be appended into the parent.
Roman Tolchenov
committed
Geometry::ICompAssembly *ass;
// 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
Element* pType = getTypeElement[pCompElem->getAttribute("type")];
if (pType->hasAttribute("outline") && pType->getAttribute("outline") != "no")
{
ass = new Geometry::ObjCompAssembly(LoadInstrumentHelper::getNameOfLocationElement(pLocElem),parent);
Roman Tolchenov
committed
}
else
{
ass = new Geometry::CompAssembly(LoadInstrumentHelper::getNameOfLocationElement(pLocElem),parent);
Roman Tolchenov
committed
}
Anders Markvardsen
committed
Janik Zikovsky
committed
if (VERBOSE) std::cout << "appendAssembly() is creating an assembly called " << ass->getName() << "\n";
Anders Markvardsen
committed
Michael Whitty
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
LoadInstrumentHelper::setLocation(ass, pLocElem, m_angleConvertConst, m_deltaOffsets);
Michael Whitty
committed
setFacing(ass, pLocElem);
Janik Zikovsky
committed
setLogfile(ass, pCompElem, m_instrument->getLogfileCache()); // params specified within <component>
Michael Whitty
committed
setLogfile(ass, pLocElem, m_instrument->getLogfileCache()); // params specified within specific <location>
Anders Markvardsen
committed
Russell Taylor
committed
// If enabled, check for a 'neutronic position' tag and add to cache if found
if ( m_indirectPositions )
{
Element* neutronic = pLocElem->getChildElement("neutronic");
if ( neutronic ) m_neutronicPos[ass] = neutronic;
}
Russell Taylor
committed
// Check for <exclude> tags for this location
const std::vector<std::string> excludeList = buildExcludeList(pLocElem);
Anders Markvardsen
committed
Michael Whitty
committed
NodeIterator it(pType, NodeFilter::SHOW_ELEMENT);
Anders Markvardsen
committed
Michael Whitty
committed
Node* pNode = it.nextNode();
while (pNode)
Anders Markvardsen
committed
{
Michael Whitty
committed
if ( pNode->nodeName().compare("location")==0 )
Anders Markvardsen
committed
{
Michael Whitty
committed
Element* pElem = static_cast<Element*>(pNode);
// check if this location is in the exclude list
std::vector<std::string>::const_iterator it = find(excludeList.begin(), excludeList.end(), LoadInstrumentHelper::getNameOfLocationElement(pElem));
Michael Whitty
committed
if ( it == excludeList.end() )
Anders Markvardsen
committed
{
std::string typeName = (LoadInstrumentHelper::getParentComponent(pElem))->getAttribute("type");
Michael Whitty
committed
Janik Zikovsky
committed
if (VERBOSE) std::cout << "appendAssembly() has found that its parent's type = " << typeName << "\n";
Michael Whitty
committed
if ( isAssembly(typeName) )
{
Russell Taylor
committed
appendAssembly(ass, pElem, idList);
Michael Whitty
committed
}
else
Russell Taylor
committed
{
Michael Whitty
committed
appendLeaf(ass, pElem, idList);
Russell Taylor
committed
}
Michael Whitty
committed
}
Anders Markvardsen
committed
}
Michael Whitty
committed
pNode = it.nextNode();
Anders Markvardsen
committed
}
Janik Zikovsky
committed
Roman Tolchenov
committed
// create outline object for the assembly
if (pType->hasAttribute("outline") && pType->getAttribute("outline") != "no")
{
Geometry::ObjCompAssembly* objAss = dynamic_cast<Geometry::ObjCompAssembly*>(ass);
if (pType->getAttribute("object_created") == "no")
{
pType->setAttribute("object_created","yes");
boost::shared_ptr<Geometry::Object> obj = objAss->createOutline();
if (obj)
{
mapTypeNameToShape[pType->getAttribute("name")] = obj;
}
else
{// object failed to be created
pType->setAttribute("outline","no");
g_log.warning()<<"Failed to create outline object for assembly "<<pType->getAttribute("name")<<'\n';
}
}
else
{
objAss->setOutline(mapTypeNameToShape[pType->getAttribute("name")]);
}
}
Janik Zikovsky
committed
Anders Markvardsen
committed
}
Anders Markvardsen
committed
Janik Zikovsky
committed
//-----------------------------------------------------------------------------------------------------------------------
Michael Whitty
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).
*
Janik Zikovsky
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
* @param idList :: The current IDList
Michael Whitty
committed
*
* @throw InstrumentDefinitionError Thrown if issues with the content of XML instrument file
*/
Roman Tolchenov
committed
void LoadInstrument::appendLeaf(Geometry::ICompAssembly* parent, Poco::XML::Element* pLocElem, IdList& idList)
Russell Taylor
committed
{
Michael Whitty
committed
// The location element is required to be a child of a component element. Get this component element
Element* pCompElem = LoadInstrumentHelper::getParentComponent(pLocElem);
Anders Markvardsen
committed
Janik Zikovsky
committed
//--- Get the detector's X/Y pixel sizes (optional) ---
if (VERBOSE) std::cout << "AppendLeaf: I am " << pLocElem->getAttribute("name") << " . " <<
"Parent is=" << pCompElem->getAttribute("is") <<
". Parent type=" << pCompElem->getAttribute("type") <<
Janik Zikovsky
committed
"\n";
Anders Markvardsen
committed
Janik Zikovsky
committed
// Read detector IDs into idlist if required
Anders Markvardsen
committed
// Note idlist may be defined for any component
Russell Taylor
committed
// Note any new idlist found will take precedence.
Anders Markvardsen
committed
if ( pCompElem->hasAttribute("idlist") )
{
std::string idlist = pCompElem->getAttribute("idlist");
if ( idlist.compare(idList.idname) )
{
Element* pFound = pCompElem->ownerDocument()->getElementById(idlist, "idname");
if ( pFound == NULL )
{
throw Kernel::Exception::InstrumentDefinitionError(
"No <idlist> with name idname=\"" + idlist + "\" present in instrument definition file.", m_filename);
}
idList.reset();
populateIdList(pFound, idList);
}
}
Michael Whitty
committed
// get the type element of the component element in order to determine if the type
Russell Taylor
committed
// belong to the category: "detector", "SamplePos or "Source".
Anders Markvardsen
committed
Michael Whitty
committed
std::string typeName = pCompElem->getAttribute("type");
Element* pType = getTypeElement[typeName];
Anders Markvardsen
committed
Michael Whitty
committed
std::string category = "";
if (pType->hasAttribute("is"))
category = pType->getAttribute("is");
Anders Markvardsen
committed
Michael Whitty
committed
// do stuff a bit differently depending on which category the type belong to
if ( category.compare("rectangular_detector") == 0 || category.compare("rectangularDetector") == 0 || category.compare("rectangulardetector") == 0 || category.compare("RectangularDetector") == 0 )
{
//-------------- Create a RectangularDetector ------------------------------------------------
std::string name = LoadInstrumentHelper::getNameOfLocationElement(pLocElem);
if (VERBOSE) std::cout << "AppendLeaf: Creating RectangularDetector " << name << ".\n";
//Create the bank with the given parent.
Geometry::RectangularDetector * bank = new Geometry::RectangularDetector(name, parent);
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.
LoadInstrumentHelper::setLocation(bank, pLocElem, m_angleConvertConst, m_deltaOffsets);
setFacing(bank, pLocElem);
setLogfile(bank, pCompElem, m_instrument->getLogfileCache()); // params specified within <component>
setLogfile(bank, pLocElem, m_instrument->getLogfileCache()); // params specified within specific <location>
//Extract all the parameters from the XML attributes
int xpixels=0; double xstart=0.; double xstep=0.;
int ypixels=0; double ystart=0.; double ystep=0.;
int idstart=0; bool idfillbyfirst_y=true; int idstepbyrow=0;
Anders Markvardsen
committed
int idstep=1;
//The shape!
Gigg, Martyn Anthony
committed
// Given that this leaf component is actually an assembly, its constituent component detector shapes comes from its type attribute.
const std::string shapeType = pType->getAttribute("type");
boost::shared_ptr<Geometry::Object> shape = mapTypeNameToShape[shapeType];
//These parameters are in the TYPE defining RectangularDetector
if ( pType->hasAttribute("xpixels") ) xpixels = atoi((pType->getAttribute("xpixels")).c_str());
if ( pType->hasAttribute("xstart") ) xstart = atof((pType->getAttribute("xstart")).c_str());
if ( pType->hasAttribute("xstep") ) xstep = atof((pType->getAttribute("xstep")).c_str());
if ( pType->hasAttribute("ypixels") ) ypixels = atoi((pType->getAttribute("ypixels")).c_str());
if ( pType->hasAttribute("ystart") ) ystart = atof((pType->getAttribute("ystart")).c_str());
if ( pType->hasAttribute("ystep") ) ystep = atof((pType->getAttribute("ystep")).c_str());
//THESE parameters are in the INSTANCE of this type - since they will change.
if ( pCompElem->hasAttribute("idstart") ) idstart = atoi((pCompElem->getAttribute("idstart")).c_str());
if ( pCompElem->hasAttribute("idfillbyfirst") )
idfillbyfirst_y = (pCompElem->getAttribute("idfillbyfirst") == "y");
Anders Markvardsen
committed
//Default ID row step size
if (idfillbyfirst_y) idstepbyrow = ypixels;
else idstepbyrow = xpixels;
if ( pCompElem->hasAttribute("idstepbyrow") )
{
idstepbyrow = atoi((pCompElem->getAttribute("idstepbyrow")).c_str());
Anders Markvardsen
committed
//Default ID row step size
if ( pCompElem->hasAttribute("idstep") ) idstep = atoi((pCompElem->getAttribute("idstep")).c_str());
if (VERBOSE) std::cout << "AppendLeaf: Initializing RectangularDetector with parameters : " <<
"shape " << typeName << ", " << xpixels << ", " << xstart << ", " << xstep << ", " << ypixels << ", " << ystart << ", " << ystep << ", " << idstart << ", " << idfillbyfirst_y << ", " << idstepbyrow << ".\n";
// Now, initialize all the pixels in the bank
Anders Markvardsen
committed
bank->initialize(shape, xpixels, xstart, xstep, ypixels, ystart, ystep, idstart, idfillbyfirst_y, idstepbyrow, idstep);
//Loop through all detectors in the newly created bank and mark those in the instrument.
try
{
Gigg, Martyn Anthony
committed
for (int x=0; x < bank->nelements(); x++)
Gigg, Martyn Anthony
committed
boost::shared_ptr<Geometry::ICompAssembly> xColumn = boost::dynamic_pointer_cast<Geometry::ICompAssembly>((*bank)[x]);
for (int y=0; y < xColumn->nelements(); y++)
{
Gigg, Martyn Anthony
committed
boost::shared_ptr<Geometry::Detector> detector = boost::dynamic_pointer_cast<Geometry::Detector>((*xColumn)[y]);
if (detector)
{
//Make default facing for the pixel
Geometry::IComponent* comp = (Geometry::IComponent*) detector.get();
if (m_haveDefaultFacing)
makeXYplaneFaceComponent(comp, m_defaultFacing);
//Mark it as a detector (add to the instrument cache)
m_instrument->markAsDetector(detector.get());
}
}
}
}
catch(Kernel::Exception::ExistsError&)
{
throw Kernel::Exception::InstrumentDefinitionError(
"Duplicate detector ID found when adding RectangularDetector " + name + " in XML instrument file" + m_filename);
}
}
else if ( category.compare("detector") == 0 )
Michael Whitty
committed
{
Janik Zikovsky
committed
//-------------- Create a Detector ------------------------------------------------
std::string name = LoadInstrumentHelper::getNameOfLocationElement(pLocElem);
Anders Markvardsen
committed
Michael Whitty
committed
// before setting detector ID check that the IDF satisfy the following
Anders Markvardsen
committed
Michael Whitty
committed
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);
}
Russell Taylor
committed
// Create detector and increment id. Finally add the detector to the parent
Geometry::Detector* detector =
new Geometry::Detector(name, idList.vec[idList.counted],mapTypeNameToShape[typeName], parent);
Michael Whitty
committed
idList.counted++;
parent->add(detector);
// 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.
LoadInstrumentHelper::setLocation(detector, pLocElem, m_angleConvertConst, m_deltaOffsets);
Michael Whitty
committed
setFacing(detector, pLocElem);
setLogfile(detector, pCompElem, m_instrument->getLogfileCache()); // params specified within <component>
setLogfile(detector, pLocElem, m_instrument->getLogfileCache()); // params specified within specific <location>
Anders Markvardsen
committed
Russell Taylor
committed
// If enabled, check for a 'neutronic position' tag and add to cache (null pointer added if not found)
if ( m_indirectPositions )
{
m_neutronicPos[detector] = pLocElem->getChildElement("neutronic");
}
Michael Whitty
committed
try
{
if ( pCompElem->hasAttribute("mark-as") || pLocElem->hasAttribute("mark-as") )
m_instrument->markAsMonitor(detector);
else
m_instrument->markAsDetector(detector);
Russell Taylor
committed
Michael Whitty
committed
}
catch(Kernel::Exception::ExistsError&)
{
std::stringstream convert;
convert << detector->getID();
throw Kernel::Exception::InstrumentDefinitionError("Detector with ID = " + convert.str() +
" present more then once in XML instrument file", m_filename);
}
Anders Markvardsen
committed
Michael Whitty
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);
Russell Taylor
committed
}
Michael Whitty
committed
else
{
//-------------- Not a Detector nor a RectangularDetector ------------------------------
std::string name = LoadInstrumentHelper::getNameOfLocationElement(pLocElem);
Michael Whitty
committed
Geometry::ObjComponent *comp = new Geometry::ObjComponent(name, mapTypeNameToShape[typeName], parent);
parent->add(comp);
Russell Taylor
committed
Michael Whitty
committed
// check if special Source or SamplePos Component
if ( category.compare("Source") == 0 )
{
m_instrument->markAsSource(comp);
}
if ( category.compare("SamplePos") == 0 )
{
m_instrument->markAsSamplePos(comp);
}
Russell Taylor
committed
Michael Whitty
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
LoadInstrumentHelper::setLocation(comp, pLocElem, m_angleConvertConst, m_deltaOffsets);
Michael Whitty
committed
setFacing(comp, pLocElem);
setLogfile(comp, pCompElem, m_instrument->getLogfileCache()); // params specified within <component>
setLogfile(comp, pLocElem, m_instrument->getLogfileCache()); // params specified within specific <location>
}
Russell Taylor
committed
}
Michael Whitty
committed
Janik Zikovsky
committed
//-----------------------------------------------------------------------------------------------------------------------
Michael Whitty
committed
/** Method for populating IdList.
*
Janik Zikovsky
committed
* @param pE :: Poco::XML element that points to an \<idlist\>
* @param idList :: The structure to populate with detector ID numbers
Michael Whitty
committed
*
* @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)
{
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." );
}
Anders Markvardsen
committed
Michael Whitty
committed
// set name of idlist
Anders Markvardsen
committed
Michael Whitty
committed
idList.idname = pE->getAttribute("idname");
Anders Markvardsen
committed
Michael Whitty
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() );
int endID;
if ( pE->hasAttribute("end") )
endID = atoi( (pE->getAttribute("end")).c_str() );
else
endID = startID;
int increment = 1;
if ( pE->hasAttribute("step") ) increment = atoi( (pE->getAttribute("step")).c_str() );
idList.vec.reserve(endID-startID/increment);
for (int i = startID; i != endID+increment; i += increment)
idList.vec.push_back(i);
}
else
Anders Markvardsen
committed
{
Michael Whitty
committed
// test first if any <id> elements
NodeList* pNL = pE->getElementsByTagName("id");
Anders Markvardsen
committed
Michael Whitty
committed
if ( pNL->length() == 0 )
Anders Markvardsen
committed
{
Michael Whitty
committed
throw Kernel::Exception::InstrumentDefinitionError("No id subelement of idlist element in XML instrument file", m_filename);
Anders Markvardsen
committed
}
Michael Whitty
committed
pNL->release();
Anders Markvardsen
committed
Anders Markvardsen
committed
Michael Whitty
committed
// get id numbers
NodeIterator it(pE, NodeFilter::SHOW_ELEMENT);
Node* pNode = it.nextNode();
while (pNode)
Anders Markvardsen
committed
{
Michael Whitty
committed
if ( pNode->nodeName().compare("id")==0 )
{
Element* pIDElem = static_cast<Element*>(pNode);
if ( pIDElem->hasAttribute("val") )
{
int valID = atoi( (pIDElem->getAttribute("val")).c_str() );
idList.vec.push_back(valID);
}
else if ( pIDElem->hasAttribute("start") )
{
int startID = atoi( (pIDElem->getAttribute("start")).c_str() );
int endID;
if ( pIDElem->hasAttribute("end") )
endID = atoi( (pIDElem->getAttribute("end")).c_str() );
else
endID = startID;