Newer
Older
Janik Zikovsky
committed
/*WIKI*
Loads an instrument definition file ([[InstrumentDefinitionFile|IDF]]) into a workspace, which contains information about detector positions, their geometric shape, slit properties, links between values stored in ISIS log-files and components of the instrument and so on. For more on IDFs see: [[InstrumentDefinitionFile]].
By default the algorithm will write a 1:1 map between the spectrum number and detector ID. Any custom loading algorithm that calls this as a sub algorithm will therefore get this 1:1 map be default. If the custom loader is to write its own map then it is advised to set <code>RewriteSpectraMap</code> to false to avoid extra work.
*WIKI*/
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
#include "MantidAPI/FileProperty.h"
#include "MantidAPI/InstrumentDataService.h"
#include "MantidAPI/Progress.h"
Anders Markvardsen
committed
#include "MantidDataHandling/LoadParameterFile.h"
Russell Taylor
committed
#include "MantidGeometry/Instrument.h"
#include "MantidGeometry/Instrument/Detector.h"
Roman Tolchenov
committed
#include "MantidGeometry/Instrument/ObjCompAssembly.h"
#include "MantidGeometry/Instrument/RectangularDetector.h"
#include "MantidGeometry/Instrument/XMLlogfile.h"
#include "MantidGeometry/Objects/ShapeFactory.h"
#include "MantidGeometry/Rendering/vtkGeometryCacheReader.h"
#include "MantidGeometry/Rendering/vtkGeometryCacheWriter.h"
#include "MantidKernel/ArrayProperty.h"
Gigg, Martyn Anthony
committed
#include "MantidKernel/ConfigService.h"
#include "MantidKernel/DateAndTime.h"
Anders Markvardsen
committed
#include "MantidKernel/Interpolation.h"
#include "MantidKernel/PhysicalConstants.h"
Anders Markvardsen
committed
#include "MantidKernel/UnitFactory.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>
#include "MantidGeometry/Instrument/InstrumentDefinitionParser.h"
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
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()
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",Direction::Output),
"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" );
declareProperty("InstrumentXML","","The full XML description of the instrument."
"If given, the instrument is constructed from this instead of from an IDF."
"The InstrumentName property must also be set.");
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");
Anders Markvardsen
committed
}
Anders Markvardsen
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()
Janik Zikovsky
committed
{
// Get the input workspace
m_workspace = getProperty("Workspace");
m_filename = getPropertyValue("Filename");
m_instName = getPropertyValue("InstrumentName");
// We will parse the XML using the InstrumentDefinitionParser
InstrumentDefinitionParser parser;
// If the XML is passed in via the InstrumentXML property, use that.
const Property * const InstrumentXML = getProperty("InstrumentXML");
if ( ! InstrumentXML->isDefault() )
// We need the instrument name to be set as well because, for whatever reason,
// this isn't pulled out of the XML.
Janik Zikovsky
committed
if ( m_instName.empty() )
throw std::runtime_error("The InstrumentName property must be set when using the InstrumentXML property.");
// If the Filename property is not set, set it to the same as the instrument name
if ( m_filename.empty() ) m_filename = m_instName;
// Initialize the parser. Avoid copying the xmltext out of the property here.
parser.initialize(m_filename, m_instName,
*dynamic_cast<const PropertyWithValue<std::string>*>(InstrumentXML) );
}
// otherwise we need either Filename or InstrumentName to be set
else
{
// Retrieve the filename from the properties
if ( m_filename.empty() )
// look to see if an Instrument name provided in which case create
// IDF filename on the fly
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
{
const std::string date = m_workspace->getWorkspaceStartDate();
m_filename = ExperimentInfo::getInstrumentFilename(m_instName,date);
}
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("_Def"));
Anders Markvardsen
committed
// Initialize the parser with the the XML text loaded from the IDF file
parser.initialize(m_filename, m_instName, Strings::loadFile(m_filename));
Janik Zikovsky
committed
}
// Find the mangled instrument name that includes the modified date
std::string instrumentNameMangled = parser.getMangledName();
Anders Markvardsen
committed
Instrument_sptr instrument;
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
instrument = InstrumentDataService::Instance().retrieve(instrumentNameMangled);
Michael Whitty
committed
}
else
{
// Really create the instrument
Progress * prog = new Progress(this, 0, 1, 100);
instrument = parser.parseXML(prog);
delete prog;
// Add to data service for later retrieval
InstrumentDataService::Instance().add(instrumentNameMangled, instrument);
Anders Markvardsen
committed
}
// Add the instrument to the workspace
m_workspace->setInstrument(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
// Set the monitors output property
setProperty("MonitorList",instrument->getMonitors());
Janik Zikovsky
committed
Gigg, Martyn Anthony
committed
// Rebuild the spectra map for this workspace so that it matches the instrument
// if required
const bool rewriteSpectraMap = getProperty("RewriteSpectraMap");
if( rewriteSpectraMap )
m_workspace->rebuildSpectraMapping();
Michael Whitty
committed
}
Anders Markvardsen
committed
Anders Markvardsen
committed
Janik Zikovsky
committed
//-----------------------------------------------------------------------------------------------------------------------
Michael Whitty
committed
/// Run the sub-algorithm LoadInstrument (or LoadInstrumentFromRaw)
void LoadInstrument::runLoadParameterFile()
{
g_log.debug("Loading the parameter definition...");
// First search for XML parameter file in same folder as IDF file
const std::string::size_type dir_end = m_filename.find_last_of("\\/");
std::string directoryName = m_filename.substr(0,dir_end+1); // include final '/'.
std::string fullPathParamIDF = getFullPathParamIDF( directoryName );
if( fullPathParamIDF.empty() )
Michael Whitty
committed
{
// Not found, so search the other places were it may occur
Kernel::ConfigServiceImpl & configService = Kernel::ConfigService::Instance();
std::string directoryName = configService.getString("parameterDefinition.directory");
if (directoryName.empty())
{
// This is the assumed deployment directory for parameter files, where we need to be
// relative to the directory of the executable, not the current working directory.
directoryName = Poco::Path(configService.getPropertiesDir()).resolve("../instrument").toString();
// Remove the path from the filename
std::string instrumentFile = m_filename.substr(dir_end+1,m_filename.size());
}
fullPathParamIDF = getFullPathParamIDF( directoryName );
Michael Whitty
committed
}
Anders Markvardsen
committed
if(!fullPathParamIDF.empty()) {
g_log.debug() << "Parameter file: " << fullPathParamIDF << std::endl;
// Now execute the sub-algorithm. Catch and log any error, but don't stop.
try
{
// To allow the use of ExperimentInfo instead of workspace, we call it manually
LoadParameterFile::execManually(fullPathParamIDF, m_workspace);
g_log.debug("Parameters loaded successfully.");
} catch (std::invalid_argument& e)
{
g_log.information("LoadParameterFile: No parameter file found for this instrument");
g_log.information(e.what());
} catch (std::runtime_error& e)
{
g_log.information("Unable to successfully run LoadParameterFile sub-algorithm");
g_log.information(e.what());
}
} else {
g_log.information("No parameter file found for this instrument");
}
}
//-----------------------------------------------------------------------------------------------------------------------
/// Search the directory for the Parameter IDF file and return full path name if found, else return "".
// directoryName must include a final '/'.
std::string LoadInstrument::getFullPathParamIDF( std::string directoryName )
Michael Whitty
committed
// Remove the path from the filename
const std::string::size_type stripPath = m_filename.find_last_of("\\/");
Michael Whitty
committed
std::string instrumentFile = m_filename.substr(stripPath+1,m_filename.size());
Anders Markvardsen
committed
// First check whether there is a parameter file whose name is the same as the IDF file,
// but with 'Parameters' instead of 'Definition'.
std::string definitionPart("_Definition");
const std::string::size_type prefix_end(instrumentFile.find(definitionPart));
const std::string::size_type suffix_start = prefix_end + definitionPart.length();
// Make prefix and force it to be upper case
std::string prefix = instrumentFile.substr(0, prefix_end);
std::transform(prefix.begin(), prefix.end(), prefix.begin(), toupper);
// Make suffix ensuring it has positive length
std::string suffix = ".xml";
if( suffix_start < instrumentFile.length() )
{
suffix = instrumentFile.substr(suffix_start, std::string::npos );
}
// Assemble parameter file name
std::string fullPathParamIDF = directoryName + prefix + "_Parameters" + suffix;
if( Poco::File(fullPathParamIDF).exists() == false)
{ // No such file exists, so look for file based on instrument ID given by the prefix
fullPathParamIDF = directoryName + "/" + prefix + "_Parameters.xml";
}
Anders Markvardsen
committed
if( Poco::File(fullPathParamIDF).exists() == false)
{ // No such file exists, indicate none found in this directory.
fullPathParamIDF="";
Anders Markvardsen
committed
}
return fullPathParamIDF;
Anders Markvardsen
committed
}
Michael Whitty
committed
} // namespace DataHandling