Skip to content

Commit

Permalink
Re #5426. Add option to pass instrument XML into LoadInstrument
Browse files Browse the repository at this point in the history
...directly, rather than a filename. This will be used by the SNS
live data listener.
  • Loading branch information
RussellTaylor committed May 31, 2012
1 parent 2e36b6a commit 26f7cc5
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 64 deletions.
3 changes: 1 addition & 2 deletions Code/Mantid/Framework/API/src/ExperimentInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -790,13 +790,12 @@ namespace API
*/
void ExperimentInfo::readParameterMap(const std::string & parameterStr)
{
const std::string & details = parameterStr;
Geometry::ParameterMap& pmap = this->instrumentParameters();
Instrument_const_sptr instr = this->getInstrument()->baseInstrument();

int options = Poco::StringTokenizer::TOK_IGNORE_EMPTY;
options += Poco::StringTokenizer::TOK_TRIM;
Poco::StringTokenizer splitter(details, "|", options);
Poco::StringTokenizer splitter(parameterStr, "|", options);

Poco::StringTokenizer::Iterator iend = splitter.end();
//std::string prev_name;
Expand Down
105 changes: 62 additions & 43 deletions Code/Mantid/Framework/DataHandling/src/LoadInstrument.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,13 @@ namespace Mantid
declareProperty(new FileProperty("Filename","", FileProperty::OptionalLoad, ".xml"),
"The filename (including its full or relative path) of an instrument\n"
"definition file");
declareProperty(new ArrayProperty<detid_t>("MonitorList"),
declareProperty(new ArrayProperty<detid_t>("MonitorList",Direction::Output),
"List of detector ids of monitors loaded in to the workspace");
declareProperty( "InstrumentName", "",
declareProperty("InstrumentName", "",
"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.");
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");
Expand All @@ -111,78 +114,92 @@ namespace Mantid
m_filename = getPropertyValue("Filename");
m_instName = getPropertyValue("InstrumentName");

// Retrieve the filename from the properties
if ( m_filename.empty() )
// We will parse the XML using the InstrumentDefinitionParser
InstrumentDefinitionParser parser;

// Is the XML is passed in via the InstrumentXML property, use that.
const Property * const InstrumentXML = getProperty("InstrumentXML");
if ( ! InstrumentXML->isDefault() )
{
g_log.debug() << "=====> A1" << std::endl;

// look to see if an Instrument name provided in which case create
// IDF filename on the fly
// 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.
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() )
{
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);
g_log.debug() << "=====> A1" << std::endl;

// 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);

g_log.debug() << "=====> A2 - Filename is " << m_filename << std::endl;
}
}
else

if (!m_filename.empty())
{
const std::string date = m_workspace->getWorkspaceStartDate();
m_filename = ExperimentInfo::getInstrumentFilename(m_instName,date);
// 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"));

g_log.debug() << "=====> A2 - Filename is " << m_filename << std::endl;
g_log.debug() << "=====> A3 - Instrument is " << m_instName << std::endl;
}
}

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"));
g_log.debug() << "=====> m_filename = " << m_filename << std::endl;

g_log.debug() << "=====> A3 - Instrument is " << m_instName << std::endl;
// Initialize the parser with the the XML text loaded from the IDF file
parser.initialize(m_filename, m_instName, Strings::loadFile(m_filename));
}

g_log.debug() << "=====> m_filename = " << m_filename << std::endl;

// Load the XML text into a string
std::string m_xmlText = Strings::loadFile(m_filename);

g_log.debug() << "\n\n\n\n" << "=====> B - XML:\n\n" << m_xmlText << "\n\n\n\n";

// Our new instrument
Instrument_sptr m_instrument;

// Parse the XML using the InstrumentDefinitionParser
InstrumentDefinitionParser parser;
parser.initialize(m_filename, m_instName, m_xmlText);

// Find the mangled instrument name that includes the modified date
std::string instrumentNameMangled = parser.getMangledName();
g_log.debug() << "Instrument Mangled Name = " << instrumentNameMangled << std::endl;

