diff --git a/Code/Mantid/Framework/Algorithms/inc/MantidAlgorithms/GenerateEventsFilter.h b/Code/Mantid/Framework/Algorithms/inc/MantidAlgorithms/GenerateEventsFilter.h index 24cae04d1656..bddbe433bf23 100644 --- a/Code/Mantid/Framework/Algorithms/inc/MantidAlgorithms/GenerateEventsFilter.h +++ b/Code/Mantid/Framework/Algorithms/inc/MantidAlgorithms/GenerateEventsFilter.h @@ -147,9 +147,12 @@ namespace Algorithms const double &minvalue, const double &maxvalue, const Kernel::DateAndTime &startT, const Kernel::DateAndTime &stopT, const bool &filterIncrease, const bool &filterDecrease); - /// ?? + /// Determine the chaning direction of log value int determineChangingDirection(int startindex); + /// Find the end of the run + Kernel::DateAndTime findRunEnd(); + DataObjects::EventWorkspace_const_sptr m_dataWS; /// SplitterWorkspace @@ -162,6 +165,9 @@ namespace Algorithms Kernel::DateAndTime m_startTime; Kernel::DateAndTime m_stopTime; + /// Run end time + Kernel::DateAndTime m_runEndTime; + double m_timeUnitConvertFactorToNS; Kernel::TimeSeriesProperty* m_dblLog; diff --git a/Code/Mantid/Framework/Algorithms/src/GenerateEventsFilter.cpp b/Code/Mantid/Framework/Algorithms/src/GenerateEventsFilter.cpp index 68c05a55e76e..ba88af5df5c3 100644 --- a/Code/Mantid/Framework/Algorithms/src/GenerateEventsFilter.cpp +++ b/Code/Mantid/Framework/Algorithms/src/GenerateEventsFilter.cpp @@ -277,10 +277,8 @@ namespace Algorithms // Obtain run time range DateAndTime runstarttime = m_dataWS->run().startTime(); - /// FIXME Use this simple method may miss the events in the last pulse - Kernel::TimeSeriesProperty* protonchargelog = - dynamic_cast *>(m_dataWS->run().getProperty("proton_charge")); - Kernel::DateAndTime runendtime = protonchargelog->lastTime(); + + m_runEndTime = findRunEnd(); // Obtain time unit converter std::string timeunit = this->getProperty("UnitOfTime"); @@ -298,7 +296,7 @@ namespace Algorithms else if (timeunit.compare("Percent") == 0) { // (percent of total run time) - int64_t runtime_ns = runendtime.totalNanoseconds()-runstarttime.totalNanoseconds(); + int64_t runtime_ns = m_runEndTime.totalNanoseconds()-runstarttime.totalNanoseconds(); double runtimed_ns = static_cast(runtime_ns); m_timeUnitConvertFactorToNS = 0.01*runtimed_ns; } @@ -339,7 +337,7 @@ namespace Algorithms if (defaultstop) { // Default - m_stopTime = runendtime; + m_stopTime = m_runEndTime; } else if (instringformat) { @@ -365,7 +363,7 @@ namespace Algorithms g_log.information() << "Filter: StartTime = " << m_startTime << ", StopTime = " << m_stopTime << "; Run start = " << runstarttime.toISO8601String() - << ", Run stop = " << runendtime.toISO8601String() << "\n"; + << ", Run stop = " << m_runEndTime.toISO8601String() << "\n"; return; } @@ -383,6 +381,9 @@ namespace Algorithms // Progress int64_t totaltime = m_stopTime.totalNanoseconds()-m_startTime.totalNanoseconds(); + g_log.warning() << "Filter by time: start @ " << m_startTime.totalNanoseconds() << "; " + << "stop @ " << m_stopTime.totalNanoseconds() << "\n"; + if (singleslot) { int wsindex = 0; @@ -452,15 +453,17 @@ namespace Algorithms throw runtime_error(errmsg.str()); } - // Clear duplicate value + // Clear duplicate value and extend to run end if (m_dblLog) { g_log.debug("Attempting to remove duplicates in double series log."); + m_dblLog->addValue(m_runEndTime, 0.); m_dblLog->eliminateDuplicates(); } else { g_log.debug("Attempting to remove duplicates in integer series log."); + m_intLog->addValue(m_runEndTime, 0); m_intLog->eliminateDuplicates(); } @@ -1866,5 +1869,82 @@ namespace Algorithms return; } + //---------------------------------------------------------------------------------------------- + /** Find run end time. Here is how the run end time is defined ranked from highest priority + * 1. Run.endTime() + * 2. Last proton charge log time + * 3. Last event time + * In order to consider the events in the last pulse, + * 1. if proton charge does exist, then run end time will be extended by 1 pulse time + * 2. otherwise, extended by 0.1 second (10 Hz) + * Exception: None of the 3 conditions is found to determine run end time + */ + DateAndTime GenerateEventsFilter::findRunEnd() + { + // Try to get the run end from Run object + DateAndTime runendtime(0); + bool norunendset = false; + try + { + runendtime = m_dataWS->run().endTime(); + } + catch (std::runtime_error err) + { + norunendset = true; + } + + g_log.debug() << "Check point 1 " << "Run end time = " << runendtime << ", no run end set = " + << norunendset << "\n"; + + int64_t extended_ns = static_cast(1.0E8); + if (m_dataWS->run().hasProperty("proton_charge")) + { + // Get last proton charge time and compare with run end time + Kernel::TimeSeriesProperty* protonchargelog = + dynamic_cast *>(m_dataWS->run().getProperty("proton_charge")); + + if (protonchargelog->size() > 1) + { + Kernel::DateAndTime tmpendtime = protonchargelog->lastTime(); + extended_ns = protonchargelog->nthTime(1).totalNanoseconds() - protonchargelog->nthTime(0).totalNanoseconds(); + if (tmpendtime > runendtime) + { + // Use the last proton charge time + runendtime = tmpendtime; + } + norunendset = false; + } + + g_log.debug() << "Check point 2A " << " run end time = " << runendtime << "\n"; + } + else if (norunendset && m_dataWS->getNumberEvents() > 0) + { + // No proton_charge or run_end: sort events and find the last event + norunendset = false; + + for (size_t i = 0; i < m_dataWS->getNumberHistograms(); ++i) + { + const DataObjects::EventList& evlist = m_dataWS->getEventList(i); + if (evlist.getNumberEvents() > 0) + { + // If event list is empty, the returned value may not make any sense + DateAndTime lastpulse = evlist.getPulseTimeMax(); + if (lastpulse > runendtime) + runendtime = lastpulse; + } + } + g_log.warning() << "Check point 2B " << " run end time = " << runendtime << "\n"; + } + + // Check whether run end time is set + if (norunendset) + throw runtime_error("Run end time cannot be determined. "); + + // Add 1 second to make sure that no event left behind either last pulse time or last event time + runendtime = DateAndTime(runendtime.totalNanoseconds() + extended_ns); + + return runendtime; + } + } // namespace Mantid } // namespace Algorithms diff --git a/Code/Mantid/Framework/Algorithms/test/GenerateEventsFilterTest.h b/Code/Mantid/Framework/Algorithms/test/GenerateEventsFilterTest.h index 7fd57ca80ac5..4c74cb468f78 100644 --- a/Code/Mantid/Framework/Algorithms/test/GenerateEventsFilterTest.h +++ b/Code/Mantid/Framework/Algorithms/test/GenerateEventsFilterTest.h @@ -109,8 +109,12 @@ class GenerateEventsFilterTest : public CxxTest::TestSuite */ void test_genTimeMultipleInterval() { - // 1. Create input Workspace + // Create input Workspace DataObjects::EventWorkspace_sptr eventWS = createEventWorkspace(); + for (size_t i = 0; i < eventWS->getNumberHistograms(); ++i) + std::cout << "Spectrum " << i << ": max pulse time = " << eventWS->getEventList(i).getPulseTimeMax() + << " = " << eventWS->getEventList(i).getPulseTimeMin().totalNanoseconds() << "\n"; + int64_t timeinterval_ns = 15000; // 2. Init and set property @@ -133,7 +137,7 @@ class GenerateEventsFilterTest : public CxxTest::TestSuite TS_ASSERT(splittersws); - size_t numintervals = 67; + size_t numintervals = 74; TS_ASSERT_EQUALS(splittersws->getNumberSplitters(), numintervals); std::string runstarttimestr = eventWS->run().getProperty("run_start")->value(); @@ -142,7 +146,7 @@ class GenerateEventsFilterTest : public CxxTest::TestSuite Kernel::TimeSeriesProperty *protonchargelog = dynamic_cast* >(eventWS->run().getProperty("proton_charge")); - Kernel::DateAndTime runstoptime = protonchargelog->lastTime(); + Kernel::DateAndTime runstoptime = Kernel::DateAndTime(protonchargelog->lastTime().totalNanoseconds() + 100000); // b) First interval Kernel::SplittingInterval splitter0 = splittersws->getSplitter(0); @@ -411,12 +415,11 @@ class GenerateEventsFilterTest : public CxxTest::TestSuite using namespace WorkspaceCreationHelper; double PI = 3.14159265; - // 1. Empty workspace + // Empty workspace DataObjects::EventWorkspace_sptr eventws = WorkspaceCreationHelper::createEventWorkspaceWithFullInstrument(2, 2, true); - //eventws->setName("TestWorkspace"); - // 2. Run star time + // Run star time int64_t runstarttime_ns = 3000000000; int64_t runstoptime_ns = 3001000000; int64_t pulsetime_ns = 100000; @@ -424,7 +427,7 @@ class GenerateEventsFilterTest : public CxxTest::TestSuite Kernel::DateAndTime runstarttime(runstarttime_ns); eventws->mutableRun().addProperty("run_start", runstarttime.toISO8601String()); - // 3. Proton charge log + // Proton charge log Kernel::TimeSeriesProperty *protonchargelog = new Kernel::TimeSeriesProperty("proton_charge"); int64_t curtime_ns = runstarttime_ns; @@ -435,6 +438,7 @@ class GenerateEventsFilterTest : public CxxTest::TestSuite curtime_ns += pulsetime_ns; } eventws->mutableRun().addProperty(protonchargelog, true); + std::cout << "Proton charge log from " << runstarttime_ns << " to " << runstoptime_ns << "\n"; // 4. Sine value log (value record 1/4 of pulse time. it is FAST) Kernel::TimeSeriesProperty *sinlog = new Kernel::TimeSeriesProperty("FastSineLog"); @@ -462,6 +466,8 @@ class GenerateEventsFilterTest : public CxxTest::TestSuite } eventws->mutableRun().addProperty(coslog, true); + std::cout << "<----------- Number of events = " << eventws->getNumberEvents() << "\n"; + return eventws; } @@ -607,7 +613,7 @@ class GenerateEventsFilterTest : public CxxTest::TestSuite TS_ASSERT(splittersws); // Check values of output workspace - size_t numintervals = 67; + size_t numintervals = 74; TS_ASSERT_EQUALS(splittersws->readY(0).size(), numintervals); std::string runstarttimestr = eventWS->run().getProperty("run_start")->value(); @@ -616,7 +622,7 @@ class GenerateEventsFilterTest : public CxxTest::TestSuite Kernel::TimeSeriesProperty *protonchargelog = dynamic_cast* >(eventWS->run().getProperty("proton_charge")); - Kernel::DateAndTime runstoptime = protonchargelog->lastTime(); + Kernel::DateAndTime runstoptime = Kernel::DateAndTime(protonchargelog->lastTime().totalNanoseconds() + 100000); // First interval TS_ASSERT_EQUALS(static_cast(splittersws->readX(0)[0]), runstarttime_ns); @@ -890,8 +896,6 @@ class GenerateEventsFilterTest : public CxxTest::TestSuite */ void test_genMultipleIntLogValuesFilterMatrixSplitter() { - std::cout << "\n==== Test Multiple Integer Log Value Filter (Matrix Splitter) ====\n" << std::endl; - // Create input DataObjects::EventWorkspace_sptr eventWS = createEventWorkspaceIntLog(); AnalysisDataService::Instance().addOrReplace("TestEventWS09", eventWS);