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() {