Skip to content

Commit 509f9c4

Browse files
authored
LoadEventAsWorkspace2D: consistent duration when using time filtering (#40963)
The calculation of the log `duration` when using time filtering was found to be inconsistent between `LoadEventNexus` and `LoadEventAsWorkspace2D`. This PR updates `LoadEventAsWorkspace2D` to be consistent with `LoadEventNexus` and update the `duration` to the time between `FilterByTimeStart` and `FilterByTimeStop`. ORNL issue: [Defect 15001](https://ornlrse.clm.ibmcloud.com/ccm/web/projects/Neutron%20Data%20Project%20(Change%20Management)#action=com.ibm.team.workitem.viewWorkItem&id=15001)
1 parent 11e6026 commit 509f9c4

File tree

5 files changed

+73
-23
lines changed

5 files changed

+73
-23
lines changed

Framework/DataHandling/src/LoadEventAsWorkspace2D.cpp

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,18 @@ void LoadEventAsWorkspace2D::exec() {
237237
Types::Core::DateAndTime filter_time_start;
238238
Types::Core::DateAndTime filter_time_stop;
239239

240+
// compute time filter bounds
241+
if (filter_time_start_sec != EMPTY_DBL()) {
242+
filter_time_start = runstart + filter_time_start_sec;
243+
} else {
244+
filter_time_start = runstart;
245+
}
246+
if (filter_time_stop_sec != EMPTY_DBL()) {
247+
filter_time_stop = runstart + filter_time_stop_sec;
248+
} else {
249+
filter_time_stop = endtime;
250+
}
251+
240252
// vector to stored to integrated counts by detector ID
241253
std::vector<uint32_t> Y(max_detid - min_detid + 1, 0);
242254

@@ -245,7 +257,7 @@ void LoadEventAsWorkspace2D::exec() {
245257
h5file.openAddress("/");
246258
h5file.openGroup("entry", "NXentry");
247259

248-
// Now we want to go through all the bankN_event entrie
260+
// Now we want to go through all the bankN_event entries
249261
prog->doReport("Reading and integrating data");
250262

251263
std::set<std::string> const classEntries = h5file.getEntriesByClass("NXevent_data");
@@ -282,25 +294,11 @@ void LoadEventAsWorkspace2D::exec() {
282294
const auto event_index =
283295
std::make_shared<std::vector<uint64_t>>(Nexus::IOHelper::readNexusVector<uint64_t>(h5file, "event_index"));
284296

285-
// if "filterTimeStart" is empty, use run start time as default
286-
if (filter_time_start_sec != EMPTY_DBL()) {
287-
filter_time_start = runstart + filter_time_start_sec;
288-
} else {
289-
filter_time_start = runstart;
290-
}
297+
// Use filter_time_start time as starting reference in time and create a pulseROI using bankPulseTimes
298+
const auto pulseROI = bankPulseTimes->getPulseIndices(filter_time_start, filter_time_stop);
291299

292-
// if "filterTimeStop" is empty, use end time as default
293-
if (filter_time_stop_sec != EMPTY_DBL()) {
294-
filter_time_stop = runstart + filter_time_stop_sec;
295-
} else {
296-
filter_time_stop = endtime;
297-
}
298-
299-
// Use filter_time_start time as starting reference in time and create a TimeROI using bankPulseTimes
300-
const auto TimeROI = bankPulseTimes->getPulseIndices(filter_time_start, filter_time_stop);
301-
302-
// set up PulseIndexer and give previous TimeROI to pulseIndexer
303-
const PulseIndexer pulseIndexer(event_index, event_index->at(0), event_ids.size(), entry_name, TimeROI);
300+
// set up PulseIndexer and give previous pulseROI to pulseIndexer
301+
const PulseIndexer pulseIndexer(event_index, event_index->at(0), event_ids.size(), entry_name, pulseROI);
304302

305303
std::vector<float> event_times;
306304
if (tof_filtering) {
@@ -310,7 +308,7 @@ void LoadEventAsWorkspace2D::exec() {
310308
event_times = Nexus::IOHelper::readNexusVector<float>(h5file, "event_time_of_flight");
311309
}
312310

313-
// Nested loop to loop through all the relavent pulses and relavent event_ids.
311+
// Nested loop to loop through all the relevant pulses and relevant event_ids.
314312
// For someone new to this, every pulse creates an entry in event_index, event_index.size() = # of pulses,
315313
// the value of event_index[i] points to the index of event_ids. In short, event_ids[event_index[i]] is the
316314
// detector id from the i-th pulse. See NXevent_data description for more details.
@@ -336,6 +334,14 @@ void LoadEventAsWorkspace2D::exec() {
336334
h5file.closeGroup();
337335
h5file.close();
338336

337+
// filter the logs the same way FilterByTime does
338+
const bool is_time_filtered = (filter_time_start_sec != EMPTY_DBL() || filter_time_stop_sec != EMPTY_DBL());
339+
if (is_time_filtered) {
340+
Kernel::TimeROI timeroi(filter_time_start, filter_time_stop);
341+
outWS->mutableRun().setTimeROI(timeroi);
342+
outWS->mutableRun().removeDataOutsideTimeROI();
343+
}
344+
339345
// determine x values
340346
const auto xBins = {center - width / 2, center + width / 2};
341347

Framework/DataHandling/src/PulseIndexer.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,12 @@ PulseIndexer::PulseIndexer(std::shared_ptr<std::vector<uint64_t> const> const &e
5353

5454
// determine if should trim the back end to remove empty pulses
5555
auto lastPulseIndex = m_roi.back();
56-
eventRange = this->getEventIndexRange(lastPulseIndex - 1);
57-
while (eventRange.first == eventRange.second && eventRange.second > 0) {
58-
--lastPulseIndex;
56+
if (lastPulseIndex > 0) {
5957
eventRange = this->getEventIndexRange(lastPulseIndex - 1);
58+
while (eventRange.first == eventRange.second && eventRange.second > 0 && lastPulseIndex > 1) {
59+
--lastPulseIndex;
60+
eventRange = this->getEventIndexRange(lastPulseIndex - 1);
61+
}
6062
}
6163

6264
// update the value if it has changed

Framework/DataHandling/test/LoadEventAsWorkspace2DTest.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "MantidAPI/AlgorithmManager.h"
1212
#include "MantidAPI/Axis.h"
1313
#include "MantidAPI/FileFinder.h"
14+
#include "MantidAPI/Run.h"
1415
#include "MantidDataHandling/LoadEventAsWorkspace2D.h"
1516
#include "MantidDataObjects/EventWorkspace.h"
1617
#include "MantidDataObjects/Workspace2D.h"
@@ -269,6 +270,13 @@ class LoadEventAsWorkspace2DTest : public CxxTest::TestSuite {
269270
compare->setProperty("Workspace2", outputWS3);
270271
compare->execute();
271272
TS_ASSERT(compare->getProperty("Result"));
273+
274+
// compare the duration, which should have been filtered to 5s
275+
constexpr auto expected_duration = 5.0;
276+
const auto duration1 = outputWS->run().getPropertyValueAsType<double>("duration");
277+
const auto duration2 = outputWS3->run().getPropertyValueAsType<double>("duration");
278+
TS_ASSERT_DELTA(duration1, expected_duration, 1e-6);
279+
TS_ASSERT_DELTA(duration2, expected_duration, 1e-6);
272280
}
273281

274282
void test_BSS_filterbytimeStart() {

Framework/DataHandling/test/PulseIndexerTest.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,4 +291,32 @@ class PulseIndexerTest : public CxxTest::TestSuite {
291291
TS_ASSERT_EQUALS(num_steps, 2); // calculated by hand
292292
}
293293
}
294+
295+
// regression test: constructing with a pulse_roi that contains no events must not infinite-loop
296+
void test_noEventsInPulseROI() {
297+
// event_index where pulses 1 and 2 are empty (repeated value 10)
298+
auto eventIndices = std::make_shared<std::vector<uint64_t>>();
299+
eventIndices->push_back(0);
300+
eventIndices->push_back(10);
301+
eventIndices->push_back(10); // pulse 1: empty
302+
eventIndices->push_back(10); // pulse 2: empty
303+
eventIndices->push_back(20);
304+
305+
constexpr size_t start_event_index{0};
306+
constexpr size_t total_events{20};
307+
308+
// roi selects only pulses 1 and 2, both of which have no events
309+
std::vector<size_t> roi{1, 3};
310+
PulseIndexer indexer(eventIndices, start_event_index, total_events, entry_name, roi);
311+
312+
// the roi contains no events, so the indexer should produce an empty range
313+
TS_ASSERT_EQUALS(indexer.getFirstPulseIndex(), indexer.getLastPulseIndex());
314+
315+
size_t num_steps{0};
316+
for (const auto &iter : indexer) {
317+
(void)iter;
318+
num_steps++;
319+
}
320+
TS_ASSERT_EQUALS(num_steps, 0);
321+
}
294322
};
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
- Algorithm :ref:`LoadEventAsWorkspace2D <algm-LoadEventAsWorkspace2D>` now updates the run duration log when using
2+
parameters ``FilterByTimeStart`` and/or ``FilterByTimeStop``. The behaviour is now consistent with
3+
:ref:`LoadEventNexus <algm-LoadEventNexus>` when using the same parameters.
4+
- Loading with :ref:`LoadEventAsWorkspace2D <algm-LoadEventAsWorkspace2D>` and
5+
:ref:`LoadEventNexus <algm-LoadEventNexus>` with time filtering when there are banks with no events in that time
6+
range no longer causes the loading to hang due to an infinite loop.

0 commit comments

Comments
 (0)