diff --git a/Framework/DataHandling/inc/MantidDataHandling/LoadInstrument.h b/Framework/DataHandling/inc/MantidDataHandling/LoadInstrument.h index 6e2a1c7b6d8e915f9df83392d48aaaa6ed9e4b6c..ad1f34c2c1c737f6721378f56b37847699fe5981 100644 --- a/Framework/DataHandling/inc/MantidDataHandling/LoadInstrument.h +++ b/Framework/DataHandling/inc/MantidDataHandling/LoadInstrument.h @@ -9,7 +9,6 @@ #include "MantidAPI/DistributedAlgorithm.h" #include "MantidAPI/ExperimentInfo.h" -#include "MantidGeometry/Instrument/InstrumentDefinitionParser.h" #include <mutex> @@ -80,13 +79,6 @@ public: const std::string category() const override { return "DataHandling\\Instrument"; } - /// Load instrument from IDF XML file - void idfInstrumentLoader(const boost::shared_ptr<API::MatrixWorkspace> &ws, - std::string filename, std::string instname, - const std::string &xmlString); - /// Load instrument from Nexus file - void nexusInstrumentLoader(const boost::shared_ptr<API::MatrixWorkspace> &ws, - std::string filename); private: void init() override; diff --git a/Framework/DataHandling/src/LoadInstrument.cpp b/Framework/DataHandling/src/LoadInstrument.cpp index e43b8e1a5ed60e38c31bb878c7cfb1c8adb12330..014881b29a6947e02bcbf8c58409b45e69998b8f 100644 --- a/Framework/DataHandling/src/LoadInstrument.cpp +++ b/Framework/DataHandling/src/LoadInstrument.cpp @@ -11,6 +11,7 @@ #include "MantidAPI/Progress.h" #include "MantidDataHandling/LoadGeometry.h" #include "MantidGeometry/Instrument.h" +#include "MantidGeometry/Instrument/InstrumentDefinitionParser.h" #include "MantidKernel/ArrayProperty.h" #include "MantidKernel/ConfigService.h" #include "MantidKernel/MandatoryValidator.h" @@ -100,6 +101,7 @@ void LoadInstrument::exec() { boost::shared_ptr<API::MatrixWorkspace> ws = getProperty("Workspace"); std::string filename = getPropertyValue("Filename"); std::string instname = getPropertyValue("InstrumentName"); + std::pair <std::string,std::string> loader_type; // If instrumentXML is not default (i.e. it has been defined), then use that // Note: this is part of the IDF loader @@ -114,8 +116,9 @@ void LoadInstrument::exec() { // name if (filename.empty()) filename = instname; - // Call IDF loader - idfInstrumentLoader(ws, filename, instname, InstrumentXML->value()); + + loader_type.first = "idf"; + loader_type.second = "xml"; } else { // This part of the loader searches through the instrument directories for @@ -145,66 +148,73 @@ void LoadInstrument::exec() { "Unable to find an Instrument File for instrument: ", instname); } + // Remove the path from the filename for use with the InstrumentDataService + const std::string::size_type stripPath = filename.find_last_of("\\/"); + std::string instrumentFile = + filename.substr(stripPath + 1, filename.size()); + // Strip off "_Definition.xml" + instname = instrumentFile.substr(0, instrumentFile.find("_Def")); + // Now that we have a file name, decide whether to use Nexus or IDF loading if (LoadGeometry::isIDF(filename)) { - // Remove the path from the filename for use with the - // InstrumentDataService - const std::string::size_type stripPath = filename.find_last_of("\\/"); - std::string instrumentFile = - filename.substr(stripPath + 1, filename.size()); - // Strip off "_Definition.xml" - instname = instrumentFile.substr(0, instrumentFile.find("_Def")); // Call theIDF loader with the the XML text loaded from the IDF file - idfInstrumentLoader(ws, filename, instname, Strings::loadFile(filename)); + loader_type.first = "idf"; + loader_type.second = "idffile"; } else if (LoadGeometry::isNexus(filename)) { - nexusInstrumentLoader(ws, filename); + // nexusInstrumentLoader(ws, filename, instname); + loader_type.first = "nxs"; + loader_type.second = "nxsfile"; } else { throw Kernel::Exception::FileError( "No valid loader found for instrument file ", filename); } } - // Set the monitors output property - setProperty("MonitorList", (ws->getInstrument())->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) - ws->rebuildSpectraMapping(); -} - -/// Load instrument from IDF XML file -void LoadInstrument::idfInstrumentLoader( - const boost::shared_ptr<API::MatrixWorkspace> &ws, std::string filename, - std::string instname, const std::string &xmlString) { + InstrumentDefinitionParser parser; + std::string instrumentNameMangled; + Instrument_sptr instrument; - auto parser = InstrumentDefinitionParser(filename, instname, xmlString); + // Define a parser if using IDFs + if (loader_type.second == "xml") + parser = InstrumentDefinitionParser(filename, instname, InstrumentXML->value()); + else if (loader_type.second == "idffile") + parser = InstrumentDefinitionParser(filename, instname, Strings::loadFile(filename)); // Find the mangled instrument name that includes the modified date - std::string instrumentNameMangled = parser.getMangledName(); + if (loader_type.first == "idf") + instrumentNameMangled = parser.getMangledName(); + else if (loader_type.first == "nxs") + instrumentNameMangled = + NexusGeometry::NexusGeometryParser::getMangledName(filename, instname); + else + throw std::runtime_error("Unknown instrument loader type"); - Instrument_sptr instrument; - // Check whether the instrument is already in the InstrumentDataService { // Make InstrumentService access thread-safe std::lock_guard<std::recursive_mutex> lock(m_mutex); + // 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 instrument = InstrumentDataService::Instance().retrieve(instrumentNameMangled); } else { - // Really create the instrument - Progress prog(this, 0.0, 1.0, 100); - instrument = parser.parseXML(&prog); - // Parse the instrument tree (internally create ComponentInfo and - // DetectorInfo). This is an optimization that avoids duplicate parsing of - // the instrument tree when loading multiple workspaces with the same - // instrument. As a consequence less time is spent and less memory is - // used. Note that this is only possible since the tree in `instrument` - // will not be modified once we add it to the IDS. - instrument->parseTreeAndCacheBeamline(); + + if (loader_type.first == "idf") { + // Really create the instrument + Progress prog(this, 0.0, 1.0, 100); + instrument = parser.parseXML(&prog); + // Parse the instrument tree (internally create ComponentInfo and + // DetectorInfo). This is an optimization that avoids duplicate parsing of + // the instrument tree when loading multiple workspaces with the same + // instrument. As a consequence less time is spent and less memory is + // used. Note that this is only possible since the tree in `instrument` + // will not be modified once we add it to the IDS. + instrument->parseTreeAndCacheBeamline(); + } else { + Instrument_const_sptr ins = NexusGeometry::NexusGeometryParser::createInstrument(filename); + instrument = boost::const_pointer_cast<Instrument>(ins); + } // Add to data service for later retrieval InstrumentDataService::Instance().add(instrumentNameMangled, instrument); } @@ -216,18 +226,18 @@ void LoadInstrument::idfInstrumentLoader( // LoadParameterFile modifies the base instrument stored in the IDS so this // must also be protected by the lock until LoadParameterFile is fixed. // check if default parameter file is also present, unless loading from - if (!filename.empty()) + if (!filename.empty() && (loader_type.first == "idf")) runLoadParameterFile(ws, filename); - } -} + } // end of mutex scope -/// Load instrument from Nexus file -void LoadInstrument::nexusInstrumentLoader( - const boost::shared_ptr<API::MatrixWorkspace> &ws, std::string filename) { - Instrument_const_sptr instrument = - NexusGeometry::NexusGeometryParser::createInstrument(filename); - ws->setInstrument(instrument); - ws->populateInstrumentParameters(); + // Set the monitors output property + setProperty("MonitorList", (ws->getInstrument())->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) + ws->rebuildSpectraMapping(); } //----------------------------------------------------------------------------------------------------------------------- diff --git a/Framework/DataHandling/test/LoadInstrumentTest.h b/Framework/DataHandling/test/LoadInstrumentTest.h index a455f294fdd9979ce02313dee2db4a7ebf5b8d44..948e8411c27839363f1842ecf88c6670fa8b4858 100644 --- a/Framework/DataHandling/test/LoadInstrumentTest.h +++ b/Framework/DataHandling/test/LoadInstrumentTest.h @@ -243,9 +243,6 @@ public: loaderNIMROD.execute(); TS_ASSERT(loaderNIMROD.isExecuted()); - // Get back the saved workspace - // MatrixWorkspace_sptr output = loaderNIMROD.getProperty("Workspace"); - const auto &detectorInfo = ws2D->detectorInfo(); const auto &ptrDet = detectorInfo.detector(detectorInfo.indexOf(20201001)); TS_ASSERT_EQUALS(ptrDet.getName(), "det 1"); @@ -255,6 +252,36 @@ public: TS_ASSERT_DELTA(ptrDet.getPos().Z(), 4.8888, 0.0001); } + void testExecNIMRODandRetrieveFromIDS() { + // Make sure the IDS is empty + InstrumentDataServiceImpl &IDS = InstrumentDataService::Instance(); + IDS.clear(); + + LoadInstrument loaderNIMROD; + loaderNIMROD.initialize(); + loaderNIMROD.setChild(true); + + // create a workspace with some sample data + MatrixWorkspace_sptr ws2D = + DataObjects::create<Workspace2D>(1, HistogramData::Points(1)); + + const std::string instrFilename = "NIM_Definition.xml"; + loaderNIMROD.setPropertyValue("Filename", instrFilename); + loaderNIMROD.setProperty("RewriteSpectraMap", OptionalBool(true)); + loaderNIMROD.setProperty("Workspace", ws2D); + loaderNIMROD.execute(); + TS_ASSERT(loaderNIMROD.isExecuted()); + + TS_ASSERT_EQUALS(IDS.size(), 1); + if (IDS.size() != 1) + return; + // Retrieve the instrument from the InstrumentDataService + Instrument_const_sptr nimrodInst = IDS.getObjects()[0]; + TS_ASSERT_EQUALS(nimrodInst->getName(), "NIM"); + TS_ASSERT_EQUALS(nimrodInst->getNumberDetectors(),1521); + TS_ASSERT_EQUALS((nimrodInst->getDetector(20201001))->getID(),20201001); + } + void testExecMARIFromInstrName() { LoadInstrument loaderMARI; loaderMARI.initialize(); @@ -299,53 +326,6 @@ public: TS_ASSERT_DELTA(ptrDet2.getPos().Z(), 3.9211, 0.0001); } - // // Test loading from XML - // void testExecFromXML() { - // LoadInstrument loaderXML; - // loaderXML.initialize(); - // loaderXML.setChild(true); - // - // // create a workspace with some sample data - // MatrixWorkspace_sptr ws2D = - // DataObjects::create<Workspace2D>(1, HistogramData::Points(1)); - // - // - // const std::string instrumentXML = - // Kernel::Strings::loadFile(Kernel::ConfigService::Instance().getFullPath("REFL_Definition.xml")); - // loaderMARI.setPropertyValue("InstrumentName", instrName); - // loaderMARI.setProperty("RewriteSpectraMap", OptionalBool(true)); - // loaderMARI.setProperty("Workspace", ws2D); - // - // loaderMARI.execute(); - // TS_ASSERT(loaderMARI.isExecuted()); - // - // std::string result = loaderMARI.getPropertyValue("Filename"); - // const std::string::size_type stripPath = result.find_last_of("\\/"); - // result = result.substr(stripPath + 1, result.size()); - // TS_ASSERT_EQUALS(result, "MARI_Definition.xml"); - // - // auto &componentInfo = ws2D->componentInfo(); - // auto &detectorInfo = ws2D->detectorInfo(); - // TS_ASSERT_EQUALS(componentInfo.name(componentInfo.root()), "MARI"); - // TS_ASSERT_EQUALS(detectorInfo.size(), 921); - // TS_ASSERT_EQUALS(1, detectorInfo.detectorIDs()[0]); - // TS_ASSERT_EQUALS(4816, detectorInfo.detectorIDs()[920]); - // - // const auto &ptrDet1 = detectorInfo.detector(detectorInfo.indexOf(1)); - // TS_ASSERT_EQUALS(ptrDet1.getName(), "monitor"); - // TS_ASSERT_EQUALS(ptrDet1.getID(), 1); - // TS_ASSERT_DELTA(ptrDet1.getPos().X(), 0.0000, 0.0001); - // TS_ASSERT_DELTA(ptrDet1.getPos().Y(), 0.0000, 0.0001); - // TS_ASSERT_DELTA(ptrDet1.getPos().Z(), -4.7390, 0.0001); - // - // const auto &ptrDet2 = detectorInfo.detector(detectorInfo.indexOf(4816)); - // TS_ASSERT_EQUALS(ptrDet2.getName(), "tall He3 element"); - // TS_ASSERT_EQUALS(ptrDet2.getID(), 4816); - // TS_ASSERT_DELTA(ptrDet2.getPos().X(), 0.6330, 0.0001); - // TS_ASSERT_DELTA(ptrDet2.getPos().Y(), 0.6330, 0.0001); - // TS_ASSERT_DELTA(ptrDet2.getPos().Z(), 3.9211, 0.0001); - // } - /// Common initialisation for Nexus loading tests MatrixWorkspace_sptr doLoadNexus(const std::string filename) { LoadInstrument nexusLoader; @@ -429,6 +409,23 @@ public: TS_ASSERT_EQUALS(1, detectorInfo.detectorIDs()[1]); } + /// Test the Nexus geometry loader from LOKI file + void testExecNexusLOKIandRetrieveFromIDS() { + // Make sure the IDS is empty + InstrumentDataServiceImpl &IDS = InstrumentDataService::Instance(); + IDS.clear(); + MatrixWorkspace_sptr outputWs = doLoadNexus("LOKI_Definition.hdf5"); + TS_ASSERT_EQUALS(IDS.size(), 1); + if (IDS.size() != 1) + return; + // Retrieve the instrument from the InstrumentDataService + Instrument_const_sptr lokiInst = IDS.getObjects()[0]; + TS_ASSERT_EQUALS(lokiInst->getName(), "LOKI"); + TS_ASSERT_EQUALS(lokiInst->getNumberDetectors(),8000); + TS_ASSERT_EQUALS((lokiInst->getDetector(1001))->getID(),1001); + TS_ASSERT_EQUALS((lokiInst->getDetector(7777))->getID(),7777); + } + void testExecHRP2() { // Test Parameter file in instrument folder is used by an IDF file not in // the instrument folder diff --git a/Framework/NexusGeometry/inc/MantidNexusGeometry/NexusGeometryParser.h b/Framework/NexusGeometry/inc/MantidNexusGeometry/NexusGeometryParser.h index 29666462225a27448584b66d348b56419523d198..8676f38c21d6ab1a485d1e384932c8c9e955edaf 100644 --- a/Framework/NexusGeometry/inc/MantidNexusGeometry/NexusGeometryParser.h +++ b/Framework/NexusGeometry/inc/MantidNexusGeometry/NexusGeometryParser.h @@ -22,6 +22,8 @@ namespace NexusGeometry { namespace NexusGeometryParser { MANTID_NEXUSGEOMETRY_DLL std::unique_ptr<const Mantid::Geometry::Instrument> createInstrument(const std::string &fileName); +MANTID_NEXUSGEOMETRY_DLL std::string getMangledName(const std::string &fileName, + const std::string &instName); } // namespace NexusGeometryParser } // namespace NexusGeometry } // namespace Mantid diff --git a/Framework/NexusGeometry/src/NexusGeometryParser.cpp b/Framework/NexusGeometry/src/NexusGeometryParser.cpp index 407a648ff4f387584c4a99c4e2dd1680191deab5..199d483f9d35ad9e0899e8bf388e411713012187 100644 --- a/Framework/NexusGeometry/src/NexusGeometryParser.cpp +++ b/Framework/NexusGeometry/src/NexusGeometryParser.cpp @@ -10,6 +10,7 @@ #include "MantidGeometry/Objects/ShapeFactory.h" #include "MantidGeometry/Rendering/GeometryHandler.h" #include "MantidGeometry/Rendering/ShapeInfo.h" +#include "MantidKernel/ChecksumHelper.h" #include "MantidKernel/EigenConversionHelpers.h" #include "MantidKernel/make_unique.h" #include "MantidNexusGeometry/InstrumentBuilder.h" @@ -672,5 +673,16 @@ NexusGeometryParser::createInstrument(const std::string &fileName) { auto rootGroup = file.openGroup("/"); return extractInstrument(file, rootGroup); } + +// Create a unique instrument name from Nexus file +std::string NexusGeometryParser::getMangledName(const std::string &fileName, + const std::string &instName) { + std::string mangledName = instName; + if (!fileName.empty()) { + std::string checksum = Mantid::Kernel::ChecksumHelper::sha1FromFile(fileName, false); + mangledName += checksum; + } + return mangledName; +} } // namespace NexusGeometry } // namespace Mantid