From 063c7ff151a1a7d2122a6d5ccf3b955d76e97ec0 Mon Sep 17 00:00:00 2001 From: Samuel Jackson <samueljackson@outlook.com> Date: Mon, 8 Oct 2018 17:31:24 +0100 Subject: [PATCH] Refs #23666 Fix goniometer using mean instead of time average --- Framework/API/src/Run.cpp | 14 ++-- Framework/DataHandling/src/LoadNexusLogs.cpp | 72 ++++++++++++++++--- .../DataHandling/test/LoadISISNexusTest.h | 4 +- .../DataHandling/test/LoadNexusLogsTest.h | 25 ++++++- docs/source/release/v3.14.0/framework.rst | 1 + .../samplelogs/test/test_samplelogs_model.py | 2 +- 6 files changed, 97 insertions(+), 21 deletions(-) diff --git a/Framework/API/src/Run.cpp b/Framework/API/src/Run.cpp index 6486aec0feb..b91e5f6b86f 100644 --- a/Framework/API/src/Run.cpp +++ b/Framework/API/src/Run.cpp @@ -468,16 +468,18 @@ void Run::calculateGoniometerMatrix() { getLogAsSingleValue(axisName, Kernel::Math::Minimum); const double maxAngle = getLogAsSingleValue(axisName, Kernel::Math::Maximum); - const double angle = getLogAsSingleValue(axisName, Kernel::Math::Mean); + const double angle = + getLogAsSingleValue(axisName, Kernel::Math::TimeAveragedMean); + if (minAngle != maxAngle && !(std::isnan(minAngle) && std::isnan(maxAngle))) { const double lastAngle = getLogAsSingleValue(axisName, Kernel::Math::LastValue); - g_log.warning( - "Goniometer angle changed in " + axisName + " log from " + - boost::lexical_cast<std::string>(minAngle) + " to " + - boost::lexical_cast<std::string>(maxAngle) + - ". Used mean = " + boost::lexical_cast<std::string>(angle) + "."); + g_log.warning("Goniometer angle changed in " + axisName + " log from " + + boost::lexical_cast<std::string>(minAngle) + " to " + + boost::lexical_cast<std::string>(maxAngle) + + ". Used time averaged value = " + + boost::lexical_cast<std::string>(angle) + "."); if (axisName == "omega") { g_log.warning("To set to last angle, replace omega with " + boost::lexical_cast<std::string>(lastAngle) + diff --git a/Framework/DataHandling/src/LoadNexusLogs.cpp b/Framework/DataHandling/src/LoadNexusLogs.cpp index b41fe714454..879ca7e3a8a 100644 --- a/Framework/DataHandling/src/LoadNexusLogs.cpp +++ b/Framework/DataHandling/src/LoadNexusLogs.cpp @@ -20,6 +20,8 @@ #include "MantidDataHandling/LoadTOFRawNexus.h" #include <boost/scoped_array.hpp> +#include <algorithm> + namespace Mantid { namespace DataHandling { // Register the algorithm into the algorithm factory @@ -128,6 +130,58 @@ bool isControlValue(const char &c, const std::string &propName, return std::iscntrl(c, locale); } } + +/** + * Appends an additional entry to a TimeSeriesProperty which is at the end + * time of the run and contains the last value of the property recorded before + * the end time. + * + * This is a workaround to ensure that time series averaging of log values works + * correctly for instruments who do not record log values for the entire run. + * + * If the run does not have an end time or if the last time of the time series + * log is the same as the end time the property is left unmodified. + * + * @param prop :: a pointer to a TimeSeriesProperty to modify + * @param run :: handle to the run object containing the end time. + */ +void appendEndTimeLog(Kernel::Property *prop, const API::Run &run) { + try { + auto tsLog = dynamic_cast<TimeSeriesProperty<double> *>(prop); + const auto endTime = run.endTime(); + + // First check if it is valid to add a additional log entry + if (!tsLog || tsLog->size() == 0 || endTime <= tsLog->lastTime()) + return; + + tsLog->addValue(endTime, tsLog->lastValue()); + } catch (Exception::NotFoundError) { + // pass + } catch (std::runtime_error) { + // pass + } +} + +/** + * Read the start & end time of the run from the nexus file if they exist. + * + * @param file :: handle to the nexus file to read from. + * @param run :: handle to the run object to set the start & end time for. + */ +void readStartAndEndTime(::NeXus::File &file, API::Run &run) { + try { + // Read the start and end time strings + file.openData("start_time"); + Types::Core::DateAndTime start(file.getStrData()); + file.closeData(); + file.openData("end_time"); + Types::Core::DateAndTime end(file.getStrData()); + file.closeData(); + run.setStartAndEndTime(start, end); + } catch (::NeXus::Exception &) { + } +} + } // End of anonymous namespace /// Empty default constructor @@ -216,6 +270,9 @@ void LoadNexusLogs::exec() { } catch (::NeXus::Exception &) { // No time. This is not an SNS group } + + readStartAndEndTime(file, workspace->mutableRun()); + // 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(); @@ -304,17 +361,6 @@ void LoadNexusLogs::exec() { workspace->mutableRun().addProperty(pcharge, true); } } - try { - // Read the start and end time strings - file.openData("start_time"); - Types::Core::DateAndTime start(file.getStrData()); - file.closeData(); - file.openData("end_time"); - Types::Core::DateAndTime end(file.getStrData()); - file.closeData(); - workspace->mutableRun().setStartAndEndTime(start, end); - } catch (::NeXus::Exception &) { - } if (!workspace->run().hasProperty("gd_prtn_chrg")) { // Try pulling it from the main proton_charge entry first @@ -499,6 +545,7 @@ void LoadNexusLogs::loadNXLog( try { if (overwritelogs || !(workspace->run().hasProperty(entry_name))) { Kernel::Property *logValue = createTimeSeries(file, entry_name); + appendEndTimeLog(logValue, workspace->run()); workspace->mutableRun().addProperty(logValue, overwritelogs); } } catch (::NeXus::Exception &e) { @@ -539,7 +586,10 @@ void LoadNexusLogs::loadSELog( file.closeGroup(); throw; } + logValue = createTimeSeries(file, propName); + appendEndTimeLog(logValue, workspace->run()); + file.closeGroup(); } catch (std::exception &e) { g_log.warning() << "IXseblock entry '" << entry_name diff --git a/Framework/DataHandling/test/LoadISISNexusTest.h b/Framework/DataHandling/test/LoadISISNexusTest.h index 1306b867c25..53d8000081e 100644 --- a/Framework/DataHandling/test/LoadISISNexusTest.h +++ b/Framework/DataHandling/test/LoadISISNexusTest.h @@ -244,7 +244,7 @@ public: dynamic_cast<TimeSeriesProperty<double> *>( ws->run().getLogData("proton_charge")); TS_ASSERT(dlog); - TS_ASSERT_EQUALS(dlog->size(), 172); + TS_ASSERT_EQUALS(dlog->size(), 173); TimeSeriesProperty<bool> *blog = dynamic_cast<TimeSeriesProperty<bool> *>( ws->run().getLogData("period 1")); @@ -648,7 +648,7 @@ public: dynamic_cast<TimeSeriesProperty<double> *>( ws->run().getLogData("proton_charge")); TS_ASSERT(dlog); - TS_ASSERT_EQUALS(dlog->size(), 172); + TS_ASSERT_EQUALS(dlog->size(), 173); TimeSeriesProperty<bool> *blog = dynamic_cast<TimeSeriesProperty<bool> *>( ws->run().getLogData("period 1")); diff --git a/Framework/DataHandling/test/LoadNexusLogsTest.h b/Framework/DataHandling/test/LoadNexusLogsTest.h index 0fe697a9e61..36f104abe7e 100644 --- a/Framework/DataHandling/test/LoadNexusLogsTest.h +++ b/Framework/DataHandling/test/LoadNexusLogsTest.h @@ -120,7 +120,7 @@ public: dynamic_cast<TimeSeriesProperty<double> *>( run.getLogData("proton_charge")); TS_ASSERT(dlog); - TS_ASSERT_EQUALS(dlog->size(), 172); + TS_ASSERT_EQUALS(dlog->size(), 173); } void test_File_With_Bad_Property() { @@ -271,6 +271,29 @@ public: TS_ASSERT_THROWS_NOTHING(loader.execute()) } + void test_last_time_series_log_entry_equals_end_time() { + LoadNexusLogs ld; + std::string outws_name = "REF_L_instrument"; + ld.initialize(); + ld.setPropertyValue("Filename", "REF_L_32035.nxs"); + MatrixWorkspace_sptr ws = createTestWorkspace(); + // Put it in the object. + ld.setProperty("Workspace", ws); + ld.execute(); + TS_ASSERT(ld.isExecuted()); + + auto run = ws->run(); + auto pclog = dynamic_cast<TimeSeriesProperty<double> *>( + run.getLogData("PhaseRequest1")); + + TS_ASSERT(pclog); + + const auto lastTime = pclog->lastTime(); + const auto endTime = run.endTime(); + + TS_ASSERT_EQUALS(endTime.totalNanoseconds(), lastTime.totalNanoseconds()); + } + private: API::MatrixWorkspace_sptr createTestWorkspace() { return WorkspaceFactory::Instance().create("Workspace2D", 1, 1, 1); diff --git a/docs/source/release/v3.14.0/framework.rst b/docs/source/release/v3.14.0/framework.rst index 13843146508..ecbfc20d335 100644 --- a/docs/source/release/v3.14.0/framework.rst +++ b/docs/source/release/v3.14.0/framework.rst @@ -79,6 +79,7 @@ Bugfixes - Fixed a rare bug in :ref:`MaskDetectors <algm-MaskDetectors>` where a workspace could become invalidaded in Python if it was a ``MaskWorkspace``. - Fixed a crash in :ref:`MaskDetectors <algm-MaskDetectors>` when a non-existent component was given in ``ComponentList``. - History for algorithms that took groups sometimes would get incorrect history causing history to be incomplete, so now full group history is saved for all items belonging to the group. +- Fixed a bug in `SetGoniometer <algm-SetGoniometer>` where it would use the mean log value rather than the time series average value for goniometer angles. Python diff --git a/qt/python/mantidqt/widgets/samplelogs/test/test_samplelogs_model.py b/qt/python/mantidqt/widgets/samplelogs/test/test_samplelogs_model.py index 52a8e707d77..1b78f09131c 100644 --- a/qt/python/mantidqt/widgets/samplelogs/test/test_samplelogs_model.py +++ b/qt/python/mantidqt/widgets/samplelogs/test/test_samplelogs_model.py @@ -55,7 +55,7 @@ class SampleLogsModelTest(unittest.TestCase): self.assertEqual(itemModel.rowCount(), 48) self.assertEqual(itemModel.item(0,0).text(), "ChopperStatus1") self.assertEqual(itemModel.item(0,1).text(), "float series") - self.assertEqual(itemModel.item(0,2).text(), "4.0") + self.assertEqual(itemModel.item(0,2).text(), "(2 entries)") self.assertEqual(itemModel.item(0,3).text(), "") def test_model_MD(self): -- GitLab