From 5ef2babdc32df7c895dcbd2040aa5de9d2edb0b3 Mon Sep 17 00:00:00 2001 From: Russell Taylor Date: Tue, 4 Dec 2012 14:01:42 -0500 Subject: [PATCH] Re #6267. Extend FilterByLogValue algorithm to handle integer logs. Made use of the ITimeSeriesProperty method and added methods to that to facilitate this. The algorithm will now handle numeric (double or integer) TimeSeriesProperty logs, though not string ones. --- .../Algorithms/src/FilterByLogValue.cpp | 34 +++------- .../inc/MantidKernel/ITimeSeriesProperty.h | 10 +++ .../inc/MantidKernel/TimeSeriesProperty.h | 2 + .../Kernel/src/TimeSeriesProperty.cpp | 58 ++++++++++++++++ .../Kernel/test/TimeSeriesPropertyTest.h | 68 +++++++++++++++++++ 5 files changed, 146 insertions(+), 26 deletions(-) diff --git a/Code/Mantid/Framework/Algorithms/src/FilterByLogValue.cpp b/Code/Mantid/Framework/Algorithms/src/FilterByLogValue.cpp index 224ed6b27c84..e57921047a21 100644 --- a/Code/Mantid/Framework/Algorithms/src/FilterByLogValue.cpp +++ b/Code/Mantid/Framework/Algorithms/src/FilterByLogValue.cpp @@ -74,6 +74,8 @@ using DataObjects::EventWorkspace; using DataObjects::EventWorkspace_sptr; using DataObjects::EventWorkspace_const_sptr; +std::string CENTRE("Centre"); +std::string LEFT("Left"); //======================================================================== //======================================================================== @@ -114,8 +116,8 @@ void FilterByLogValue::init() "If there are several consecutive log values matching the filter, events between T1-Tolerance and T2+Tolerance are kept."); std::vector types(2); - types[0] = "Centre"; - types[1] = "Left"; + types[0] = CENTRE; + types[1] = LEFT; declareProperty("LogBoundary", types[0], boost::make_shared(types), "How to treat log values as being measured in the centre of the time, or beginning (left) boundary"); @@ -161,7 +163,7 @@ void FilterByLogValue::exec() // Now make the splitter vector TimeSplitterType splitter; //This'll throw an exception if the log doesn't exist. That is good. - Kernel::TimeSeriesProperty * log = dynamic_cast *>( inputWS->run().getLogData(logname) ); + Kernel::ITimeSeriesProperty * log = dynamic_cast( inputWS->run().getLogData(logname) ); if (log) { if (PulseFilter) @@ -188,32 +190,12 @@ void FilterByLogValue::exec() throw std::invalid_argument("MaximumValue should be > MinimumValue. Aborting."); //This function creates the splitter vector we will use to filter out stuff. - std::string logBoundary(this->getPropertyValue("LogBoundary")); - std::transform(logBoundary.begin(), logBoundary.end(), logBoundary.begin(), tolower); - log->makeFilterByValue(splitter, min, max, tolerance, (logBoundary.compare("centre") == 0)); + const std::string logBoundary(this->getPropertyValue("LogBoundary")); + log->makeFilterByValue(splitter, min, max, tolerance, (logBoundary == CENTRE)); if (log->realSize() >= 1 && handle_edge_values) { - double val; - // Assume everything before the 1st value is constant - val = log->firstValue(); - if ((val >= min) && (val <= max)) - { - TimeSplitterType extraFilter; - extraFilter.push_back( SplittingInterval(run_start, log->firstTime(), 0)); - // Include everything from the start of the run to the first time measured (which may be a null time interval; this'll be ignored) - splitter = splitter | extraFilter; - } - - // Assume everything after the LAST value is constant - val = log->lastValue(); - if ((val >= min) && (val <= max)) - { - TimeSplitterType extraFilter; - extraFilter.push_back( SplittingInterval(log->lastTime(), run_stop, 0) ); - // Include everything from the start of the run to the first time measured (which may be a null time interval; this'll be ignored) - splitter = splitter | extraFilter; - } + log->expandFilterToRange(splitter, min, max, TimeInterval(run_start,run_stop)); } } // (filter by value) diff --git a/Code/Mantid/Framework/Kernel/inc/MantidKernel/ITimeSeriesProperty.h b/Code/Mantid/Framework/Kernel/inc/MantidKernel/ITimeSeriesProperty.h index e8eb7b7de84c..83e5e3e27f52 100644 --- a/Code/Mantid/Framework/Kernel/inc/MantidKernel/ITimeSeriesProperty.h +++ b/Code/Mantid/Framework/Kernel/inc/MantidKernel/ITimeSeriesProperty.h @@ -4,6 +4,7 @@ //---------------------------------------------------------------------- // Includes //---------------------------------------------------------------------- +#include "MantidKernel/TimeSplitter.h" namespace Mantid { @@ -31,8 +32,17 @@ namespace Kernel class ITimeSeriesProperty { public: + /// Fill a TimeSplitterType that will filter the events by matching + virtual void makeFilterByValue(TimeSplitterType& split, double min, double max, double TimeTolerance, bool centre=true) const = 0; + /// Make sure an existing filter covers the full time range given + virtual void expandFilterToRange(TimeSplitterType& split, double min, double max, const TimeInterval & range) const = 0; + /// Return the time series's times as a vector + virtual std::vector timesAsVector() const = 0; + /// Returns the real size of the time series property map: + virtual int realSize() const = 0; /// Deletes the series of values in the property virtual void clear() = 0; + /// Virtual destructor virtual ~ITimeSeriesProperty() {} }; diff --git a/Code/Mantid/Framework/Kernel/inc/MantidKernel/TimeSeriesProperty.h b/Code/Mantid/Framework/Kernel/inc/MantidKernel/TimeSeriesProperty.h index 2be89b48050a..ea69758a96dc 100644 --- a/Code/Mantid/Framework/Kernel/inc/MantidKernel/TimeSeriesProperty.h +++ b/Code/Mantid/Framework/Kernel/inc/MantidKernel/TimeSeriesProperty.h @@ -167,6 +167,8 @@ namespace Mantid void splitByTime(TimeSplitterType& splitter, std::vector< Property * > outputs) const; /// Fill a TimeSplitterType that will filter the events by matching void makeFilterByValue(TimeSplitterType& split, double min, double max, double TimeTolerance, bool centre) const; + /// Make sure an existing filter covers the full time range given + void expandFilterToRange(TimeSplitterType& split, double min, double max, const TimeInterval & range) const; /// Return the time series as a correct C++ map. All values std::map valueAsCorrectMap() const; diff --git a/Code/Mantid/Framework/Kernel/src/TimeSeriesProperty.cpp b/Code/Mantid/Framework/Kernel/src/TimeSeriesProperty.cpp index 3057b9b1e171..7c27effd7cee 100644 --- a/Code/Mantid/Framework/Kernel/src/TimeSeriesProperty.cpp +++ b/Code/Mantid/Framework/Kernel/src/TimeSeriesProperty.cpp @@ -450,6 +450,14 @@ namespace Mantid template void TimeSeriesProperty::makeFilterByValue(TimeSplitterType& split, double min, double max, double TimeTolerance, bool centre) const { + if ( max < min ) + { + std::stringstream ss; + ss << "TimeSeriesProperty::makeFilterByValue: 'max' argument must be greater than 'min'" + << "(got min=" << min << " max=" << max; + throw std::invalid_argument(ss.str()); + } + // Make sure the splitter starts out empty split.clear(); @@ -519,6 +527,56 @@ namespace Mantid throw Exception::NotImplementedError("TimeSeriesProperty::makeFilterByValue is not implemented for string properties"); } + /** If the first and/or last values in a log are between min & max, expand and existing TimeSplitter + * (created by makeFilterByValue) if necessary to cover the full TimeInterval given. + * @param split The splitter to modify if necessary + * @param min The minimum 'good' value + * @param max The maximum 'good' value + * @param TimeInterval The full time range that we want this splitter to cover + */ + template + void TimeSeriesProperty::expandFilterToRange(TimeSplitterType& split, double min, double max, const TimeInterval & range) const + { + if ( max < min ) + { + std::stringstream ss; + ss << "TimeSeriesProperty::expandFilterToRange: 'max' argument must be greater than 'min'" + << "(got min=" << min << " max=" << max; + throw std::invalid_argument(ss.str()); + } + + // Assume everything before the 1st value is constant + double val = firstValue(); + if ((val >= min) && (val <= max)) + { + TimeSplitterType extraFilter; + extraFilter.push_back( SplittingInterval(range.begin(), firstTime(), 0)); + // Include everything from the start of the run to the first time measured (which may be a null time interval; this'll be ignored) + split = split | extraFilter; + } + + // Assume everything after the LAST value is constant + val = lastValue(); + if ((val >= min) && (val <= max)) + { + TimeSplitterType extraFilter; + extraFilter.push_back( SplittingInterval(lastTime(), range.end(), 0) ); + // Include everything from the start of the run to the first time measured (which may be a null time interval; this'll be ignored) + split = split | extraFilter; + } + + return; + } + + /** Function specialization for TimeSeriesProperty + * @throws Kernel::Exception::NotImplementedError always + */ + template<> + void TimeSeriesProperty::expandFilterToRange(TimeSplitterType&, double, double, const TimeInterval&) const + { + throw Exception::NotImplementedError("TimeSeriesProperty::makeFilterByValue is not implemented for string properties"); + } + /** * Return the time series as a correct C++ map. All values * are included. diff --git a/Code/Mantid/Framework/Kernel/test/TimeSeriesPropertyTest.h b/Code/Mantid/Framework/Kernel/test/TimeSeriesPropertyTest.h index fd53a4f70139..2e231b5a8b86 100644 --- a/Code/Mantid/Framework/Kernel/test/TimeSeriesPropertyTest.h +++ b/Code/Mantid/Framework/Kernel/test/TimeSeriesPropertyTest.h @@ -382,6 +382,9 @@ class TimeSeriesPropertyTest : public CxxTest::TestSuite t = DateAndTime("2007-11-30T16:17:50"); TS_ASSERT_DELTA( s.stop(), t, 1e-3); + // Check throws if min > max + TS_ASSERT_THROWS( log->makeFilterByValue(splitter, 2.0, 1.0, 0.0, true), std::invalid_argument); + delete log; } @@ -392,6 +395,71 @@ class TimeSeriesPropertyTest : public CxxTest::TestSuite TS_ASSERT_THROWS(log.makeFilterByValue(splitter,0.0,0.0,0.0,true), Exception::NotImplementedError); } + void test_expandFilterToRange() + { + TimeSeriesProperty log("MyIntLog"); + TS_ASSERT_THROWS_NOTHING( log.addValue("2007-11-30T16:17:00",1) ); + TS_ASSERT_THROWS_NOTHING( log.addValue("2007-11-30T16:17:10",2) ); + TS_ASSERT_THROWS_NOTHING( log.addValue("2007-11-30T16:17:20",3) ); + TS_ASSERT_THROWS_NOTHING( log.addValue("2007-11-30T16:17:30",4) ); + TS_ASSERT_THROWS_NOTHING( log.addValue("2007-11-30T16:17:40",6) ); + TS_ASSERT_THROWS_NOTHING( log.addValue("2007-11-30T16:17:50",2) ); + + // Create a TimeInterval that's wider than this log + TimeInterval interval(DateAndTime("2007-11-30T16:16:00"),DateAndTime("2007-11-30T16:18:50")); + + TimeSplitterType splitter; + // Test good at both ends + log.makeFilterByValue(splitter, 1.0, 2.2, 1.0, false); + log.expandFilterToRange(splitter, 1.0, 2.2, interval); + TS_ASSERT_EQUALS( splitter.size(), 2 ); + TS_ASSERT_DELTA( splitter[0].start(), DateAndTime("2007-11-30T16:16:00"), 1e-3); + TS_ASSERT_DELTA( splitter[0].stop(), DateAndTime("2007-11-30T16:17:20"), 1e-3); + TS_ASSERT_DELTA( splitter[1].start(), DateAndTime("2007-11-30T16:17:50"), 1e-3); + TS_ASSERT_DELTA( splitter[1].stop(), DateAndTime("2007-11-30T16:18:50"), 1e-3); + + // Test bad at both ends + log.makeFilterByValue(splitter, 2.5, 10.0, 0.0, false); + log.expandFilterToRange(splitter, 2.5, 10.0, interval); + TS_ASSERT_EQUALS( splitter.size(), 1 ); + TS_ASSERT_DELTA( splitter[0].start(), DateAndTime("2007-11-30T16:17:20"), 1e-3); + TS_ASSERT_DELTA( splitter[0].stop(), DateAndTime("2007-11-30T16:17:50"), 1e-3); + + // Test good at start, bad at end + log.makeFilterByValue(splitter, -1.0, 1.5, 0.0, false); + log.expandFilterToRange(splitter, -1.0, 1.5, interval); + TS_ASSERT_EQUALS( splitter.size(), 1 ); + TS_ASSERT_DELTA( splitter[0].start(), DateAndTime("2007-11-30T16:16:00"), 1e-3); + TS_ASSERT_DELTA( splitter[0].stop(), DateAndTime("2007-11-30T16:17:10"), 1e-3); + + // Test good at end, bad at start + log.makeFilterByValue(splitter, 1.99, 2.5, 1.0, false); + log.expandFilterToRange(splitter, 1.99, 2.5, interval); + TS_ASSERT_EQUALS( splitter.size(), 2 ); + TS_ASSERT_DELTA( splitter[0].start(), DateAndTime("2007-11-30T16:17:10"), 1e-3); + TS_ASSERT_DELTA( splitter[0].stop(), DateAndTime("2007-11-30T16:17:20"), 1e-3); + TS_ASSERT_DELTA( splitter[1].start(), DateAndTime("2007-11-30T16:17:50"), 1e-3); + TS_ASSERT_DELTA( splitter[1].stop(), DateAndTime("2007-11-30T16:18:50"), 1e-3); + + // Check throws if min > max + TS_ASSERT_THROWS( log.expandFilterToRange(splitter, 2.0, 1.0, interval), std::invalid_argument); + + // Test good at both ends, but interval narrower than log range + TimeInterval narrowinterval(DateAndTime("2007-11-30T16:17:15"),DateAndTime("2007-11-30T16:17:41")); + log.makeFilterByValue(splitter, 0.0, 10.0, 0.0, false); + log.expandFilterToRange(splitter, 0.0, 10.0, narrowinterval); + TS_ASSERT_EQUALS( splitter.size(), 1 ); + TS_ASSERT_DELTA( splitter[0].start(), DateAndTime("2007-11-30T16:17:00"), 1e-3); + TS_ASSERT_DELTA( splitter[0].stop(), DateAndTime("2007-11-30T16:17:50"), 1e-3); + } + + void test_expandFilterToRange_throws_for_string_property() + { + TimeSeriesProperty log("StringTSP"); + TimeSplitterType splitter; + TS_ASSERT_THROWS(log.expandFilterToRange(splitter,0.0,0.0,TimeInterval()), Exception::NotImplementedError); + } + //---------------------------------------------------------------------------- void test_splitByTime_and_getTotalValue() {