Skip to content

Commit

Permalink
Re #6267. Extend FilterByLogValue algorithm to handle integer logs.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
RussellTaylor committed Dec 4, 2012
1 parent 1f191ed commit 5ef2bab
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 26 deletions.
34 changes: 8 additions & 26 deletions Code/Mantid/Framework/Algorithms/src/FilterByLogValue.cpp
Expand Up @@ -74,6 +74,8 @@ using DataObjects::EventWorkspace;
using DataObjects::EventWorkspace_sptr;
using DataObjects::EventWorkspace_const_sptr;

std::string CENTRE("Centre");
std::string LEFT("Left");

//========================================================================
//========================================================================
Expand Down Expand Up @@ -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<std::string> types(2);
types[0] = "Centre";
types[1] = "Left";
types[0] = CENTRE;
types[1] = LEFT;
declareProperty("LogBoundary", types[0], boost::make_shared<StringListValidator>(types),
"How to treat log values as being measured in the centre of the time, or beginning (left) boundary");

Expand Down Expand Up @@ -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<double> * log = dynamic_cast<Kernel::TimeSeriesProperty<double> *>( inputWS->run().getLogData(logname) );
Kernel::ITimeSeriesProperty * log = dynamic_cast<Kernel::ITimeSeriesProperty*>( inputWS->run().getLogData(logname) );
if (log)
{
if (PulseFilter)
Expand All @@ -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)

Expand Down
Expand Up @@ -4,6 +4,7 @@
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
#include "MantidKernel/TimeSplitter.h"

namespace Mantid
{
Expand Down Expand Up @@ -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<DateAndTime>
virtual std::vector<DateAndTime> 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() {}
};
Expand Down
Expand Up @@ -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<DateAndTime, TYPE>. All values
std::map<DateAndTime, TYPE> valueAsCorrectMap() const;
Expand Down
58 changes: 58 additions & 0 deletions Code/Mantid/Framework/Kernel/src/TimeSeriesProperty.cpp
Expand Up @@ -450,6 +450,14 @@ namespace Mantid
template<typename TYPE>
void TimeSeriesProperty<TYPE>::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();

Expand Down Expand Up @@ -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<typename TYPE>
void TimeSeriesProperty<TYPE>::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<std::string>
* @throws Kernel::Exception::NotImplementedError always
*/
template<>
void TimeSeriesProperty<std::string>::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<DateAndTime, TYPE>. All values
* are included.
Expand Down
68 changes: 68 additions & 0 deletions Code/Mantid/Framework/Kernel/test/TimeSeriesPropertyTest.h
Expand Up @@ -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;
}

Expand All @@ -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<int> 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<std::string> log("StringTSP");
TimeSplitterType splitter;
TS_ASSERT_THROWS(log.expandFilterToRange(splitter,0.0,0.0,TimeInterval()), Exception::NotImplementedError);
}

//----------------------------------------------------------------------------
void test_splitByTime_and_getTotalValue()
{
Expand Down

0 comments on commit 5ef2bab

Please sign in to comment.