Instrument_sptr instrument;
// Check whether the instrument is already in the InstrumentDataService
if ( InstrumentDataService::Instance().doesExist(instrumentNameMangled) )
{
// If it does, just use the one from the one stored there
g_log.debug() << "Instrument definition already loaded, using cached version.";
m_instrument = InstrumentDataService::Instance().retrieve(instrumentNameMangled);
instrument = InstrumentDataService::Instance().retrieve(instrumentNameMangled);
}
else
{
g_log.debug() << "Loading instrument XML...";
// Really create the instrument
Progress * prog = new Progress(this, 0, 1, 100);
m_instrument = parser.parseXML(prog);
instrument = parser.parseXML(prog);
delete prog;
g_log.debug() << "...done!" << std::endl;
// Add to data service for later retrieval
InstrumentDataService::Instance().add(instrumentNameMangled, m_instrument);
InstrumentDataService::Instance().add(instrumentNameMangled, instrument);
}


// Add the instrument to the workspace
m_workspace->setInstrument(m_instrument);
m_workspace->setInstrument(instrument);

// populate parameter map of workspace
m_workspace->populateInstrumentParameters();
Expand All @@ -192,12 +209,12 @@ namespace Mantid
runLoadParameterFile();

// Set the monitors output property
setProperty("MonitorList",m_instrument->getMonitors());
setProperty("MonitorList",instrument->getMonitors());

// Rebuild the spectra map for this workspace so that it matches the instrument
// if required
const bool rewriteSpectraMap = getProperty("RewriteSpectraMap");
if( rewriteSpectraMap && m_workspace )
if( rewriteSpectraMap )
m_workspace->rebuildSpectraMapping();
}

Expand Down Expand Up @@ -228,11 +245,13 @@ namespace Mantid
std::transform(instrumentID.begin(), instrumentID.end(), instrumentID.begin(), toupper);
std::string fullPathIDF = directoryName + "/" + instrumentID + "_Parameters.xml";

g_log.debug() << "Parameter file: " << fullPathIDF << 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(fullPathIDF, m_workspace);
g_log.debug("Parameters loaded successfully.");
} catch (std::invalid_argument& e)
{
g_log.information("LoadParameterFile: No parameter file found for this instrument");
Expand Down
61 changes: 60 additions & 1 deletion Code/Mantid/Framework/DataHandling/test/LoadInstrumentTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -495,12 +495,71 @@ class LoadInstrumentTest : public CxxTest::TestSuite
IDS.clear();
}

void test_loading_via_InstrumentXML_property()
{
// Make sure the IDS is empty
InstrumentDataServiceImpl& IDS = InstrumentDataService::Instance();
IDS.clear();

// Minimal XML instrument, inspired by IDF_for_UNIT_TESTING3.xml
const std::string instrumentXML =
"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>"
"<instrument name=\"xmlInst\" valid-from=\"1900-01-31 23:59:59\" valid-to=\"2100-01-31 23:59:59\" last-modified=\"2010-10-06T16:21:30\">"
"<defaults />"
"<component type=\"panel\" idlist=\"idlist_for_bank1\">"
"<location r=\"0\" t=\"0\" rot=\"0\" axis-x=\"0\" axis-y=\"1\" axis-z=\"0\" name=\"bank1\" xpixels=\"3\" ypixels=\"2\" />"
"</component>"
"<type is=\"detector\" name=\"panel\">"
"<properties/>"
"<component type=\"pixel\">"
"<location y=\"1\" x=\"1\"/>"
"</component>"
"</type>"
"<type is=\"detector\" name=\"pixel\">"
"<cuboid id=\"pixel-shape\" />"
"<algebra val=\"pixel-shape\"/>"
"</type>"
"<idlist idname=\"idlist_for_bank1\">"
"<id start=\"1005\" end=\"1005\" />"
"</idlist>"
"</instrument>";

LoadInstrument instLoader;
instLoader.initialize();
instLoader.setProperty("Workspace",WorkspaceFactory::Instance().create("EventWorkspace",1,1,1));
instLoader.setProperty("InstrumentXML",instrumentXML);
instLoader.setProperty("InstrumentName", "Nonsense"); // Want to make sure it doesn't matter what we call it

TS_ASSERT( instLoader.execute() )

TS_ASSERT( IDS.doesExist("Nonsense2010-10-06T16:21:30") )
}

