Newer
Older
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
#include "MantidAPI/FileProperty.h"
#include "MantidAPI/InstrumentDataService.h"
Federico Montesino Pouzols
committed
#include "MantidAPI/MatrixWorkspace.h"
#include "MantidAPI/Progress.h"
Russell Taylor
committed
#include "MantidGeometry/Instrument.h"
#include "MantidKernel/ArrayProperty.h"
Gigg, Martyn Anthony
committed
#include "MantidKernel/ConfigService.h"
#include "MantidKernel/OptionalBool.h"
#include "MantidKernel/MandatoryValidator.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 "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
namespace Mantid {
namespace DataHandling {
DECLARE_ALGORITHM(LoadInstrument)
using namespace Kernel;
using namespace API;
using namespace Geometry;
Poco::Mutex LoadInstrument::m_mutex;
/// Empty default constructor
LoadInstrument::LoadInstrument() : Algorithm() {}
//------------------------------------------------------------------------------------------------------------------------------
/// Initialisation method.
void LoadInstrument::init() {
// When used as a Child 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. Any existing instrument will be replaced.");
declareProperty(
new FileProperty("Filename", "", FileProperty::OptionalLoad, ".xml"),
"The filename (including its full or relative path) of an instrument "
"definition file. The file extension must either be .xml or .XML when "
"specifying an instrument definition file. Note Filename or "
"InstrumentName must be specified but not both.");
declareProperty(new ArrayProperty<detid_t>("MonitorList", Direction::Output),
"Will be filled with a list of the detector ids of any "
"monitors loaded in to the workspace.");
declareProperty(
"InstrumentName", "",
"Name of instrument. Can be used instead of Filename to specify an IDF");
declareProperty("InstrumentXML", "",
"The full XML instrument definition as a string.");
declareProperty(
new PropertyWithValue<OptionalBool>(
"RewriteSpectraMap", OptionalBool::Unset,
boost::make_shared<MandatoryValidator<OptionalBool>>()),
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
"If true then a 1:1 map between the spectrum numbers and "
"detector/monitor IDs is set up as follows: the detector/monitor IDs in "
"the IDF are ordered from smallest to largest number and then assigned "
"in that order to the spectra in the workspace. For example if the IDF "
"has defined detectors/monitors with ID = 1, 5 and 10 and the workspace "
"contains 3 spectra with numbers 1,2,3 (and workspace indices 0,1, and "
"2) then spectrum number 1 is associated with det ID=1, spectrum number "
"2 with det ID=5 and spectrum number 3 with det ID=10");
}
//------------------------------------------------------------------------------------------------------------------------------
/** 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
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.
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.
const PropertyWithValue<std::string> *xml =
dynamic_cast<const PropertyWithValue<std::string> *>(InstrumentXML);
if (xml) {
parser = InstrumentDefinitionParser(m_filename, m_instName, *xml);
} else {
throw std::invalid_argument("The instrument XML passed cannot be "
"casted to a standard string.");
}
}
// 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);
const std::string date = m_workspace->getWorkspaceStartDate();
m_filename = ExperimentInfo::getInstrumentFilename(m_instName, date);
if (m_filename.empty()) {
throw Exception::NotFoundError(
"Unable to find an Instrument Definition File for", m_instName);
Anders Markvardsen
committed
}
// 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"));
// Initialize the parser with the the XML text loaded from the IDF file
parser = InstrumentDefinitionParser(m_filename, m_instName,
Strings::loadFile(m_filename));
}
// Find the mangled instrument name that includes the modified date
std::string instrumentNameMangled = parser.getMangledName();
Instrument_sptr instrument;
// Check whether the instrument is already in the InstrumentDataService
{
// Make InstrumentService access thread-safe
Poco::Mutex::ScopedLock lock(m_mutex);
if (InstrumentDataService::Instance().doesExist(instrumentNameMangled)) {
// If it does, just use the one from the one stored there
instrument =
InstrumentDataService::Instance().retrieve(instrumentNameMangled);
} else {
// Really create the instrument
auto 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);
}
}
// Add the instrument to the workspace
m_workspace->setInstrument(instrument);
// populate parameter map of workspace
m_workspace->populateInstrumentParameters();
// check if default parameter file is also present, unless loading from
if (!m_filename.empty())
runLoadParameterFile();
// Set the monitors output property
setProperty("MonitorList", instrument->getMonitors());
// Rebuild the spectra map for this workspace so that it matches the
// instrument
// if required
const OptionalBool RewriteSpectraMap = getProperty("RewriteSpectraMap");
if (RewriteSpectraMap == OptionalBool::True)
m_workspace->rebuildSpectraMapping();
}
//-----------------------------------------------------------------------------------------------------------------------
/// Run the Child 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()) {
// Not found, so search the other places were it may occur
Kernel::ConfigServiceImpl &configService =
Kernel::ConfigService::Instance();
std::vector<std::string> directoryNames =
configService.getInstrumentDirectories();
for (auto directoryName : directoryNames) {
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
// This will iterate around the directories from user ->etc ->install, and
// find the first beat file
fullPathParamIDF = getFullPathParamIDF(directoryName);
// stop when you find the first one
if (!fullPathParamIDF.empty())
break;
}
}
if (!fullPathParamIDF.empty()) {
g_log.debug() << "Parameter file: " << fullPathParamIDF << std::endl;
// Now execute the Child Algorithm. Catch and log any error, but don't stop.
try {
// To allow the use of ExperimentInfo instead of workspace, we call it
// manually
Algorithm_sptr loadParamAlg = createChildAlgorithm("LoadParameterFile");
loadParamAlg->setProperty("Filename", fullPathParamIDF);
loadParamAlg->setProperty("Workspace", m_workspace);
loadParamAlg->execute();
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 Child 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) {
Poco::Path directoryPath(directoryName);
directoryPath.makeDirectory();
// Remove the path from the filename
Poco::Path filePath(m_filename);
std::string instrumentFile = filePath.getFileName();
// 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 =
directoryPath.setFileName(prefix + "_Parameters" + suffix).toString();
if (Poco::File(fullPathParamIDF).exists() ==
false) { // No such file exists, so look for file based on instrument ID
// given by the prefix
fullPathParamIDF =
directoryPath.setFileName(prefix + "_Parameters.xml").toString();
}
if (Poco::File(fullPathParamIDF).exists() ==
false) { // No such file exists, indicate none found in this directory.
fullPathParamIDF = "";
}
return fullPathParamIDF;
}
} // namespace DataHandling