From 6bba0d02452ca818af9bc49d2e2a6be976045d52 Mon Sep 17 00:00:00 2001 From: Martyn Gigg <martyn.gigg@stfc.ac.uk> Date: Wed, 1 Jun 2011 12:46:29 +0000 Subject: [PATCH] Refs #3106. Created a new algorithm that replaces LoadLogsFromSNSNexus and can be used for any of the NXlog and IXseblock entries in a NeXus file. LoadEventNexus and LoadISISNexus2 now use these also. --- Code/Mantid/Framework/Nexus/CMakeLists.txt | 135 +++--- .../Nexus/inc/MantidNexus/LoadISISNexus2.h | 2 +- .../inc/MantidNexus/LoadLogsFromSNSNexus.h | 4 +- .../Nexus/inc/MantidNexus/LoadRunLogs.h | 100 +++++ .../Framework/Nexus/src/LoadEventNexus.cpp | 7 +- .../Framework/Nexus/src/LoadISISNexus2.cpp | 112 ++--- .../Nexus/src/LoadLogsFromSNSNexus.cpp | 11 +- .../Framework/Nexus/src/LoadRunLogs.cpp | 396 ++++++++++++++++++ .../Framework/Nexus/test/LoadISISNexusTest.h | 47 ++- .../Framework/Nexus/test/LoadRunLogsTest.h | 76 ++++ 10 files changed, 715 insertions(+), 175 deletions(-) create mode 100644 Code/Mantid/Framework/Nexus/inc/MantidNexus/LoadRunLogs.h create mode 100644 Code/Mantid/Framework/Nexus/src/LoadRunLogs.cpp create mode 100644 Code/Mantid/Framework/Nexus/test/LoadRunLogsTest.h diff --git a/Code/Mantid/Framework/Nexus/CMakeLists.txt b/Code/Mantid/Framework/Nexus/CMakeLists.txt index 53ec8d5c48b..f4fcc351433 100644 --- a/Code/Mantid/Framework/Nexus/CMakeLists.txt +++ b/Code/Mantid/Framework/Nexus/CMakeLists.txt @@ -1,79 +1,82 @@ set ( SRC_FILES - src/LoadEventNexus.cpp - src/LoadISISNexus.cpp - src/LoadISISNexus2.cpp - src/LoadInstrumentFromNexus.cpp - src/LoadLogsFromSNSNexus.cpp - src/LoadMuonLog.cpp - src/LoadMuonNexus.cpp - src/LoadMuonNexus2.cpp - src/LoadNeXus.cpp - src/LoadNexusMonitors.cpp - src/LoadNexusProcessed.cpp - src/LoadRaw/byte_rel_comp.cpp - src/LoadRaw/isisraw.cpp - src/LoadRaw/isisraw2.cpp - src/LoadRaw/item_struct.cpp - src/LoadRaw/vms_convert.cpp - src/LoadSNSEventNexus.cpp - src/LoadSNSNexus.cpp - src/LoadTOFRawNeXus.cpp - src/MuonNexusReader.cpp - src/NeXusException.cpp - src/NeXusFile.cpp - src/NeXusStream.cpp - src/NexusClasses.cpp - src/NexusFileIO.cpp - src/SaveISISNeXus.cpp - src/SaveNeXus.cpp - src/SaveNexusProcessed.cpp - src/SaveToSNSHistogramNexus.cpp + src/LoadEventNexus.cpp + src/LoadISISNexus.cpp + src/LoadISISNexus2.cpp + src/LoadInstrumentFromNexus.cpp + src/LoadLogsFromSNSNexus.cpp + src/LoadMuonLog.cpp + src/LoadMuonNexus.cpp + src/LoadMuonNexus2.cpp + src/LoadNeXus.cpp + src/LoadNexusMonitors.cpp + src/LoadNexusProcessed.cpp + src/LoadRaw/byte_rel_comp.cpp + src/LoadRaw/isisraw.cpp + src/LoadRaw/isisraw2.cpp + src/LoadRaw/item_struct.cpp + src/LoadRaw/vms_convert.cpp + src/LoadRunLogs.cpp + src/LoadSNSEventNexus.cpp + src/LoadSNSNexus.cpp + src/LoadTOFRawNeXus.cpp + src/MuonNexusReader.cpp + src/NeXusException.cpp + src/NeXusFile.cpp + src/NeXusStream.cpp + src/NexusClasses.cpp + src/NexusFileIO.cpp + src/SaveISISNeXus.cpp + src/SaveNeXus.cpp + src/SaveNexusProcessed.cpp + src/SaveToSNSHistogramNexus.cpp ) set ( SRC_UNITY_IGNORE_FILES src/NeXusFile.cpp) set ( INC_FILES - inc/MantidNexus/LoadEventNexus.h - inc/MantidNexus/LoadISISNexus.h - inc/MantidNexus/LoadISISNexus2.h - inc/MantidNexus/LoadInstrumentFromNexus.h - inc/MantidNexus/LoadLogsFromSNSNexus.h - inc/MantidNexus/LoadMuonLog.h - inc/MantidNexus/LoadMuonNexus.h - inc/MantidNexus/LoadMuonNexus2.h - inc/MantidNexus/LoadNeXus.h - inc/MantidNexus/LoadNexusMonitors.h - inc/MantidNexus/LoadNexusProcessed.h - inc/MantidNexus/LoadSNSEventNexus.h - inc/MantidNexus/LoadSNSNexus.h - inc/MantidNexus/LoadTOFRawNeXus.h - inc/MantidNexus/MuonNexusReader.h - inc/MantidNexus/NexusClasses.h - inc/MantidNexus/NexusFileIO.h - inc/MantidNexus/SaveISISNeXus.h - inc/MantidNexus/SaveNeXus.h - inc/MantidNexus/SaveNexusProcessed.h - inc/MantidNexus/SaveToSNSHistogramNexus.h + inc/MantidNexus/LoadEventNexus.h + inc/MantidNexus/LoadISISNexus.h + inc/MantidNexus/LoadISISNexus2.h + inc/MantidNexus/LoadInstrumentFromNexus.h + inc/MantidNexus/LoadLogsFromSNSNexus.h + inc/MantidNexus/LoadMuonLog.h + inc/MantidNexus/LoadMuonNexus.h + inc/MantidNexus/LoadMuonNexus2.h + inc/MantidNexus/LoadNeXus.h + inc/MantidNexus/LoadNexusMonitors.h + inc/MantidNexus/LoadNexusProcessed.h + inc/MantidNexus/LoadRunLogs.h + inc/MantidNexus/LoadSNSEventNexus.h + inc/MantidNexus/LoadSNSNexus.h + inc/MantidNexus/LoadTOFRawNeXus.h + inc/MantidNexus/MuonNexusReader.h + inc/MantidNexus/NexusClasses.h + inc/MantidNexus/NexusFileIO.h + inc/MantidNexus/SaveISISNeXus.h + inc/MantidNexus/SaveNeXus.h + inc/MantidNexus/SaveNexusProcessed.h + inc/MantidNexus/SaveToSNSHistogramNexus.h ) set ( TEST_FILES - #test/LoadSNSNexusTest.h # TODO has no active tests in it - #test/LoadTOFRawNeXusTest.h # TODO has no active tests in it - test/LoadEventNexusTest.h - test/LoadISISNexusTest.h - test/LoadLogsFromSNSNexusTest.h - test/LoadMuonLogTest.h - test/LoadMuonNexus2Test.h - test/LoadMuonNexusTest.h - test/LoadNeXusTest.h - test/LoadNexusMonitorsTest.h - test/LoadNexusProcessedTest.h - test/LoadRSaveNLoadNcspTest.h - test/LoadSNSEventNexusTest.h - test/NexusAPITest.h - test/SaveNeXusTest.h - test/SaveNexusProcessedTest.h + #test/LoadSNSNexusTest.h # TODO has no active tests in it + #test/LoadTOFRawNeXusTest.h # TODO has no active tests in it + test/LoadEventNexusTest.h + test/LoadISISNexusTest.h + test/LoadLogsFromSNSNexusTest.h + test/LoadMuonLogTest.h + test/LoadMuonNexus2Test.h + test/LoadMuonNexusTest.h + test/LoadNeXusTest.h + test/LoadNexusMonitorsTest.h + test/LoadNexusProcessedTest.h + test/LoadRunLogsTest.h + test/LoadRSaveNLoadNcspTest.h + test/LoadSNSEventNexusTest.h + test/NexusAPITest.h + test/SaveNeXusTest.h + test/SaveNexusProcessedTest.h ) if(UNITY_BUILD) diff --git a/Code/Mantid/Framework/Nexus/inc/MantidNexus/LoadISISNexus2.h b/Code/Mantid/Framework/Nexus/inc/MantidNexus/LoadISISNexus2.h index eff586312d0..3b7071054af 100644 --- a/Code/Mantid/Framework/Nexus/inc/MantidNexus/LoadISISNexus2.h +++ b/Code/Mantid/Framework/Nexus/inc/MantidNexus/LoadISISNexus2.h @@ -96,7 +96,7 @@ namespace Mantid /// Load in details about the sample void loadSampleData(DataObjects::Workspace2D_sptr, NXEntry & entry); /// Load log data from the nexus file - void loadLogs(DataObjects::Workspace2D_sptr, NXEntry & entry,int period = 1); + void loadLogs(DataObjects::Workspace2D_sptr, int period = 1); // Load a given period into the workspace void loadPeriodData(int64_t period, NXEntry & entry, DataObjects::Workspace2D_sptr local_workspace); // Load a data block diff --git a/Code/Mantid/Framework/Nexus/inc/MantidNexus/LoadLogsFromSNSNexus.h b/Code/Mantid/Framework/Nexus/inc/MantidNexus/LoadLogsFromSNSNexus.h index 900c507b4f7..30b97117bc7 100644 --- a/Code/Mantid/Framework/Nexus/inc/MantidNexus/LoadLogsFromSNSNexus.h +++ b/Code/Mantid/Framework/Nexus/inc/MantidNexus/LoadLogsFromSNSNexus.h @@ -14,6 +14,7 @@ #include "MantidNexus/NeXusFile.hpp" #include "MantidNexus/NeXusException.hpp" +#include "MantidAPI/DeprecatedAlgorithm.h" namespace Mantid { @@ -60,8 +61,9 @@ namespace Mantid File change history is stored at: <https://svn.mantidproject.org/mantid/trunk/Code/Mantid> */ - class DLLExport LoadLogsFromSNSNexus : public API::Algorithm + class DLLExport LoadLogsFromSNSNexus : public API::Algorithm, public API::DeprecatedAlgorithm { + public: /// Default constructor LoadLogsFromSNSNexus(); diff --git a/Code/Mantid/Framework/Nexus/inc/MantidNexus/LoadRunLogs.h b/Code/Mantid/Framework/Nexus/inc/MantidNexus/LoadRunLogs.h new file mode 100644 index 00000000000..a6b3d84524f --- /dev/null +++ b/Code/Mantid/Framework/Nexus/inc/MantidNexus/LoadRunLogs.h @@ -0,0 +1,100 @@ +#ifndef MANTID_DATAHANDLING_LOADRUNLOGS_H_ +#define MANTID_DATAHANDLING_LOADRUNLOGS_H_ + +//---------------------------------------------------------------------- +// Includes +//---------------------------------------------------------------------- +#include "MantidAPI/Algorithm.h" +#include "MantidNexus/NeXusFile.hpp" + +namespace Mantid +{ + //---------------------------------------------------------------------- + // Forward declaration + //---------------------------------------------------------------------- + namespace Kernel + { + class Property; + } + namespace API + { + class MatrixWorkspace; + } + + namespace NeXus + { + + /** + + Loads the run logs from a NeXus file. + + Required Properties: + <UL> + <LI> Filename - The name of and path to the input Nexus file </LI> + <LI> Workspace - The name of the workspace in which to store the imported data.</LI> + </UL> + + @author Martyn Gigg, Tessella plc + + Copyright © 2011 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory + + This file is part of Mantid. + + Mantid is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + Mantid is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + File change history is stored at: <https://svn.mantidproject.org/mantid/trunk/Code/Mantid>. + Code Documentation is available at: <http://doxygen.mantidproject.org> + */ + class DLLExport LoadRunLogs : public API::Algorithm + { + public: + /// Default constructor + LoadRunLogs(); + /// Destructor + virtual ~LoadRunLogs() {} + /// Algorithm's name for identification overriding a virtual method + virtual const std::string name() const { return "LoadRunLogs"; } + /// Algorithm's version for identification overriding a virtual method + virtual int version() const { return 1; } + /// Algorithm's category for identification overriding a virtual method + virtual const std::string category() const { return "DataHandling"; } + + private: + /// Overwrites Algorithm method. + void initDocs(); + /// Overwrites Algorithm method. + void init(); + /// Overwrites Algorithm method + void exec(); + /// Load log data from a group + void loadLogs(::NeXus::File & file, const std::string & entry_name, + const std::string & entry_class, + boost::shared_ptr<API::MatrixWorkspace> workspace) const; + /// Load an NXlog entry + void loadNXLog(::NeXus::File & file, const std::string & entry_name, + boost::shared_ptr<API::MatrixWorkspace> workspace) const; + /// Load an IXseblock entry + void loadSELog(::NeXus::File & file, const std::string & entry_name, + boost::shared_ptr<API::MatrixWorkspace> workspace) const; + /// Create a time series property + Kernel::Property * createTimeSeries(::NeXus::File & file, + const std::string & prop_name) const; + ///Progress reporting object + boost::shared_ptr<API::Progress> m_progress; + }; + + } // namespace NeXus +} // namespace Mantid + +#endif /*MANTID_DATAHANDLING_LOADRUNLOGS_H_*/ diff --git a/Code/Mantid/Framework/Nexus/src/LoadEventNexus.cpp b/Code/Mantid/Framework/Nexus/src/LoadEventNexus.cpp index 3f55662ea43..9c3f1fd3700 100644 --- a/Code/Mantid/Framework/Nexus/src/LoadEventNexus.cpp +++ b/Code/Mantid/Framework/Nexus/src/LoadEventNexus.cpp @@ -627,10 +627,6 @@ void LoadEventNexus::init() declareProperty( new PropertyWithValue<bool>("LoadMonitors", false, Direction::Input), "Load the monitors from the file (optional, default False)."); -// -// declareProperty( -// new PropertyWithValue<bool>("LoadLogs", true, Direction::Input), -// "Load the sample logs from the file (optional, default True)."); declareProperty( new PropertyWithValue<bool>("Precount", false, Direction::Input), @@ -657,7 +653,6 @@ void LoadEventNexus::exec() precount = getProperty("Precount"); compressTolerance = getProperty("CompressTolerance"); - //loadlogs = getProperty("LoadLogs"); loadlogs = true; //Get the limits to the filter @@ -709,7 +704,7 @@ void LoadEventNexus::exec() prog.doReport("Loading DAS logs"); //The pulse times will be empty if not specified in the DAS logs. pulseTimes.clear(); - IAlgorithm_sptr loadLogs = createSubAlgorithm("LoadLogsFromSNSNexus"); + IAlgorithm_sptr loadLogs = createSubAlgorithm("LoadRunLogs"); // Now execute the sub-algorithm. Catch and log any error, but don't stop. try diff --git a/Code/Mantid/Framework/Nexus/src/LoadISISNexus2.cpp b/Code/Mantid/Framework/Nexus/src/LoadISISNexus2.cpp index be1662f97d9..9234e7ec4c0 100644 --- a/Code/Mantid/Framework/Nexus/src/LoadISISNexus2.cpp +++ b/Code/Mantid/Framework/Nexus/src/LoadISISNexus2.cpp @@ -213,7 +213,7 @@ namespace Mantid loadSampleData(local_workspace, entry); m_progress->report("Loading logs"); - loadLogs(local_workspace, entry); + loadLogs(local_workspace); // Load first period outside loop m_progress->report("Loading data"); @@ -271,7 +271,7 @@ namespace Mantid { //Check the numbers supplied are not in the range and erase the ones that are struct range_check - { + { range_check(int64_t min, int64_t max) : m_min(min), m_max(max) {} bool operator()(int64_t x) @@ -609,26 +609,26 @@ namespace Mantid // RPB struct info NXInt rpb_int = vms_compat.openNXInt("IRPB"); rpb_int.load(); - runDetails.addProperty("dur", rpb_int[0]); // actual run duration - runDetails.addProperty("durunits", rpb_int[1]); // scaler for above (1=seconds) + runDetails.addProperty("dur", rpb_int[0]); // actual run duration + runDetails.addProperty("durunits", rpb_int[1]); // scaler for above (1=seconds) runDetails.addProperty("dur_freq", rpb_int[2]); // testinterval for above (seconds) runDetails.addProperty("dmp", rpb_int[3]); // dump interval - runDetails.addProperty("dmp_units", rpb_int[4]); // scaler for above - runDetails.addProperty("dmp_freq", rpb_int[5]); // interval for above - runDetails.addProperty("freq", rpb_int[6]); // 2**k where source frequency = 50 / 2**k + runDetails.addProperty("dmp_units", rpb_int[4]); // scaler for above + runDetails.addProperty("dmp_freq", rpb_int[5]); // interval for above + runDetails.addProperty("freq", rpb_int[6]); // 2**k where source frequency = 50 / 2**k // Now double data NXFloat rpb_dbl = vms_compat.openNXFloat("RRPB"); rpb_dbl.load(); runDetails.addProperty("gd_prtn_chrg", static_cast<double>(rpb_dbl[7])); // good proton charge (uA.hour) runDetails.addProperty("tot_prtn_chrg", static_cast<double>(rpb_dbl[8])); // total proton charge (uA.hour) - runDetails.addProperty("goodfrm",rpb_int[9]); // good frames - runDetails.addProperty("rawfrm", rpb_int[10]); // raw frames + runDetails.addProperty("goodfrm",rpb_int[9]); // good frames + runDetails.addProperty("rawfrm", rpb_int[10]); // raw frames runDetails.addProperty("dur_wanted", rpb_int[11]); // requested run duration (units as for "duration" above) - runDetails.addProperty("dur_secs", rpb_int[12]); // actual run duration in seconds - runDetails.addProperty("mon_sum1", rpb_int[13]); // monitor sum 1 - runDetails.addProperty("mon_sum2", rpb_int[14]); // monitor sum 2 - runDetails.addProperty("mon_sum3",rpb_int[15]); // monitor sum 3 + runDetails.addProperty("dur_secs", rpb_int[12]); // actual run duration in seconds + runDetails.addProperty("mon_sum1", rpb_int[13]); // monitor sum 1 + runDetails.addProperty("mon_sum2", rpb_int[14]); // monitor sum 2 + runDetails.addProperty("mon_sum3",rpb_int[15]); // monitor sum 3 // End date and time is stored separately in ISO format in the "raw_data1/endtime" class char_data = entry.openNXChar("end_time"); @@ -699,78 +699,34 @@ namespace Mantid * /raw_data_1/runlog group of the file. Call to this method must be done * within /raw_data_1 group. * @param ws :: The workspace to load the logs to. - * @param entry :: The Nexus entry * @param period :: The period of this workspace */ - void LoadISISNexus2::loadLogs(DataObjects::Workspace2D_sptr ws, NXEntry & entry,int period) + void LoadISISNexus2::loadLogs(DataObjects::Workspace2D_sptr ws, int period) { - - NXMainClass runlogs = entry.openNXClass<NXMainClass>("runlog"); - - for(std::vector<NXClassInfo>::const_iterator it=runlogs.groups().begin();it!=runlogs.groups().end();it++) + IAlgorithm_sptr alg = createSubAlgorithm("LoadRunLogs", 0.0, 0.5); + alg->setPropertyValue("Filename", this->getProperty("Filename")); + alg->setProperty<MatrixWorkspace_sptr>("Workspace", ws); + try { - if (it->nxclass == "NXlog") - { - - NXLog nxLog(runlogs,it->nxname); - nxLog.openLocal(); - - Kernel::Property* logv = nxLog.createTimeSeries(); - if (!logv) - { - nxLog.close(); - continue; - } - ws->mutableRun().addLogData(logv); - if (it->nxname == "icp_event") - { - LogParser parser(logv); - ws->mutableRun().addLogData(parser.createPeriodLog(period)); - ws->mutableRun().addLogData(parser.createAllPeriodsLog()); - ws->mutableRun().addLogData(parser.createRunningLog()); - } - nxLog.close(); - } + alg->executeAsSubAlg(); } - - NXMainClass selogs = entry.openNXClass<NXMainClass>("selog"); - for(std::vector<NXClassInfo>::const_iterator it=selogs.groups().begin();it!=selogs.groups().end();it++) + catch(std::runtime_error&) { - if (it->nxclass == "IXseblock") - { - NXMainClass selog(selogs,it->nxname); - selog.openLocal("IXseblock"); - NXLog nxLog(selog,"value_log"); - bool ok = nxLog.openLocal(); - std::string propName = it->nxname; - if (ws->run().hasProperty(propName)) - { - propName = "selog_"+propName; - } - - if (ok) - { - Kernel::Property* logv = nxLog.createTimeSeries("",propName); - if (!logv) - { - nxLog.close(); - selog.close(); - continue; - } - ws->mutableRun().addLogData(logv); - nxLog.close(); - } - else - { - NXFloat value = selog.openNXFloat("value"); - value.load(); - ws->mutableRun().addProperty(propName,(double)*value()); - } - selog.close(); - } + g_log.warning() << "Unable to load run logs. There will be no log " + << "data associated with this workspace\n"; + return; } - ws->populateInstrumentParameters(); + // If we loaded an icp_event log then create the necessary period logs + if( ws->run().hasProperty("icp_event") ) + { + Kernel::Property *log = ws->run().getProperty("icp_event"); + LogParser parser(log); + ws->mutableRun().addProperty(parser.createPeriodLog(period)); + ws->mutableRun().addProperty(parser.createAllPeriodsLog()); + ws->mutableRun().addProperty(parser.createRunningLog()); + } + } double LoadISISNexus2::dblSqrt(double in) @@ -798,7 +754,7 @@ namespace Mantid return true; } else if ( (nread >= sizeof(g_hdf5_signature)) && - (!memcmp(header.full_hdr, g_hdf5_signature, sizeof(g_hdf5_signature))) ) + (!memcmp(header.full_hdr, g_hdf5_signature, sizeof(g_hdf5_signature))) ) { //hdf5 return true; diff --git a/Code/Mantid/Framework/Nexus/src/LoadLogsFromSNSNexus.cpp b/Code/Mantid/Framework/Nexus/src/LoadLogsFromSNSNexus.cpp index bce972d96c3..067180ef5f5 100644 --- a/Code/Mantid/Framework/Nexus/src/LoadLogsFromSNSNexus.cpp +++ b/Code/Mantid/Framework/Nexus/src/LoadLogsFromSNSNexus.cpp @@ -41,7 +41,10 @@ using Geometry::Instrument; /// Empty default constructor LoadLogsFromSNSNexus::LoadLogsFromSNSNexus() -{} +{ + useAlgorithm("LoadRunLogs"); + deprecatedDate("2011-06-01"); +} /// Initialisation method. void LoadLogsFromSNSNexus::init() @@ -52,9 +55,9 @@ void LoadLogsFromSNSNexus::init() "The name of the workspace in which to import the sample logs." ); declareProperty(new FileProperty("Filename", "", FileProperty::Load, ".nxs"), - "The name (including its full or relative path) of the Nexus file to\n" - "attempt to load the instrument from. The file extension must either be\n" - ".nxs or .NXS" ); + "The name (including its full or relative path) of the Nexus file to\n" + "attempt to load the instrument from. The file extension must either be\n" + ".nxs or .NXS" ); declareProperty(new PropertyWithValue<bool>("OverwriteLogs", true, Direction::Input)); } diff --git a/Code/Mantid/Framework/Nexus/src/LoadRunLogs.cpp b/Code/Mantid/Framework/Nexus/src/LoadRunLogs.cpp new file mode 100644 index 00000000000..297d66953fa --- /dev/null +++ b/Code/Mantid/Framework/Nexus/src/LoadRunLogs.cpp @@ -0,0 +1,396 @@ +//---------------------------------------------------------------------- +// Includes +//---------------------------------------------------------------------- +#include "MantidNexus/LoadRunLogs.h" +#include "MantidNexus/NeXusException.hpp" +#include "MantidKernel/TimeSeriesProperty.h" +#include "MantidAPI/FileProperty.h" +#include <cctype> + +#include <Poco/Path.h> +#include <Poco/DateTimeFormatter.h> +#include <Poco/DateTimeParser.h> +#include <Poco/DateTimeFormat.h> + +#include <boost/scoped_array.hpp> + +namespace Mantid +{ + namespace NeXus + { + // Register the algorithm into the algorithm factory + DECLARE_ALGORITHM(LoadRunLogs) + + /// Sets documentation strings for this algorithm + void LoadRunLogs::initDocs() + { + this->setWikiSummary("Loads run logs (temperature, pulse charges, etc.) from a NeXus file and adds it to the run information in a [[workspace]]."); + this->setOptionalMessage("Loads run logs (temperature, pulse charges, etc.) from a NeXus file and adds it to the run information in a workspace."); + } + + using namespace Kernel; + using API::WorkspaceProperty; + using API::MatrixWorkspace; + using API::MatrixWorkspace_sptr; + using API::FileProperty; + using std::size_t; + + /// Empty default constructor + LoadRunLogs::LoadRunLogs() + {} + + /// Initialisation method. + void LoadRunLogs::init() + { + declareProperty(new WorkspaceProperty<MatrixWorkspace>("Workspace","Anonymous",Direction::InOut)); + std::vector<std::string> exts; + exts.push_back(".nxs"); + exts.push_back(".n*"); + declareProperty(new FileProperty("Filename", "", FileProperty::Load, exts), + "The name of the Nexus file to load" ); + declareProperty(new PropertyWithValue<bool>("OverwriteLogs", true, Direction::Input)); + + + } + + /** Executes the algorithm. Reading in the file and creating and populating + * the output workspace + * + * @throw Exception::FileError If the Nexus file cannot be found/opened + * @throw std::invalid_argument If the optional properties are set to invalid values + */ + void LoadRunLogs::exec() + { + std::string filename = getPropertyValue("Filename"); + ::NeXus::File file(filename); + MatrixWorkspace_sptr workspace = getProperty("Workspace"); + + // Find the root entry + try + { + file.openGroup("entry", "NXentry"); // SNS style root + } + catch(::NeXus::Exception&) + { + try + { + file.openGroup("raw_data_1", "NXentry"); //ISIS style root + } + catch(::NeXus::Exception&) + { + throw std::invalid_argument("Unknown NeXus file format found in file '" + filename + "'"); + } + } + // print out the entry level fields + std::map<std::string,std::string> entries = file.getEntries(); + std::map<std::string,std::string>::const_iterator iend = entries.end(); + for(std::map<std::string,std::string>::const_iterator it = entries.begin(); + it != iend; it++) + { + std::string entry_name(it->first); + std::string entry_class(it->second); + if( entry_name == "DASlogs" || entry_class == "IXrunlog" || + entry_class == "IXselog" ) + { + loadLogs(file, entry_name, entry_class, workspace); + } + } + file.close(); + } + + /** + * Load log entries from the given group + * @param file :: A reference to the NeXus file handle opened such that the + * next call can be to open the named group + * @param entry_name :: The name of the log entry + * @param_entry_class :: The class type of the log entry + * @param workspace :: A pointer to the workspace to store the logs + */ + void LoadRunLogs::loadLogs(::NeXus::File & file, const std::string & entry_name, + const std::string & entry_class, + MatrixWorkspace_sptr workspace) const + { + file.openGroup(entry_name, entry_class); + std::map<std::string,std::string> entries = file.getEntries(); + std::map<std::string,std::string>::const_iterator iend = entries.end(); + for(std::map<std::string,std::string>::const_iterator itr = entries.begin(); + itr != iend; itr++) + { + std::string log_class = itr->second; + if( log_class == "NXlog" || entry_class == "NXpositioner" ) + { + loadNXLog(file, itr->first, workspace); + } + else if( log_class == "IXseblock" ) + { + loadSELog(file, itr->first, workspace); + } + } + + file.closeGroup(); + } + + /** + * Load an NX log entry + * @param file :: A reference to the NeXus file handle opened at the parent group + * @param entry_name :: The name of the log entry + * @param workspace :: A pointer to the workspace to store the logs + */ + void LoadRunLogs::loadNXLog(::NeXus::File & file, const std::string & entry_name, + MatrixWorkspace_sptr workspace) const + { + file.openGroup(entry_name, "NXlog"); + // Validate the NX log class. + std::map<std::string, std::string> entries = file.getEntries(); + if ((entries.find("value") == entries.end()) || + (entries.find("time") == entries.end()) ) + { + g_log.warning() << "Invalid NXlog entry " << entry_name + << " found. Did not contain 'value' and 'time'.\n"; + file.closeGroup(); + return; + } + // whether or not to overwrite logs on workspace + bool overwritelogs = this->getProperty("OverwriteLogs"); + try + { + Kernel::Property *logValue = createTimeSeries(file, entry_name); + workspace->mutableRun().addProperty(logValue, overwritelogs); + } + catch(::NeXus::Exception &e) + { + g_log.warning() << "NXlog entry " << entry_name + << " gave an error when loading:'" << e.what() << "'.\n"; + } + + file.closeGroup(); + } + + /** + * Load an SE log entry + * @param file :: A reference to the NeXus file handle opened at the parent group + * @param entry_name :: The name of the log entry + * @param workspace :: A pointer to the workspace to store the logs + */ + void LoadRunLogs::loadSELog(::NeXus::File & file, const std::string & entry_name, + MatrixWorkspace_sptr workspace) const + { + // Open the entry + file.openGroup(entry_name, "IXseblock"); + std::string propName = entry_name; + if (workspace->run().hasProperty(propName)) + { + propName = "selog_" + propName; + } + // There are two possible entries: + // value_log - A time series entry + // value - A single value float entry + Kernel::Property *logValue(NULL); + std::map<std::string, std::string> entries = file.getEntries(); + if( entries.find("value_log") != entries.end() ) + { + try + { + try + { + file.openGroup("value_log", "NXlog"); + } + catch(::NeXus::Exception&) + { + file.closeGroup(); + throw; + } + logValue = createTimeSeries(file, propName); + file.closeGroup(); + } + catch(::NeXus::Exception& e) + { + g_log.warning() << "IXseblock entry " << entry_name << " gave an error when loading " + << "a time series:'" << e.what() << "'.\n"; + file.closeGroup(); + return; + } + } + else if( entries.find("value") != entries.end() ) + { + float val_array[1]; + try + { + file.openData("value"); + file.getData(val_array); + file.closeData(); + } + catch(::NeXus::Exception& e) + { + g_log.warning() << "IXseblock entry " << entry_name << " gave an error when loading " + << "a single value:'" << e.what() << "'.\n"; + file.closeData(); + file.closeGroup(); + return; + } + logValue = new Kernel::PropertyWithValue<double>(propName, static_cast<double>(val_array[0]), true); + } + else + { + g_log.warning() << "IXseblock entry " << entry_name + << " does not contain a value or value_log field, skipping entry."; + file.closeGroup(); + return; + } + workspace->mutableRun().addProperty(logValue); + file.closeGroup(); + } + + /** + * Creates a time series property from the currently opened log entry. It is assumed to + * have been checked to have a time field and the value entry's name is given as an argument + * @param file :: A reference to the file handle + * @param prop_name :: The name of the property + * @returns A pointer to a new property containing the time series + */ + Kernel::Property * LoadRunLogs::createTimeSeries(::NeXus::File & file, + const std::string & prop_name) const + { + file.openData("time"); + //----- Start time is an ISO8601 string date and time. ------ + std::string start; + try + { + file.getAttr("start", start); + } + catch (::NeXus::Exception &) + { + //Some logs have "offset" instead of start + try + { + file.getAttr("offset", start); + } + catch (::NeXus::Exception &) + { + g_log.warning() << "Log entry has no start time indicated.\n"; + file.closeData(); + throw; + } + } + //Convert to date and time + Kernel::DateAndTime start_time = Kernel::DateAndTime(start); + std::string time_units; + file.getAttr("units", time_units); + if( time_units.find("second") != 0 && time_units != "minutes" ) + { + file.closeData(); + throw ::NeXus::Exception("Unsupported time unit '" + time_units + "'"); + + } + //--- Load the seconds into a double array --- + std::vector<double> time_double; + try + { + file.getDataCoerce(time_double); + } + catch (::NeXus::Exception &e) + { + g_log.warning() << "Log entry 's time field could not be loaded: '" << e.what() << "'.\n"; + file.closeData(); + throw; + } + file.closeData(); // Close time data + // Convert to seconds if needed + if( time_units == "minutes" ) + { + std::transform(time_double.begin(),time_double.end(), time_double.begin(), + std::bind2nd(std::multiplies<double>(),60.0)); + } + // Now the values: Could be a string, int or double + file.openData("value"); + // Get the units of the property + std::string value_units(""); + try + { + file.getAttr("units", value_units); + } + catch (::NeXus::Exception &) + { + //Ignore missing units field. + value_units = ""; + } + + // Now the actual data + ::NeXus::Info info = file.getInfo(); + if( file.isDataInt() ) // Int type + { + std::vector<int> values; + try + { + file.getDataCoerce(values); + file.closeData(); + } + catch(::NeXus::Exception&) + { + file.closeData(); + throw; + } + //Make an int TSP + TimeSeriesProperty<int> * tsp = new TimeSeriesProperty<int>(prop_name); + tsp->create(start_time, time_double, values); + tsp->setUnits(value_units); + return tsp; + } + else if( info.type == ::NeXus::CHAR ) + { + std::string values; + const int item_length = info.dims[1]; + try + { + const int nitems = info.dims[0]; + boost::scoped_array<char> val_array(new char[nitems*item_length]); + file.getData(val_array.get()); + file.closeData(); + values = std::string(val_array.get()); + } + catch(::NeXus::Exception&) + { + file.closeData(); + throw; + } + // The string may contain non-printable (i.e. control) characters, replace these + std::replace_if(values.begin(), values.end(), iscntrl, ' '); + TimeSeriesProperty<std::string> * tsp = new TimeSeriesProperty<std::string>(prop_name); + std::vector<DateAndTime> times; + DateAndTime::createVector(start_time, time_double, times); + const size_t ntimes = times.size(); + for(size_t i = 0; i < ntimes; ++i) + { + std::string value_i = std::string(values.data() + i*item_length, item_length); + tsp->addValue(times[i], value_i); + } + tsp->setUnits(value_units); + return tsp; + } + else if( info.type == ::NeXus::FLOAT32 || info.type == ::NeXus::FLOAT64 ) + { + std::vector<double> values; + try + { + file.getDataCoerce(values); + file.closeData(); + } + catch(::NeXus::Exception&) + { + file.closeData(); + throw; + } + TimeSeriesProperty<double> * tsp = new TimeSeriesProperty<double>(prop_name); + tsp->create(start_time, time_double, values); + tsp->setUnits(value_units); + return tsp; + } + else + { + throw ::NeXus::Exception("Invalid value type for time series. Only int, double or strings are " + "supported"); + } + } + + } // namespace DataHandling +} // namespace Mantid diff --git a/Code/Mantid/Framework/Nexus/test/LoadISISNexusTest.h b/Code/Mantid/Framework/Nexus/test/LoadISISNexusTest.h index 2118656d199..ccb2077e88f 100644 --- a/Code/Mantid/Framework/Nexus/test/LoadISISNexusTest.h +++ b/Code/Mantid/Framework/Nexus/test/LoadISISNexusTest.h @@ -56,18 +56,27 @@ public: TS_ASSERT(slog); TS_ASSERT_EQUALS(slog->size(),50); - TimeSeriesProperty<double>* dlog = dynamic_cast<TimeSeriesProperty<double>*>(ws->run().getLogData("total_counts")); - TS_ASSERT(dlog); - TS_ASSERT_EQUALS(dlog->size(),172); + TimeSeriesProperty<int>* ilog = dynamic_cast<TimeSeriesProperty<int>*>(ws->run().getLogData("total_counts")); + TS_ASSERT(ilog); + TS_ASSERT_EQUALS(ilog->size(),172); + + ilog = dynamic_cast<TimeSeriesProperty<int>*>(ws->run().getLogData("period")); + TS_ASSERT(ilog); + TS_ASSERT_EQUALS(ilog->size(),172); - dlog = dynamic_cast<TimeSeriesProperty<double>*>(ws->run().getLogData("period")); + TimeSeriesProperty<double> *dlog = dynamic_cast<TimeSeriesProperty<double>*>(ws->run().getLogData("proton_charge")); TS_ASSERT(dlog); TS_ASSERT_EQUALS(dlog->size(),172); + TimeSeriesProperty<bool>* blog = dynamic_cast<TimeSeriesProperty<bool>*>(ws->run().getLogData("period 1")); TS_ASSERT(blog); TS_ASSERT_EQUALS(blog->size(),1); + blog = dynamic_cast<TimeSeriesProperty<bool>*>(ws->run().getLogData("running")); + TS_ASSERT(blog); + TS_ASSERT_EQUALS(blog->size(),2); + TS_ASSERT_EQUALS(ws->sample().getName(),""); Property *l_property = ws->run().getLogData( "run_number" ); @@ -86,7 +95,7 @@ public: TS_ASSERT_THROWS_NOTHING(ld.execute()); TS_ASSERT(ld.isExecuted()); - MatrixWorkspace_sptr ws = boost::dynamic_pointer_cast<MatrixWorkspace>(AnalysisDataService::Instance().retrieve("outWS")); + MatrixWorkspace_sptr ws = boost::dynamic_pointer_cast<MatrixWorkspace>(AnalysisDataService::Instance().retrieve("outWS")); TS_ASSERT_EQUALS(ws->blocksize(),5); TS_ASSERT_EQUALS(ws->getNumberHistograms(),14); @@ -102,27 +111,27 @@ public: TS_ASSERT_EQUALS(ws->readY(10)[3],1.); TS_ASSERT_EQUALS(ws->readY(13)[4],1.); } - void testMultiPeriodEntryNumberZero() + void testMultiPeriodEntryNumberZero() { - Mantid::API::FrameworkManager::Instance(); + Mantid::API::FrameworkManager::Instance(); LoadISISNexus2 ld; ld.initialize(); ld.setPropertyValue("Filename","TEST00000008.nxs"); - ld.setPropertyValue("OutputWorkspace","outWS"); + ld.setPropertyValue("OutputWorkspace","outWS"); ld.setPropertyValue("SpectrumMin","10"); ld.setPropertyValue("SpectrumMax","19"); - ld.setPropertyValue("EntryNumber","0"); - //ld.setPropertyValue("SpectrumList","30,31"); + ld.setPropertyValue("EntryNumber","0"); + //ld.setPropertyValue("SpectrumList","30,31"); TS_ASSERT_THROWS_NOTHING(ld.execute()); TS_ASSERT(ld.isExecuted()); - - WorkspaceGroup_sptr grpout;//=WorkspaceGroup_sptr(new WorkspaceGroup); - TS_ASSERT_THROWS_NOTHING(grpout=boost::dynamic_pointer_cast<WorkspaceGroup>(AnalysisDataService::Instance().retrieve("outWS"))); + + WorkspaceGroup_sptr grpout;//=WorkspaceGroup_sptr(new WorkspaceGroup); + TS_ASSERT_THROWS_NOTHING(grpout=boost::dynamic_pointer_cast<WorkspaceGroup>(AnalysisDataService::Instance().retrieve("outWS"))); MatrixWorkspace_sptr ws = boost::dynamic_pointer_cast<MatrixWorkspace>(AnalysisDataService::Instance().retrieve("outWS_1")); TS_ASSERT_EQUALS(ws->blocksize(),995); TS_ASSERT_EQUALS(ws->getNumberHistograms(),10); - TS_ASSERT_DELTA(ws->run().getProtonCharge(), 0.069991, 1e-6); + TS_ASSERT_DELTA(ws->run().getProtonCharge(), 0.069991, 1e-6); TS_ASSERT_EQUALS(ws->readX(0)[0],5.); TS_ASSERT_EQUALS(ws->readX(0)[1],6.); @@ -136,7 +145,7 @@ public: TS_ASSERT_EQUALS(ws->readY(9)[3],0.); TS_ASSERT_EQUALS(ws->readY(9)[1],0.); } - void testMultiPeriodEntryNumberNonZero() + void testMultiPeriodEntryNumberNonZero() { Mantid::API::FrameworkManager::Instance(); LoadISISNexus2 ld; @@ -145,17 +154,17 @@ public: ld.setPropertyValue("OutputWorkspace","outWS"); ld.setPropertyValue("SpectrumMin","10"); ld.setPropertyValue("SpectrumMax","20"); - // ld.setPropertyValue("SpectrumList","29,30,31"); - ld.setPropertyValue("EntryNumber","5"); + // ld.setPropertyValue("SpectrumList","29,30,31"); + ld.setPropertyValue("EntryNumber","5"); TS_ASSERT_THROWS_NOTHING(ld.execute()); TS_ASSERT(ld.isExecuted()); - + MatrixWorkspace_sptr ws = boost::dynamic_pointer_cast<MatrixWorkspace>(AnalysisDataService::Instance().retrieve("outWS")); TS_ASSERT_EQUALS(ws->blocksize(),995); // TS_ASSERT_EQUALS(ws->getNumberHistograms(),14); TS_ASSERT_EQUALS(ws->getTitle(), "hello\\0"); - TS_ASSERT_DELTA(ws->run().getProtonCharge(), 0.069991, 1e-6); + TS_ASSERT_DELTA(ws->run().getProtonCharge(), 0.069991, 1e-6); TS_ASSERT_EQUALS(ws->readX(0)[0],5.); TS_ASSERT_EQUALS(ws->readX(0)[1],6.); TS_ASSERT_EQUALS(ws->readX(0)[2],7.); diff --git a/Code/Mantid/Framework/Nexus/test/LoadRunLogsTest.h b/Code/Mantid/Framework/Nexus/test/LoadRunLogsTest.h new file mode 100644 index 00000000000..2b6834bff89 --- /dev/null +++ b/Code/Mantid/Framework/Nexus/test/LoadRunLogsTest.h @@ -0,0 +1,76 @@ +#ifndef LOADLOGSFROMSNSNEXUSTEST_H_ +#define LOADLOGSFROMSNSNEXUSTEST_H_ + +#include "MantidNexus/LoadRunLogs.h" +#include "MantidAPI/AnalysisDataService.h" +#include "MantidAPI/MatrixWorkspace.h" +#include "MantidKernel/TimeSeriesProperty.h" +#include "MantidAPI/FrameworkManager.h" +#include "MantidAPI/Workspace.h" +#include "MantidDataObjects/Workspace2D.h" +#include "MantidKernel/PhysicalConstants.h" +using namespace Mantid; +using namespace Mantid::Geometry; +using namespace Mantid::API; +using namespace Mantid::Kernel; +using namespace Mantid::NeXus; + +#include <cxxtest/TestSuite.h> +#include "MantidAPI/WorkspaceGroup.h" +#include <iostream> + +class LoadRunLogsTest : public CxxTest::TestSuite +{ +public: + + void testExec() + { + Mantid::API::FrameworkManager::Instance(); + LoadRunLogs ld; + std::string outws_name = "CNCS_instrument"; + ld.initialize(); + ld.setPropertyValue("Filename","CNCS_7860.nxs"); + + //Create an empty workspace with some fake size, to start from. + DataObjects::Workspace2D_sptr ws = boost::dynamic_pointer_cast<DataObjects::Workspace2D> + (WorkspaceFactory::Instance().create("Workspace2D",1000,18+1,18)); + //Put it in the object. + ld.setProperty("Workspace", boost::dynamic_pointer_cast<MatrixWorkspace>(ws)); + + ld.execute(); + TS_ASSERT( ld.isExecuted() ); + + double val; + Run& run = ws->mutableRun(); + Property * prop; + TimeSeriesProperty<double> * dProp; + + prop = run.getLogData("Speed3"); + TS_ASSERT(prop); + //TS_ASSERT_EQUALS( prop->value(), "60"); + TS_ASSERT_EQUALS( prop->units(), "Hz"); + + prop = run.getLogData("PhaseRequest1"); + dProp = dynamic_cast< TimeSeriesProperty<double> * >(prop); + TS_ASSERT(dProp); + val = dProp->nthValue(0); + TS_ASSERT_DELTA( val, 8798.7236, 1e-2); + TS_ASSERT_EQUALS(prop->units(), "microsecond"); + + TimeSeriesProperty<double> * tsp; + + prop = run.getLogData("Phase1"); + tsp = dynamic_cast< TimeSeriesProperty<double> * >(prop); + TS_ASSERT(tsp); + TS_ASSERT_EQUALS(tsp->units(), "microsecond"); + TS_ASSERT_DELTA( tsp->nthValue(1), 8798.99, 2); + + //The time diff between the 0th and 1st entry is 2.328 seconds + TS_ASSERT_DELTA( Kernel::DateAndTime::seconds_from_duration(tsp->nthInterval(0).length()), 2.328, 0.01); + + //Now the stats + + } +}; + +#endif /*LOADRUNLOGS_H_*/ -- GitLab