void test_failure_if_InstrumentXML_property_set_but_not_InstrumentName()
{
LoadInstrument instLoader;
instLoader.initialize();
instLoader.setProperty("Workspace",WorkspaceFactory::Instance().create("EventWorkspace",1,1,1));
instLoader.setProperty("InstrumentXML","<doesn't matter what>");

TS_ASSERT( ! instLoader.execute() )
}

void test_failure_if_InstrumentXML_is_malformed()
{
LoadInstrument instLoader;
instLoader.initialize();
instLoader.setProperty("Workspace",WorkspaceFactory::Instance().create("EventWorkspace",1,1,1));
instLoader.setProperty("InstrumentXML","<instrument>");
instLoader.setProperty("InstrumentName", "Nonsense");

TS_ASSERT( ! instLoader.execute() )
}

private:
LoadInstrument loader;
std::string inputFile;
std::string wsName;

};


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,6 @@ namespace Geometry

/// The name and path of the input file
std::string m_filename;
/// Full XML contents
std::string m_xmlText;
/// Name of the instrument
std::string m_instName;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,23 +89,22 @@ namespace Geometry
// Handle the parameters
m_filename = filename;
m_instName = instName;
m_xmlText = xmlText;

// Set up the DOM parser and parse xml file
DOMParser pParser;
try
{
g_log.debug() << "====> pre pDoc = pParser.parseString(m_xmlText);" << std::endl;
pDoc = pParser.parseString(m_xmlText);
pDoc = pParser.parseString(xmlText);
g_log.debug() << "====> post pDoc = pParser.parseString(m_xmlText);" << std::endl;
}
catch(Poco::Exception& exc)
{
throw Kernel::Exception::FileError(exc.displayText() + ". Unable to parse File:", m_filename);
throw Kernel::Exception::FileError(exc.displayText() + ". Unable to parse XML", m_filename);
}
catch(...)
{
throw Kernel::Exception::FileError("Unable to parse File:" , m_filename);
throw Kernel::Exception::FileError("Unable to parse XML" , m_filename);
}
// Get pointer to root element
pRootElem = pDoc->documentElement();
Expand All @@ -115,6 +114,14 @@ namespace Geometry
throw Kernel::Exception::InstrumentDefinitionError("No root element in XML instrument file", m_filename);
}

// Create our new instrument
// 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 = boost::make_shared<Instrument>(m_instName);

// Save the XML file path and contents
m_instrument->setFilename(m_filename);
m_instrument->setXmlText(xmlText);

g_log.debug() << "====> InstrumentDefinitionParser::initialize finished" << std::endl;
}

Expand Down Expand Up @@ -151,14 +158,6 @@ namespace Geometry
if (!pDoc)
throw std::runtime_error("Call InstrumentDefinitionParser::initialize() before parseXML.");

// Create our new instrument
// 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);
m_instrument->setXmlText(m_xmlText);

setValidityRange(pRootElem);
readDefaults(pRootElem->getChildElement("defaults"));
// create maps: isTypeAssembly and mapTypeNameToShape
Expand Down Expand Up @@ -1768,16 +1767,25 @@ namespace Geometry
// Get cached file name
// If the instrument directory is writable, put them there else use temporary directory
std::string cacheFilename;
if (m_filename.size() > 4)
Poco::File defFile(m_filename);
Poco::File instrDir;
if ( defFile.exists() && m_filename.size() > 4 )
{
cacheFilename = std::string(m_filename.begin(),m_filename.end()-3);
instrDir = Poco::Path(defFile.path()).parent();
}
else
cacheFilename = m_instName + ".";
{
// If the IDF doesn't exist, the XML was probably passed directly into LoadInstrument
// (e.g. from a live data listener) and we should use the temp directory for the cache
// as we won't be able to tell when the definition has changed
instrDir = ConfigService::Instance().getTempDir();
cacheFilename = instrDir.path() + "/" + m_instName + ".";
}

cacheFilename += "vtp";
// check for the geometry cache
Poco::File defFile(m_filename);
Poco::File vtkFile(cacheFilename);
Poco::File instrDir(Poco::Path(defFile.path()).parent());

bool cacheAvailable = true;
if ((!vtkFile.exists()) || (defFile.exists() && (defFile.getLastModified() > vtkFile.getLastModified())))
Expand Down

0 comments on commit 26f7cc5

Please sign in to comment.