Mantid
Loading...
Searching...
No Matches
EventList.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
13#include "MantidKernel/Logger.h"
15#include "MantidKernel/Unit.h"
17
18#ifdef _MSC_VER
19// qualifier applied to function type has no meaning; ignored
20#pragma warning(disable : 4180)
21#endif
22#include "tbb/parallel_sort.h"
23#ifdef _MSC_VER
24#pragma warning(default : 4180)
25#endif
26
27#include <algorithm>
28#include <cfloat>
29#include <cmath>
30#include <functional>
31#include <limits>
32#include <stdexcept>
33
34using std::ostream;
35using std::runtime_error;
36using std::size_t;
37using std::vector;
38
39namespace Mantid::DataObjects {
40using Types::Core::DateAndTime;
41using Types::Event::TofEvent;
42using namespace Mantid::API;
43
44namespace {
45
46constexpr double SEC_TO_NANO{1.e9};
47
48// minimum event vector length to use tbb::parallel_sort
49// this is 4x what parallel_sort uses in the indidividual blocks
50constexpr size_t MIN_VEC_LENGTH_PARALLEL_SORT{2000};
51
59template <typename EventType>
60int64_t calculateCorrectedFullTime(const EventType &event, const double tofFactor, const double tofShift) {
61 return event.pulseTime().totalNanoseconds() +
62 static_cast<int64_t>(tofFactor * (event.tof() * 1.0E3) + (tofShift * 1.0E9));
63}
64
68template <typename EventType> class CompareTimeAtSample {
69private:
70 const double m_tofFactor;
71 const double m_tofShift;
72
73public:
74 CompareTimeAtSample(const double tofFactor, const double tofShift) : m_tofFactor(tofFactor), m_tofShift(tofShift) {}
75
85 bool operator()(const EventType &e1, const EventType &e2) const {
86 const auto tAtSample1 = calculateCorrectedFullTime(e1, m_tofFactor, m_tofShift);
87 const auto tAtSample2 = calculateCorrectedFullTime(e2, m_tofFactor, m_tofShift);
88 return (tAtSample1 < tAtSample2);
89 }
90};
91} // namespace
92//==========================================================================
95//==========================================================================
100bool compareEventPulseTime(const TofEvent &e1, const TofEvent &e2) { return (e1.pulseTime() < e2.pulseTime()); }
101
108bool compareEventPulseTimeTOF(const TofEvent &e1, const TofEvent &e2) {
109
110 if (e1.pulseTime() < e2.pulseTime()) {
111 return true;
112 } else if ((e1.pulseTime() == e2.pulseTime()) && (e1.tof() < e2.tof())) {
113 return true;
114 }
115
116 return false;
117}
118
119// comparator for pulse time with tolerance
121 explicit comparePulseTimeTOFDelta(const Types::Core::DateAndTime &start, const double seconds)
122 : startNano(start.totalNanoseconds()), deltaNano(static_cast<int64_t>(seconds * SEC_TO_NANO)) {}
123
124 bool operator()(const TofEvent &e1, const TofEvent &e2) {
125 // get the pulse times converted into bin number from start time
126 const int64_t e1Pulse = (e1.pulseTime().totalNanoseconds() - startNano) / deltaNano;
127 const int64_t e2Pulse = (e2.pulseTime().totalNanoseconds() - startNano) / deltaNano;
128
129 // compare with the calculated bin information
130 if (e1Pulse < e2Pulse) {
131 return true;
132 } else if ((e1Pulse == e2Pulse) && (e1.tof() < e2.tof())) {
133 return true;
134 }
135
136 return false;
137 }
138
139 int64_t startNano;
140 int64_t deltaNano;
141};
142
143struct FindBin {
144 double divisor;
145 double offset;
146 std::optional<size_t> (*findBin)(std::span<double const>, const double, const double, const double, const bool);
147 FindBin(double step, double xmin) {
148 if (step < 0) {
150 divisor = 1. / log1p(abs(step)); // use this to do change of base
151 offset = log(xmin) * divisor;
152 } else {
154 divisor = 1. / step;
155 offset = xmin * divisor;
156 }
157 }
158
159 std::optional<size_t> operator()(std::span<double const> X, const double tof, const bool findExact) {
160 return findBin(X, tof, divisor, offset, findExact);
161 }
162};
163
165// EventWorkspace is always histogram data and so is thus EventList
167 : m_histogram(HistogramData::Histogram::XMode::BinEdges, HistogramData::Histogram::YMode::Counts),
168 eventType(event_type), order(UNSORTED), mru(nullptr) {
169 switch (eventType) {
170 case TOF:
171 this->events = std::make_unique<std::vector<Mantid::Types::Event::TofEvent>>();
172 this->weightedEvents = nullptr;
173 this->weightedEventsNoTime = nullptr;
174 break;
175
176 case WEIGHTED:
177 this->events = nullptr;
178 this->weightedEvents = std::make_unique<std::vector<WeightedEvent>>();
179 this->weightedEventsNoTime = nullptr;
180 break;
181
182 case WEIGHTED_NOTIME:
183 this->events = nullptr;
184 this->weightedEventsNoTime = std::make_unique<std::vector<WeightedEventNoTime>>();
185 this->weightedEventsNoTime = nullptr;
186 break;
187 }
188}
189
195 : IEventList(specNo),
196 m_histogram(HistogramData::Histogram::XMode::BinEdges, HistogramData::Histogram::YMode::Counts),
197 weightedEvents(nullptr), weightedEventsNoTime(nullptr), eventType(TOF), order(UNSORTED), mru(mru) {
198 this->events = std::make_unique<std::vector<Mantid::Types::Event::TofEvent>>();
199}
200
203EventList::EventList(const EventList &rhs) : IEventList(rhs), m_histogram(rhs.m_histogram), mru{nullptr} {
204 // Note that operator= also assigns m_histogram, but the above use of the copy
205 // constructor avoid a memory allocation and is thus faster.
206 this->operator=(rhs);
207}
208
211EventList::EventList(const std::vector<Types::Event::TofEvent> &events)
212 : m_histogram(HistogramData::Histogram::XMode::BinEdges, HistogramData::Histogram::YMode::Counts),
213 weightedEvents(nullptr), weightedEventsNoTime(nullptr), eventType(TOF), mru(nullptr) {
214 this->events = std::make_unique<std::vector<Mantid::Types::Event::TofEvent>>(events.cbegin(), events.cend());
215 this->eventType = TOF;
216 this->order = UNSORTED;
217}
218
221EventList::EventList(const std::vector<WeightedEvent> &events)
222 : m_histogram(HistogramData::Histogram::XMode::BinEdges, HistogramData::Histogram::YMode::Counts), events(nullptr),
223 weightedEventsNoTime(nullptr), mru(nullptr) {
224 this->weightedEvents = std::make_unique<std::vector<WeightedEvent>>(events.cbegin(), events.cend());
225 this->eventType = WEIGHTED;
226 this->order = UNSORTED;
227}
228
231EventList::EventList(const std::vector<WeightedEventNoTime> &events)
232 : m_histogram(HistogramData::Histogram::XMode::BinEdges, HistogramData::Histogram::YMode::Counts), events(nullptr),
233 weightedEvents(nullptr), mru(nullptr) {
234 this->weightedEventsNoTime = std::make_unique<std::vector<WeightedEventNoTime>>(events.cbegin(), events.cend());
236 this->order = UNSORTED;
237}
238
241 // clear this out of the MRU (copy of code from EventList::clear()
242 if (mru) {
243 try {
244 mru->deleteIndex(this);
245 } catch (const std::runtime_error &) {
246 // this is an ignorable error
247 }
248 }
249
250 // set all member vectors to nullptr
251 this->events.reset();
252 this->weightedEvents.reset();
253 this->weightedEventsNoTime.reset();
254}
255
257void EventList::copyDataFrom(const ISpectrum &source) { source.copyDataInto(*this); }
258
262 if (events)
263 sink.events = std::make_unique<std::vector<Types::Event::TofEvent>>(events->cbegin(), events->cend());
264 else if (sink.events)
265 sink.events = std::make_unique<std::vector<Types::Event::TofEvent>>();
266 if (weightedEvents)
267 sink.weightedEvents =
268 std::make_unique<std::vector<WeightedEvent>>(weightedEvents->cbegin(), weightedEvents->cend());
269 else if (sink.weightedEvents)
270 sink.weightedEvents = std::make_unique<std::vector<WeightedEvent>>();
272 sink.weightedEventsNoTime = std::make_unique<std::vector<WeightedEventNoTime>>(weightedEventsNoTime->cbegin(),
273 weightedEventsNoTime->cend());
274 else if (sink.weightedEventsNoTime)
275 sink.weightedEventsNoTime = std::make_unique<std::vector<WeightedEventNoTime>>();
276
277 sink.eventType = eventType;
278 sink.order = order;
279}
280
283
284// --------------------------------------------------------------------------
296void EventList::createFromHistogram(const ISpectrum *inSpec, bool GenerateZeros, bool GenerateMultipleEvents,
297 int MaxEventsPerBin) {
298 // Fresh start
299 this->clear(true);
300
301 // Get the input histogram
302 Mantid::HistogramData::HistogramX const &X = inSpec->x();
303 Mantid::HistogramData::HistogramY const &Y = inSpec->y();
304 Mantid::HistogramData::HistogramE const &E = inSpec->e();
305 if (Y.size() + 1 != X.size()) {
306 throw std::runtime_error("Expected a histogram (X vector should be 1 longer than the Y vector)");
307 }
308
309 // Copy detector IDs and spectra
310 this->copyInfoFrom(*inSpec);
311 // We need weights but have no way to set the time. So use weighted, no time
313 if (GenerateZeros)
314 this->weightedEventsNoTime->reserve(Y.size());
315
316 for (size_t i = 0; i < X.size() - 1; i++) {
317 double weight = Y[i];
318 if ((weight != 0.0 || GenerateZeros) && std::isfinite(weight)) {
319 double error = E[i];
320 // Also check that the error is not a bad number
321 if (std::isfinite(error)) {
322 if (GenerateMultipleEvents) {
323 // --------- Multiple events per bin ----------
324 double errorSquared = error * error;
325 // Find how many events to fake
326 double val = weight / E[i];
327 val *= val;
328 // Convert to int with slight rounding up. This is to avoid rounding
329 // errors
330 auto numEvents = int(val + 0.2);
331 if (numEvents < 1)
332 numEvents = 1;
333 if (numEvents > MaxEventsPerBin)
334 numEvents = MaxEventsPerBin;
335 // Scale the weight and error for each
336 weight /= numEvents;
337 errorSquared /= numEvents;
338
339 // Spread the TOF. e.g. 2 events = 0.25, 0.75.
340 double tofStep = (X[i + 1] - X[i]) / (numEvents);
341 for (size_t j = 0; j < size_t(numEvents); j++) {
342 double tof = X[i] + tofStep * (0.5 + double(j));
343 // Create and add the event
344 // TODO: try emplace_back() here.
345 weightedEventsNoTime->emplace_back(tof, weight, errorSquared);
346 }
347 } else {
348 // --------- Single event per bin ----------
349 // TOF = midpoint of the bin
350 double tof = (X[i] + X[i + 1]) / 2.0;
351 // Error squared is carried in the event
352 double errorSquared = E[i];
353 errorSquared *= errorSquared;
354 // Create and add the event
355 weightedEventsNoTime->emplace_back(tof, weight, errorSquared);
356 }
357 } // error is nont NAN or infinite
358 } // weight is non-zero, not NAN, and non-infinite
359 } // (each bin)
360
361 // Set the X binning parameters
362 this->setSharedX(inSpec->sharedX());
363
364 // Manually set that this is sorted by TOF, since it is. This will make it
365 // "threadSafe" in other algos.
366 this->setSortOrder(TOF_SORT);
367}
368
369// --------------------------------------------------------------------------
370// --- Operators
371// -------------------------------------------------------------------
372
378 // Note that we are NOT copying the MRU pointer
379 // the EventWorkspace that possesses the EventList has already configured the mru
380 IEventList::operator=(rhs);
381 m_histogram = rhs.m_histogram;
382 rhs.copyDataInto(*this);
383 return *this;
384}
385
386// --------------------------------------------------------------------------
391EventList &EventList::operator+=(const Types::Event::TofEvent &event) {
392
393 switch (this->eventType) {
394 case TOF:
395 // Simply push the events
396 this->events->emplace_back(event);
397 break;
398
399 case WEIGHTED:
400 this->weightedEvents->emplace_back(event);
401 break;
402
403 case WEIGHTED_NOTIME:
404 this->weightedEventsNoTime->emplace_back(event);
405 break;
406 }
407
408 this->order = UNSORTED;
409 return *this;
410}
411
412// --------------------------------------------------------------------------
419EventList &EventList::operator+=(const std::vector<Types::Event::TofEvent> &more_events) {
420 switch (this->eventType) {
421 case TOF:
422 // Simply push the events
423 this->events->insert(this->events->end(), more_events.cbegin(), more_events.cend());
424 break;
425
426 case WEIGHTED:
427 // Add default weights to all the un-weighted incoming events from the list.
428 // and append to the list
429 this->weightedEvents->reserve(this->weightedEvents->size() + more_events.size());
430 std::copy(more_events.cbegin(), more_events.cend(), std::back_inserter(*this->weightedEvents));
431 break;
432
433 case WEIGHTED_NOTIME:
434 // Add default weights to all the un-weighted incoming events from the list.
435 // and append to the list
436 this->weightedEventsNoTime->reserve(this->weightedEventsNoTime->size() + more_events.size());
437 std::copy(more_events.cbegin(), more_events.cend(), std::back_inserter(*this->weightedEventsNoTime));
438 break;
439 }
440
441 this->order = UNSORTED;
442 return *this;
443}
444
445// --------------------------------------------------------------------------
454 this->switchTo(WEIGHTED);
455 this->weightedEvents->emplace_back(event);
456 this->order = UNSORTED;
457 return *this;
458}
459
460// --------------------------------------------------------------------------
468EventList &EventList::operator+=(const std::vector<WeightedEvent> &more_events) {
469 switch (this->eventType) {
470 case TOF:
471 // Need to switch to weighted
472 this->switchTo(WEIGHTED);
473 // Fall through to the insertion!
474
475 case WEIGHTED:
476 // Append the two lists
477 this->weightedEvents->insert(weightedEvents->end(), more_events.cbegin(), more_events.cend());
478 break;
479
480 case WEIGHTED_NOTIME:
481 // Add default weights to all the un-weighted incoming events from the list.
482 // and append to the list
483 this->weightedEventsNoTime->reserve(this->weightedEventsNoTime->size() + more_events.size());
484 std::copy(more_events.cbegin(), more_events.cend(), std::back_inserter(*this->weightedEventsNoTime));
485 break;
486 }
487
488 this->order = UNSORTED;
489 return *this;
490}
491
492// --------------------------------------------------------------------------
500EventList &EventList::operator+=(const std::vector<WeightedEventNoTime> &more_events) {
501 switch (this->eventType) {
502 case TOF:
503 case WEIGHTED:
504 // Need to switch to weighted with no time
506 // Fall through to the insertion!
507
508 case WEIGHTED_NOTIME:
509 // Simple appending of the two lists
510 this->weightedEventsNoTime->insert(weightedEventsNoTime->end(), more_events.cbegin(), more_events.cend());
511 break;
512 }
513
514 this->order = UNSORTED;
515 return *this;
516}
517
518// --------------------------------------------------------------------------
528 if (!more_events.empty()) {
529 // We'll let the += operator for the given vector of event lists handle it
530 switch (more_events.getEventType()) {
531 case TOF:
532 this->operator+=(*more_events.events);
533 break;
534
535 case WEIGHTED:
536 this->operator+=(*more_events.weightedEvents);
537 break;
538
539 case WEIGHTED_NOTIME:
540 this->operator+=(*more_events.weightedEventsNoTime);
541 break;
542 }
543
544 // No guaranteed order
545 if (this->empty()) {
546 this->order = more_events.order;
547 } else {
548 this->order = UNSORTED;
549 }
550 }
551
552 // Do a union between the detector IDs of both lists
553 addDetectorIDs(more_events.getDetectorIDs());
554
555 return *this;
556}
557
558// --------------------------------------------------------------------------
567template <class T1, class T2> void EventList::minusHelper(std::vector<T1> &events, const std::vector<T2> &more_events) {
568 // Make the end vector big enough in one go (avoids repeated re-allocations).
569 events.reserve(events.size() + more_events.size());
570 /* In the event of subtracting in place, calling the end() vector would make
571 * it point at the wrong place
572 * Using it caused a segault, Ticket #2306.
573 * So we cache the end (this speeds up too).
574 */
575 // We call the constructor for T1. In the case of WeightedEventNoTime, the pulse time will just be ignored.
576 std::transform(more_events.cbegin(), more_events.cend(), std::back_inserter(events),
577 [](const auto &ev) { return T1(ev.tof(), ev.pulseTime(), ev.weight() * (-1.0), ev.errorSquared()); });
578}
579
580// --------------------------------------------------------------------------
589 if (this == &more_events) {
590 // Special case, ticket #3844 part 2.
591 // When doing this = this - this,
592 // simply clear the input event list. Saves memory!
593 this->clearData();
594 return *this;
595 }
596
597 // We'll let the -= operator for the given vector of event lists handle it
598 switch (this->getEventType()) {
599 case TOF:
600 this->switchTo(WEIGHTED);
601 // Fall through
602
603 case WEIGHTED:
604 switch (more_events.getEventType()) {
605 case TOF:
606 minusHelper(*this->weightedEvents, *more_events.events);
607 break;
608 case WEIGHTED:
609 minusHelper(*this->weightedEvents, *more_events.weightedEvents);
610 break;
611 case WEIGHTED_NOTIME:
612 // TODO: Should this throw?
613 minusHelper(*this->weightedEvents, *more_events.weightedEventsNoTime);
614 break;
615 }
616 break;
617
618 case WEIGHTED_NOTIME:
619 switch (more_events.getEventType()) {
620 case TOF:
621 minusHelper(*this->weightedEventsNoTime, *more_events.events);
622 break;
623 case WEIGHTED:
624 minusHelper(*this->weightedEventsNoTime, *more_events.weightedEvents);
625 break;
626 case WEIGHTED_NOTIME:
628 break;
629 }
630 break;
631 }
632
633 // No guaranteed order
634 this->order = UNSORTED;
635
636 // NOTE: What to do about detector ID's?
637 return *this;
638}
639
640namespace {
641/*
642 * Both can be nullptr, or the values can be equal, but do not have one nullptr
643 */
644template <typename T>
645bool vectorPtrEquals(const std::unique_ptr<std::vector<T>> &left, const std::unique_ptr<std::vector<T>> &right) {
646 if (left && right) {
647 return (*left == *right);
648 ;
649 } else if ((left && !right) || (right && !left)) {
650 return false;
651 }
652 return true;
653}
654} // anonymous namespace
655
656// --------------------------------------------------------------------------
662 if (this->getNumberEvents() != rhs.getNumberEvents())
663 return false;
664 if (this->eventType != rhs.eventType)
665 return false;
666 if (this->empty())
667 return true;
668 // Check all event lists; The empty ones will compare equal
669 if (!vectorPtrEquals(events, rhs.events))
670 return false;
671 if (!vectorPtrEquals(weightedEvents, rhs.weightedEvents))
672 return false;
673 if (!vectorPtrEquals(weightedEventsNoTime, rhs.weightedEventsNoTime))
674 return false;
675
676 // nothing wasn't equal, so they are equal
677 return true;
678}
679
684bool EventList::operator!=(const EventList &rhs) const { return (!this->operator==(rhs)); }
685
686bool EventList::equals(const EventList &rhs, const double tolTof, const double tolWeight,
687 const int64_t tolPulse) const {
688 // generic checks
689 if (this->getNumberEvents() != rhs.getNumberEvents())
690 return false;
691 if (this->eventType != rhs.eventType)
692 return false;
693 if (this->empty())
694 return true;
695
696 // loop over the events
697 switch (this->eventType) {
698 case TOF: {
699 auto leftIter = this->events->cbegin();
700 auto leftEnd = this->events->cend();
701 auto rightIter = rhs.events->cbegin();
702 while (leftIter != leftEnd) {
703 if (!leftIter->equals(*rightIter, tolTof, tolPulse))
704 return false;
705 leftIter = std::next(leftIter);
706 rightIter = std::next(rightIter);
707 }
708 break;
709 }
710 case WEIGHTED: {
711 auto leftIter = this->weightedEvents->cbegin();
712 auto leftEnd = this->weightedEvents->cend();
713 auto rightIter = rhs.weightedEvents->cbegin();
714 while (leftIter != leftEnd) {
715 if (!leftIter->equals(*rightIter, tolTof, tolWeight, tolPulse))
716 return false;
717 leftIter = std::next(leftIter);
718 rightIter = std::next(rightIter);
719 }
720 break;
721 }
722 case WEIGHTED_NOTIME: {
723 auto leftIter = this->weightedEventsNoTime->cbegin();
724 auto leftEnd = this->weightedEventsNoTime->cend();
725 auto rightIter = rhs.weightedEventsNoTime->cbegin();
726 while (leftIter != leftEnd) {
727 if (!leftIter->equals(*rightIter, tolTof, tolWeight))
728 return false;
729 leftIter = std::next(leftIter);
730 rightIter = std::next(rightIter);
731 }
732 break;
733 }
734 default:
735 break;
736 }
737
738 // anything that gets this far is equal within tolerances
739 return true;
740}
741
742// -----------------------------------------------------------------------------------------------
747
748// -----------------------------------------------------------------------------------------------
753 switch (newType) {
754 case TOF:
755 if (eventType != TOF)
756 throw std::runtime_error("EventList::switchTo() called on an EventList with weights to go down to TofEvent's. "
757 "This would remove weight information and therefore is not possible.");
758 break;
759
760 case WEIGHTED:
762 break;
763
764 case WEIGHTED_NOTIME:
766 break;
767 }
768 // Make sure to free memory
769 this->clearUnused();
770}
771
772// -----------------------------------------------------------------------------------------------
777 switch (eventType) {
778 case WEIGHTED:
779 // Do nothing; it already is weighted
780 return;
781
782 case WEIGHTED_NOTIME:
783 throw std::runtime_error("EventList::switchToWeightedEvents() called on an EventList with WeightedEventNoTime's. "
784 "It has lost the pulse time information and can't go back to WeightedEvent's.");
785 break;
786
787 case TOF:
788 if (events && !events->empty()) {
789 // Convert and copy all TofEvents to the weightedEvents list.
790 weightedEvents = std::make_unique<std::vector<WeightedEvent>>(events->cbegin(), events->cend());
791 // Get rid of the old events
792 events.reset();
793 } else {
794 weightedEvents = std::make_unique<std::vector<WeightedEvent>>();
795 }
797 break;
798 }
799}
800
801// -----------------------------------------------------------------------------------------------
806 switch (eventType) {
807 case WEIGHTED_NOTIME:
808 // Do nothing if already there
809 return;
810
811 case TOF: {
812 if (events && !events->empty()) {
813 // Convert and copy all TofEvents to the weightedEvents list.
814 this->weightedEventsNoTime = std::make_unique<std::vector<WeightedEventNoTime>>(events->cbegin(), events->cend());
815 // Get rid of the old events
816 events.reset();
817 } else {
818 this->weightedEventsNoTime = std::make_unique<std::vector<WeightedEventNoTime>>();
819 }
820 break;
821 }
822
823 case WEIGHTED: {
824 // Convert and copy all TofEvents to the weightedEvents list.
825 if (weightedEvents && !weightedEvents->empty()) {
827 std::make_unique<std::vector<WeightedEventNoTime>>(weightedEvents->cbegin(), weightedEvents->cend());
828 // Get rid of the old events
829 weightedEvents.reset();
830 } else {
831 this->weightedEventsNoTime = std::make_unique<std::vector<WeightedEventNoTime>>();
832 }
833 break;
834 }
835 }
837}
838
839// ==============================================================================================
840// --- Testing functions (mostly)
841// ---------------------------------------------------------------
842// ==============================================================================================
843
850WeightedEvent EventList::getEvent(size_t event_number) {
851 switch (eventType) {
852 case TOF:
853 return WeightedEvent(events->at(event_number));
854 case WEIGHTED:
855 return weightedEvents->at(event_number);
856 case WEIGHTED_NOTIME: {
857 const auto event = weightedEventsNoTime->at(event_number);
858 return WeightedEvent(event.tof(), 0, event.weight(), event.errorSquared());
859 }
860 }
861 throw std::runtime_error("EventList: invalid event type value was found.");
862}
863
864// ==============================================================================================
865// --- Handling the event list
866// -------------------------------------------------------------------
867// ==============================================================================================
868
876const std::vector<TofEvent> &EventList::getEvents() const {
877 if (eventType != TOF)
878 throw std::runtime_error("EventList::getEvents() called for an EventList that has weights. Use getWeightedEvents() "
879 "or getWeightedEventsNoTime().");
880 if (this->events)
881 return *this->events;
882 else
883 throw std::runtime_error("unweighted event vector is not initialized");
884}
885
892std::vector<TofEvent> &EventList::getEvents() {
893 if (eventType != TOF)
894 throw std::runtime_error("EventList::getEvents() called for an EventList that has weights. Use getWeightedEvents() "
895 "or getWeightedEventsNoTime().");
896 if (this->events)
897 return *this->events;
898 else
899 throw std::runtime_error("unweighted event vector is not initialized");
900}
901
909std::vector<WeightedEvent> &EventList::getWeightedEvents() {
910 if (eventType != WEIGHTED)
911 throw std::runtime_error("EventList::getWeightedEvents() called for an EventList not of type WeightedEvent. Use "
912 "getEvents() or getWeightedEventsNoTime().");
913 if (this->weightedEvents)
914 return *this->weightedEvents;
915 else
916 throw std::runtime_error("weighted event vector is not initialized");
917}
918
926const std::vector<WeightedEvent> &EventList::getWeightedEvents() const {
927 if (eventType != WEIGHTED)
928 throw std::runtime_error("EventList::getWeightedEvents() called for an EventList not of type WeightedEvent. Use "
929 "getEvents() or getWeightedEventsNoTime().");
930 if (this->weightedEvents)
931 return *this->weightedEvents;
932 else
933 throw std::runtime_error("weighted event vector is not initialed");
934}
935
941std::vector<WeightedEventNoTime> &EventList::getWeightedEventsNoTime() {
943 throw std::runtime_error("EventList::getWeightedEventsNoTime() called for an EventList not of type "
944 "WeightedEventNoTime. Use getEvents() or getWeightedEvents().");
945 if (this->weightedEventsNoTime)
946 return *this->weightedEventsNoTime;
947 else
948 throw std::runtime_error("weighted event no time vector is not initialed");
949}
950
956const std::vector<WeightedEventNoTime> &EventList::getWeightedEventsNoTime() const {
958 throw std::runtime_error("EventList::getWeightedEventsNoTime() called for an EventList not of type "
959 "WeightedEventNoTime. Use getEvents() or getWeightedEvents().");
960 if (this->weightedEventsNoTime)
961 return *this->weightedEventsNoTime;
962 else
963 throw std::runtime_error("weighted event no time vector is not initialed");
964}
965
969void EventList::clear(const bool removeDetIDs) {
970 if (mru) {
971 try {
972 mru->deleteIndex(this);
973 } catch (const std::runtime_error &) {
974 // this is an ignorable error
975 }
976 }
977 // clear representations that aren't for the current type
978 this->clearUnused();
979
980 // release unused memory or allocate new vector
981 // rather than creating a new object, reset existing pointer
982 if (!this->empty()) {
983 if (this->events && eventType == TOF) {
984 this->events->clear();
985 std::vector<TofEvent>().swap(*this->events); // STL Trick to release memory
986 }
987 if (this->weightedEvents && eventType == WEIGHTED) {
988 this->weightedEvents->clear();
989 std::vector<WeightedEvent>().swap(*this->weightedEvents); // STL Trick to release memory
990 }
992 this->weightedEventsNoTime->clear();
993 std::vector<WeightedEventNoTime>().swap(*this->weightedEventsNoTime); // STL Trick to release memory
994 }
995 }
996 if (removeDetIDs)
997 this->clearDetectorIDs();
998}
999
1005 if (eventType != TOF && (this->events)) {
1006 this->events.reset();
1007 }
1008 if (eventType != WEIGHTED && (this->weightedEvents)) {
1009 this->weightedEvents.reset();
1010 }
1012 this->weightedEventsNoTime.reset();
1013 }
1014}
1015
1017void EventList::clearData() { this->clear(false); }
1018
1023void EventList::setMRU(EventWorkspaceMRU *newMRU) { mru = newMRU; }
1024
1032void EventList::reserve(size_t num) {
1033 switch (this->eventType) {
1034 case TOF:
1035 this->events->reserve(num);
1036 break;
1037 case WEIGHTED:
1038 this->weightedEvents->reserve(num);
1039 break;
1040 case WEIGHTED_NOTIME:
1041 this->weightedEventsNoTime->reserve(num);
1042 break;
1043 }
1044}
1045
1046// ==============================================================================================
1047// --- Sorting functions -----------------------------------------------------
1048// ==============================================================================================
1049
1050// --------------------------------------------------------------------------
1054void EventList::sort(const EventSortType order) const {
1055 if (order == UNSORTED) {
1056 return; // don't bother doing anything. Why did you ask to unsort?
1057 } else if (order == TOF_SORT) {
1058 this->sortTof();
1059 } else if (order == PULSETIME_SORT) {
1060 this->sortPulseTime();
1061 } else if (order == PULSETIMETOF_SORT) {
1062 this->sortPulseTimeTOF();
1063 } else if (order == PULSETIMETOF_DELTA_SORT) {
1064 throw std::invalid_argument("sorting by pulse time with delta requires "
1065 "extra parameters. Use sortPulseTimeTOFDelta "
1066 "instead.");
1067 } else if (order == TIMEATSAMPLE_SORT) {
1068 throw std::invalid_argument("sorting by time at sample requires extra "
1069 "parameters. Use sortTimeAtSample instead.");
1070 } else {
1071 throw runtime_error("Invalid sort type in EventList::sort(EventSortType)");
1072 }
1073}
1074
1075// --------------------------------------------------------------------------
1080void EventList::setSortOrder(const EventSortType order) const { this->order = order; }
1081
1082namespace {
1083// these are abstractions
1084template <class RandomIt> void switchable_sort(RandomIt first, RandomIt last) {
1085 const auto vec_size = static_cast<size_t>(std::distance(first, last));
1086 if (vec_size < 2)
1087 return;
1088 else if (vec_size < MIN_VEC_LENGTH_PARALLEL_SORT)
1089 std::sort(first, last);
1090 else
1091 tbb::parallel_sort(first, last);
1092}
1093
1094template <class RandomIt, class Compare> void switchable_sort(RandomIt first, RandomIt last, Compare comp) {
1095 const auto vec_size = static_cast<size_t>(std::distance(first, last));
1096 if (vec_size < 2)
1097 return;
1098 else if (vec_size < MIN_VEC_LENGTH_PARALLEL_SORT)
1099 std::sort(first, last, std::move(comp));
1100 else
1101 tbb::parallel_sort(first, last, comp);
1102}
1103} // anonymous namespace
1104
1105// --------------------------------------------------------------------------
1108 // nothing to do
1109 if (this->order == TOF_SORT)
1110 return;
1111
1112 // Avoid sorting from multiple threads
1113 std::lock_guard<std::mutex> _lock(m_sortMutex);
1114 // If the list was sorted while waiting for the lock, return.
1115 if (this->order == TOF_SORT) // cppcheck-suppress identicalConditionAfterEarlyExit
1116 return;
1117
1118 switch (eventType) {
1119 case TOF:
1120 switchable_sort(events->begin(), events->end());
1121 break;
1122 case WEIGHTED:
1123 switchable_sort(weightedEvents->begin(), weightedEvents->end());
1124 break;
1125 case WEIGHTED_NOTIME:
1126 switchable_sort(weightedEventsNoTime->begin(), weightedEventsNoTime->end());
1127 break;
1128 }
1129 // Save the order to avoid unnecessary re-sorting.
1130 this->order = TOF_SORT;
1131}
1132
1133// --------------------------------------------------------------------------
1142void EventList::sortTimeAtSample(const double &tofFactor, const double &tofShift, bool forceResort) const {
1143 // Check pre-cached sort flag.
1144 if (this->order == TIMEATSAMPLE_SORT && !forceResort)
1145 return;
1146
1147 // Avoid sorting from multiple threads
1148 std::lock_guard<std::mutex> _lock(m_sortMutex);
1149 // If the list was sorted while waiting for the lock, return.
1150 if (this->order == TIMEATSAMPLE_SORT && !forceResort)
1151 return;
1152
1153 // Perform sort.
1154 switch (eventType) {
1155 case TOF: {
1156 CompareTimeAtSample<TofEvent> comparitor(tofFactor, tofShift);
1157 switchable_sort(events->begin(), events->end(), comparitor);
1158 } break;
1159 case WEIGHTED: {
1160 CompareTimeAtSample<WeightedEvent> comparitor(tofFactor, tofShift);
1161 switchable_sort(weightedEvents->begin(), weightedEvents->end(), comparitor);
1162 } break;
1163 case WEIGHTED_NOTIME: {
1164 CompareTimeAtSample<WeightedEventNoTime> comparitor(tofFactor, tofShift);
1165 switchable_sort(weightedEventsNoTime->begin(), weightedEventsNoTime->end(), comparitor);
1166 } break;
1167 }
1168 // Save the order to avoid unnecessary re-sorting.
1169 this->order = TIMEATSAMPLE_SORT;
1170}
1171
1172// --------------------------------------------------------------------------
1175 if (this->order == PULSETIME_SORT || this->order == PULSETIMETOF_SORT)
1176 return; // nothing to do
1177
1178 // Avoid sorting from multiple threads
1179 std::lock_guard<std::mutex> _lock(m_sortMutex);
1180 // If the list was sorted while waiting for the lock, return.
1181 if (this->order == PULSETIME_SORT)
1182 return;
1183
1184 // Perform sort.
1185 switch (eventType) {
1186 case TOF:
1187 switchable_sort(events->begin(), events->end(), compareEventPulseTime);
1188 break;
1189 case WEIGHTED:
1190 switchable_sort(weightedEvents->begin(), weightedEvents->end(), compareEventPulseTime);
1191 break;
1192 case WEIGHTED_NOTIME:
1193 // Do nothing; there is no time to sort
1194 break;
1195 }
1196 // Save the order to avoid unnecessary re-sorting.
1197 this->order = PULSETIME_SORT;
1198}
1199
1200/*
1201 * Sort events by pulse time + TOF
1202 * (the absolute time)
1203 */
1205 if (this->order == PULSETIMETOF_SORT)
1206 return; // already ordered
1207
1208 // Avoid sorting from multiple threads
1209 std::lock_guard<std::mutex> _lock(m_sortMutex);
1210 // If the list was sorted while waiting for the lock, return.
1211 if (this->order == PULSETIMETOF_SORT) // cppcheck-suppress identicalConditionAfterEarlyExit
1212 return;
1213
1214 switch (eventType) {
1215 case TOF:
1216 switchable_sort(events->begin(), events->end(), compareEventPulseTimeTOF);
1217 break;
1218 case WEIGHTED:
1219 switchable_sort(weightedEvents->begin(), weightedEvents->end(), compareEventPulseTimeTOF);
1220 break;
1221 case WEIGHTED_NOTIME:
1222 // Do nothing; there is no time to sort
1223 break;
1224 }
1225
1226 // Save
1227 this->order = PULSETIMETOF_SORT;
1228}
1229
1237void EventList::sortPulseTimeTOFDelta(const Types::Core::DateAndTime &start, const double seconds) const {
1238 // Avoid sorting from multiple threads
1239 std::lock_guard<std::mutex> _lock(m_sortMutex);
1240
1241 std::function<bool(const TofEvent &, const TofEvent &)> comparator = comparePulseTimeTOFDelta(start, seconds);
1242
1243 switch (eventType) {
1244 case TOF:
1245 switchable_sort(events->begin(), events->end(), std::move(comparator));
1246 break;
1247 case WEIGHTED:
1248 switchable_sort(weightedEvents->begin(), weightedEvents->end(), std::move(comparator));
1249 break;
1250 case WEIGHTED_NOTIME:
1251 // Do nothing; there is no time to sort
1252 break;
1253 }
1254
1255 this->order = UNSORTED; // so the function always re-runs
1256}
1257
1258// --------------------------------------------------------------------------
1260bool EventList::isSortedByTof() const { return (this->order == TOF_SORT); }
1261
1262// --------------------------------------------------------------------------
1265
1266// --------------------------------------------------------------------------
1273 // reverse the histogram bin parameters. Changing X invalidates any histogram cached in the MRU,
1274 // so drop it first -- this is what the deprecated dataX() used to do implicitly.
1275 if (mru)
1276 mru->deleteIndex(this);
1277 auto &x = mutableX();
1278 std::reverse(x.begin(), x.end());
1279
1280 // flip the events if they are tof sorted
1281 if (this->isSortedByTof()) {
1282 switch (eventType) {
1283 case TOF:
1284 std::reverse(this->events->begin(), this->events->end());
1285 break;
1286 case WEIGHTED:
1287 std::reverse(this->weightedEvents->begin(), this->weightedEvents->end());
1288 break;
1289 case WEIGHTED_NOTIME:
1290 std::reverse(this->weightedEventsNoTime->begin(), this->weightedEventsNoTime->end());
1291 break;
1292 }
1293 // And we are still sorted! :)
1294 }
1295 // Otherwise, do nothing. If it was sorted by pulse time, then it still is
1296}
1297
1298// --------------------------------------------------------------------------
1307 switch (eventType) {
1308 case TOF:
1309 return (this->events) ? this->events->size() : 0;
1310 case WEIGHTED:
1311 return (this->weightedEvents) ? this->weightedEvents->size() : 0;
1312 case WEIGHTED_NOTIME:
1313 return (this->weightedEventsNoTime) ? this->weightedEventsNoTime->size() : 0;
1314 }
1315 throw std::runtime_error("EventList: invalid event type value was found.");
1316}
1317
1321bool EventList::empty() const {
1322 switch (eventType) {
1323 case TOF:
1324 if (this->events)
1325 return this->events->empty();
1326 else
1327 throw std::runtime_error("TOF events is nullptr");
1328 case WEIGHTED:
1329 if (this->weightedEvents)
1330 return this->weightedEvents->empty();
1331 else
1332 throw std::runtime_error("WEIGHTED events is nullptr");
1333 case WEIGHTED_NOTIME:
1334 if (this->weightedEventsNoTime)
1335 return this->weightedEventsNoTime->empty();
1336 else
1337 throw std::runtime_error("WEIGHTED_NOTIME events is nullptr");
1338 }
1339 throw std::runtime_error("EventList: invalid event type value was found.");
1340}
1341
1342// --------------------------------------------------------------------------
1350 switch (eventType) {
1351 case TOF:
1352 return this->events->capacity() * sizeof(TofEvent) + sizeof(EventList);
1353 case WEIGHTED:
1354 return this->weightedEvents->capacity() * sizeof(WeightedEvent) + sizeof(EventList);
1355 case WEIGHTED_NOTIME:
1356 return this->weightedEventsNoTime->capacity() * sizeof(WeightedEventNoTime) + sizeof(EventList);
1357 }
1358 throw std::runtime_error("EventList: invalid event type value was found.");
1359}
1360
1361// --------------------------------------------------------------------------
1365 size_t x_size = x().size();
1366 if (x_size > 1)
1367 return x_size - 1;
1368 else
1369 return 0;
1370}
1371
1372// ==============================================================================================
1373// --- Setting the Histogram X axis, without recalculating the histogram
1374// -----------------------
1375// ==============================================================================================
1376
1381[[deprecated("The setX() method is deprecated in Mantid 7.0. Use setSharedX() instead.")]]
1383 m_histogram.setSharedX(X);
1384 if (mru)
1385 mru->deleteIndex(this);
1386}
1387
1391[[deprecated("The dataX() method is deprecated in Mantid 7.0. Use mutableX() instead.")]]
1393 if (mru)
1394 mru->deleteIndex(this);
1395 // Handing out a mutable MantidVec would allow the length to be changed, so
1396 // FixedLengthVector::mutableRawData() is protected and Histogram is its only friend. The
1397 // deprecated legacy accessor is therefore the only way to express this.
1398 GNU_DIAG_OFF("deprecated-declarations")
1399 MSVC_DIAG_OFF(4996)
1400 return m_histogram.dataX();
1401 MSVC_DIAG_ON(4996)
1402 GNU_DIAG_ON("deprecated-declarations")
1403}
1404
1407[[deprecated("The dataX() method is deprecated in Mantid 7.0. Use x() instead.")]]
1409 return m_histogram.x().rawData();
1410}
1411
1413[[deprecated("The readX() method is deprecated in Mantid 7.0. Use x() instead.")]]
1415 return m_histogram.x().rawData();
1416}
1417
1419[[deprecated("The sharedX() method is deprecated in Mantid 7.0. Use sharedX() instead,")]]
1423
1425GNU_DIAG_OFF("deprecated-declarations")
1426MSVC_DIAG_OFF(4996)
1427[[deprecated("The dataDx() method is deprecated in Mantid 7.0. Use mutableDx() instead.")]]
1429 return m_histogram.dataDx();
1430}
1431MSVC_DIAG_ON(4996)
1432GNU_DIAG_ON("deprecated-declarations")
1434[[deprecated("The dataDx() method is deprecated in Mantid 7.0. Use dx() instead.")]]
1435const MantidVec &EventList::dataDx() const {
1436 return m_histogram.dx().rawData();
1437}
1439[[deprecated("The readDx() method is deprecated in Mantid 7.0. Use dx() instead.")]]
1441 return m_histogram.dx().rawData();
1442}
1443
1444// ==============================================================================================
1445// --- Return Data Vectors --------------------------------------------------
1446// ==============================================================================================
1447
1454 auto Y = new MantidVec();
1455 MantidVec E;
1456 // Generate the Y histogram while skipping the E if possible.
1457 generateHistogram(x().rawData(), *Y, E, true);
1458 return Y;
1459}
1460
1467 MantidVec Y;
1468 auto E = new MantidVec();
1469 generateHistogram(x().rawData(), Y, *E);
1470 // Y is unused.
1471 return E;
1472}
1473
1475HistogramData::Histogram EventList::getHistogram() const { return m_histogram; }
1476
1477HistogramData::Histogram EventList::histogram() const {
1478 HistogramData::Histogram ret(m_histogram);
1479 ret.setSharedY(sharedY());
1480 ret.setSharedE(sharedE());
1481 return ret;
1482}
1483
1484HistogramData::Counts EventList::counts() const { return histogram().counts(); }
1485
1486HistogramData::CountVariances EventList::countVariances() const { return histogram().countVariances(); }
1487
1488HistogramData::CountStandardDeviations EventList::countStandardDeviations() const {
1489 return histogram().countStandardDeviations();
1490}
1491
1492HistogramData::Frequencies EventList::frequencies() const { return histogram().frequencies(); }
1493
1494HistogramData::FrequencyVariances EventList::frequencyVariances() const { return histogram().frequencyVariances(); }
1495
1496HistogramData::FrequencyStandardDeviations EventList::frequencyStandardDeviations() const {
1497 return histogram().frequencyStandardDeviations();
1498}
1499
1500const HistogramData::HistogramY &EventList::y() const {
1501 if (!mru)
1502 throw std::runtime_error("'EventList::y()' called with no MRU set. This is not allowed.");
1503
1504 return *sharedY();
1505}
1506const HistogramData::HistogramE &EventList::e() const {
1507 if (!mru)
1508 throw std::runtime_error("'EventList::e()' called with no MRU set. This is not allowed.");
1509
1510 return *sharedE();
1511}
1513 // This is the thread number from which this function was called.
1514 const int thread = PARALLEL_THREAD_NUMBER;
1515
1517
1518 // Is the data in the mrulist?
1519 if (mru) {
1520 mru->ensureEnoughBuffersY(static_cast<size_t>(thread));
1521 yData = mru->findY(static_cast<size_t>(thread), this);
1522 }
1523
1524 if (!yData) {
1525 MantidVec Y;
1526 MantidVec E;
1527 this->generateHistogram(x().rawData(), Y, E);
1528
1529 // Create the MRU object
1530 yData = Kernel::make_cow<HistogramData::HistogramY>(std::move(Y));
1531
1532 // Lets save it in the MRU
1533 if (mru) {
1534 mru->insertY(thread, yData, this);
1535 auto eData = Kernel::make_cow<HistogramData::HistogramE>(std::move(E));
1536 mru->ensureEnoughBuffersE(thread);
1537 mru->insertE(thread, eData, this);
1538 }
1539 }
1540 return yData;
1541}
1543 // This is the thread number from which this function was called.
1544 const auto thread = static_cast<size_t>(PARALLEL_THREAD_NUMBER);
1545
1547
1548 // Is the data in the mrulist?
1549 if (mru) {
1550 mru->ensureEnoughBuffersE(thread);
1551 eData = mru->findE(thread, this);
1552 }
1553
1554 if (!eData) {
1555 // Now use that to get E -- Y values are generated from another function
1556 MantidVec Y_ignored;
1557 MantidVec E;
1558 this->generateHistogram(x().rawData(), Y_ignored, E);
1559 eData = Kernel::make_cow<HistogramData::HistogramE>(std::move(E));
1560
1561 // Lets save it in the MRU
1562 if (mru)
1563 mru->insertE(thread, eData, this);
1564 }
1565 return eData;
1566}
1573 if (!mru)
1574 throw std::runtime_error("'EventList::dataY()' called with no MRU set. This is not allowed.");
1575
1576 // WARNING: The Y data of sharedY() is stored in MRU, returning reference fine
1577 // as long as it stays there.
1578 return sharedY()->rawData();
1579}
1580
1587 if (!mru)
1588 throw std::runtime_error("'EventList::dataE()' called with no MRU set. This is not allowed.");
1589
1590 // WARNING: The E data of sharedE() is stored in MRU, returning reference fine
1591 // as long as it stays there.
1592 return sharedE()->rawData();
1593}
1594
1595namespace {
1596inline double calcNorm(const double errorSquared) {
1597 if (errorSquared == 0.)
1598 return 0;
1599 else if (errorSquared == 1.)
1600 return 1.;
1601 else
1602 return 1. / std::sqrt(errorSquared);
1603}
1604} // namespace
1605
1606// --------------------------------------------------------------------------
1615template <class T>
1616inline void EventList::compressEventsHelper(const std::vector<T> &events, std::vector<WeightedEventNoTime> &out,
1617 double tolerance) {
1618 // Clear the output. We can't know ahead of time how much space to reserve :(
1619 out.clear();
1620 // We will make a starting guess of 1/20th of the number of input events.
1621 out.reserve(events.size() / 20);
1622
1623 // The last TOF to which we are comparing.
1624 double lastTof = events.front().m_tof;
1625 // For getting an accurate average TOF
1626 double totalTof = 0;
1627 int num = 0;
1628 // Carrying weight, error, and normalization
1629 double weight = 0;
1630 double errorSquared = 0;
1631 double normalization = 0.;
1632
1633 double bin_end = lastTof;
1634 std::function<bool(const double, const double)> compareTof;
1635 std::function<double(const double, double)> next_bin;
1636
1637 if (tolerance < 0) { // log
1638 if (lastTof < 0)
1639 throw std::runtime_error("compressEvents with log binning doesn't work with negative TOF");
1640
1641 if (lastTof == 0)
1642 bin_end = fabs(tolerance);
1643
1644 // for log we do "less than" so that is matches the log binning of the Rebin algorithm
1645 compareTof = [](const double lhs, const double rhs) { return lhs < rhs; };
1646 next_bin = [tolerance](const double lastTof, double bin_end) {
1647 // advance the bin_end until we find the one that this next event falls into
1648 while (lastTof >= bin_end)
1649 bin_end = bin_end * (1 - tolerance);
1650 return bin_end;
1651 };
1652 } else { // linear
1653 // for linear we do "less than or equals" because that is how it was originally implemented
1654 compareTof = [](const double lhs, const double rhs) { return lhs <= rhs; };
1655 next_bin = [tolerance](const double lastTof, double) { return lastTof + tolerance; };
1656 }
1657
1658 // get first bin_end
1659 bin_end = next_bin(lastTof, bin_end);
1660
1661 for (auto it = events.cbegin(); it != events.cend(); it++) {
1662 if (compareTof(it->m_tof, bin_end)) {
1663 // Carry the error and weight
1664 weight += it->weight();
1665 errorSquared += it->errorSquared();
1666 // Track the average tof
1667 num++;
1668 const double norm = calcNorm(it->errorSquared());
1669 normalization += norm;
1670 totalTof += it->m_tof * norm;
1671 } else {
1672 // We exceeded the tolerance
1673 // Create a new event with the average TOF and summed weights and
1674 // squared errors.
1675 if (num == 1) {
1676 // last time-of-flight is the only one contributing
1677 out.emplace_back(lastTof, weight, errorSquared);
1678 } else if (num > 1) {
1679 out.emplace_back(totalTof / normalization, weight, errorSquared);
1680 }
1681 // Start a new combined object
1682 num = 1;
1683 const double norm = calcNorm(it->errorSquared());
1684 normalization = norm;
1685 totalTof = it->m_tof * norm;
1686 weight = it->weight();
1687 errorSquared = it->errorSquared();
1688 lastTof = it->m_tof;
1689
1690 bin_end = next_bin(lastTof, bin_end);
1691 }
1692 }
1693
1694 // Put the last event in there too with the average TOF and summed weights and
1695 // squared errors.
1696 if (num == 1) {
1697 // last time-of-flight is the only one contributing
1698 out.emplace_back(lastTof, weight, errorSquared);
1699 } else if (num > 1) {
1700 out.emplace_back(totalTof / normalization, weight, errorSquared);
1701 }
1702
1703 // If you have over-allocated by more than 5%, reduce the size.
1704 size_t excess_limit = out.size() / 20;
1705 if ((out.capacity() - out.size()) > excess_limit) {
1706 out.shrink_to_fit();
1707 }
1708}
1709
1710template <class T>
1711inline void EventList::compressFatEventsHelper(const std::vector<T> &events, std::vector<WeightedEvent> &out,
1712 const double tolerance, const Types::Core::DateAndTime &timeStart,
1713 const double seconds) {
1714 // Clear the output. We can't know ahead of time how much space to reserve :(
1715 out.clear();
1716 // We will make a starting guess of 1/20th of the number of input events.
1717 out.reserve(events.size() / 20);
1718
1719 // The last TOF to which we are comparing.
1720 double lastTof = events.front().m_tof;
1721 // For getting an accurate average TOF
1722 double totalTof = 0;
1723
1724 // pulsetime bin information - stored as int nanoseconds because it
1725 // is the implementation type for DateAndTime object
1726 const int64_t pulsetimeStart = timeStart.totalNanoseconds();
1727 const auto pulsetimeDelta = static_cast<int64_t>(seconds * SEC_TO_NANO);
1728
1729 // pulsetime information
1730 std::vector<DateAndTime> pulsetimes; // all the times for new event
1731 std::vector<double> pulsetimeWeights;
1732
1733 // Carrying weight and error
1734 double weight = 0.;
1735 double errorSquared = 0.;
1736 double tofNormalization = 0.;
1737
1738 // Move up to first event that has a large enough pulsetime. This is just in case someone starts from after the
1739 // starttime of the run. It is expected that users will normally use the default which means this will only check the
1740 // first event.
1741 auto it = events.cbegin();
1742 for (; it != events.cend(); ++it) {
1743 if (it->m_pulsetime >= timeStart)
1744 break;
1745 }
1746
1747 if (it == events.cend())
1748 throw std::runtime_error("failed to find first pulse time in the events");
1749
1750 // bin if the pulses are histogrammed
1751 int64_t lastPulseBin = (it->m_pulsetime.totalNanoseconds() - pulsetimeStart) / pulsetimeDelta;
1752
1753 double bin_end = lastTof;
1754 double tof_min{0};
1755 std::function<bool(const double, const double)> compareTof;
1756 std::function<double(const double, double)> next_bin;
1757
1758 if (tolerance < 0) { // log
1759 // for log we do "less than" so that is matches the log binning of the Rebin algorithm
1760 compareTof = [](const double lhs, const double rhs) { return lhs < rhs; };
1761 next_bin = [tolerance](const double lastTof, double bin_end) {
1762 // advance the bin_end until we find the one that this next event falls into
1763 while (lastTof >= bin_end)
1764 bin_end = bin_end * (1 - tolerance);
1765 return bin_end;
1766 };
1767
1768 // get minimum Tof so that binning is consistent across all pulses
1769 const auto event_min = std::min_element(
1770 events.cbegin(), events.cend(), [](const auto &left, const auto &right) { return left.tof() < right.tof(); });
1771 bin_end = tof_min = event_min->tof();
1772
1773 if (tof_min < 0)
1774 throw std::runtime_error("compressEvents with log binning doesn't work with negative TOF");
1775
1776 // can't start at 0 as this will create an infinite loop
1777 if (tof_min == 0)
1778 bin_end = tof_min = fabs(tolerance);
1779
1780 } else { // linear
1781 // for linear we do "less than or equals" because that is how it was originally implemented
1782 compareTof = [](const double lhs, const double rhs) { return lhs <= rhs; };
1783 next_bin = [tolerance](const double lastTof, double) { return lastTof + tolerance; };
1784 }
1785
1786 // get first bin_end
1787 bin_end = next_bin(lastTof, bin_end);
1788
1789 // loop through events and accumulate weight
1790 for (; it != events.cend(); ++it) {
1791 const int64_t eventPulseBin = (it->m_pulsetime.totalNanoseconds() - pulsetimeStart) / pulsetimeDelta;
1792 if ((eventPulseBin <= lastPulseBin) && compareTof(it->m_tof, bin_end)) {
1793 // Carry the error and weight
1794 weight += it->weight();
1795 errorSquared += it->errorSquared();
1796 double norm = calcNorm(it->errorSquared());
1797 tofNormalization += norm;
1798 // Track the average tof
1799 totalTof += it->m_tof * norm;
1800 // Accumulate the pulse times
1801 pulsetimes.emplace_back(it->m_pulsetime);
1802 pulsetimeWeights.emplace_back(norm);
1803 } else {
1804 // We exceeded the tolerance
1805 if (!pulsetimes.empty()) {
1806 // Create a new event with the average TOF and summed weights and
1807 // squared errors. 1 event used doesn't need to average
1808 if (pulsetimes.size() == 1) {
1809 out.emplace_back(lastTof, pulsetimes.front(), weight, errorSquared);
1810 } else {
1811 out.emplace_back(totalTof / tofNormalization,
1812 Kernel::DateAndTimeHelpers::averageSorted(pulsetimes, pulsetimeWeights), weight,
1813 errorSquared);
1814 }
1815 }
1816 if (tolerance < 0 && eventPulseBin != lastPulseBin)
1817 // reset the bin_end for the new pulse bin
1818 bin_end = tof_min;
1819
1820 // Start a new combined object
1821 double norm = calcNorm(it->errorSquared());
1822 totalTof = it->m_tof * norm;
1823 weight = it->weight();
1824 errorSquared = it->errorSquared();
1825 tofNormalization = norm;
1826 lastTof = it->m_tof;
1827 lastPulseBin = eventPulseBin;
1828 pulsetimes.clear();
1829 pulsetimes.emplace_back(it->m_pulsetime);
1830 pulsetimeWeights.clear();
1831 pulsetimeWeights.emplace_back(norm);
1832
1833 bin_end = next_bin(lastTof, bin_end);
1834 }
1835 }
1836
1837 // Put the last event in there too.
1838 if (!pulsetimes.empty()) {
1839 // Create a new event with the average TOF and summed weights and
1840 // squared errors. 1 event used doesn't need to average
1841 if (pulsetimes.size() == 1) {
1842 out.emplace_back(lastTof, pulsetimes.front(), weight, errorSquared);
1843 } else {
1844 out.emplace_back(totalTof / tofNormalization,
1845 Kernel::DateAndTimeHelpers::averageSorted(pulsetimes, pulsetimeWeights), weight, errorSquared);
1846 }
1847 }
1848
1849 // If you have over-allocated by more than 5%, reduce the size.
1850 size_t excess_limit = out.size() / 20;
1851 if ((out.capacity() - out.size()) > excess_limit) {
1852 out.shrink_to_fit();
1853 }
1854}
1855
1856// --------------------------------------------------------------------------
1867 if (this->empty()) {
1868 // allocate memory in correct vector
1870 destination->weightedEventsNoTime = std::make_unique<std::vector<WeightedEventNoTime>>();
1871 } else {
1872 this->sortTof();
1873 switch (eventType) {
1874 case TOF:
1875 // if (parallel)
1876 // compressEventsParallelHelper(this->events,
1877 // destination->weightedEventsNoTime, tolerance);
1878 // else
1879 destination->weightedEventsNoTime = std::make_unique<std::vector<WeightedEventNoTime>>();
1881 break;
1882
1883 case WEIGHTED:
1884 // if (parallel)
1885 // compressEventsParallelHelper(this->weightedEvents,
1886 // destination->weightedEventsNoTime, tolerance);
1887 // else
1888 destination->weightedEventsNoTime = std::make_unique<std::vector<WeightedEventNoTime>>();
1890
1891 break;
1892
1893 case WEIGHTED_NOTIME:
1894 if (destination == this) {
1895 // Put results in a temp output
1896 auto out = std::make_unique<std::vector<WeightedEventNoTime>>();
1897 // if (parallel)
1898 // compressEventsParallelHelper(this->weightedEventsNoTime,
1899 // out,
1900 // tolerance);
1901 // else
1903 // Put it back
1904 this->weightedEventsNoTime.swap(out);
1905 } else {
1906 // if (parallel)
1907 // compressEventsParallelHelper(this->weightedEventsNoTime,
1908 // destination->weightedEventsNoTime, tolerance);
1909 // else
1910 destination->weightedEventsNoTime = std::make_unique<std::vector<WeightedEventNoTime>>();
1912 }
1913 break;
1914 }
1915 }
1916 // In all cases, you end up WEIGHTED_NOTIME.
1917 destination->eventType = WEIGHTED_NOTIME;
1918 // The sort is still valid!
1919 destination->order = TOF_SORT;
1920 // Empty out storage for vectors that are now unused.
1921 destination->clearUnused();
1922}
1923
1924template <class T>
1925inline void EventList::createWeightedEvents(std::vector<WeightedEventNoTime> &out, const std::vector<double> &tof,
1926 const std::vector<T> &weight, const std::vector<T> &error) {
1927 out.clear();
1928 for (size_t i = 0; i < weight.size(); ++i) {
1929 const auto errors = static_cast<float>(error[i]);
1930 if (errors > 0)
1931 out.emplace_back(tof[i], static_cast<float>(weight[i]), errors);
1932 }
1933}
1934
1935template <class T>
1936inline void EventList::processWeightedEvents(const std::vector<T> &events, std::vector<WeightedEventNoTime> &out,
1937 const std::shared_ptr<std::vector<double>> histogram_bin_edges,
1938 struct FindBin findBin) {
1939 const auto NUM_BINS = histogram_bin_edges->size() - 1;
1940 std::vector<double> tof(NUM_BINS, 0.);
1941 std::vector<double> normalization(NUM_BINS, 0.);
1942 std::vector<float> weight(NUM_BINS, 0.);
1943 std::vector<float> error(NUM_BINS, 0.);
1944 for (const auto &ev : events) {
1945 const auto &bin_optional = findBin(*histogram_bin_edges.get(), ev.m_tof, false);
1946 if (bin_optional) {
1947 const auto bin = bin_optional.value();
1948 const double norm = calcNorm(ev.m_errorSquared);
1949 tof[bin] += ev.m_tof * norm;
1950 normalization[bin] += norm;
1951 weight[bin] += ev.m_weight;
1952 error[bin] += ev.m_errorSquared;
1953 }
1954 }
1955
1956 // normalize TOFs
1957 std::transform(tof.begin(), tof.end(), normalization.begin(), tof.begin(), std::divides<double>());
1958
1959 createWeightedEvents(out, tof, weight, error);
1960}
1961
1963 const std::shared_ptr<std::vector<double>> histogram_bin_edges) {
1964 if (this->empty()) {
1965 // allocate memory in correct vector
1967 destination->weightedEventsNoTime = std::make_unique<std::vector<WeightedEventNoTime>>();
1968 } else {
1969 const auto NUM_BINS = histogram_bin_edges->size() - 1;
1970 const auto xmin = static_cast<double>(histogram_bin_edges->front());
1971
1972 auto findBin = FindBin(tolerance, xmin);
1973
1974 switch (eventType) {
1975 case TOF: {
1976 std::vector<double> tof(NUM_BINS, 0);
1977 std::vector<uint32_t> count(NUM_BINS, 0);
1978 for (const auto &ev : *this->events) {
1979 const auto &bin_optional = findBin(*histogram_bin_edges.get(), ev.m_tof, false);
1980 if (bin_optional) {
1981 const auto bin = bin_optional.value();
1982 count[bin]++;
1983 tof[bin] += ev.m_tof;
1984 }
1985 }
1986
1987 // average TOFs
1988 std::transform(tof.begin(), tof.end(), count.begin(), tof.begin(), std::divides<double>());
1989
1990 destination->weightedEventsNoTime = std::make_unique<std::vector<WeightedEventNoTime>>();
1992 break;
1993 }
1994
1995 case WEIGHTED: {
1996 destination->weightedEventsNoTime = std::make_unique<std::vector<WeightedEventNoTime>>();
1997 processWeightedEvents(*this->weightedEvents, *destination->weightedEventsNoTime, histogram_bin_edges, findBin);
1998 break;
1999 }
2000 case WEIGHTED_NOTIME:
2001 if (destination == this) {
2002 // Put results in a temp output
2003 auto out = std::make_unique<std::vector<WeightedEventNoTime>>();
2004 processWeightedEvents(*this->weightedEventsNoTime, *out, histogram_bin_edges, findBin);
2005 // Put it back
2006 this->weightedEventsNoTime.swap(out);
2007 } else {
2008 destination->weightedEventsNoTime = std::make_unique<std::vector<WeightedEventNoTime>>();
2009 processWeightedEvents(*this->weightedEventsNoTime, *destination->weightedEventsNoTime, histogram_bin_edges,
2010 findBin);
2011 }
2012 break;
2013 }
2014 }
2015
2016 // In all cases, you end up WEIGHTED_NOTIME.
2017 destination->eventType = WEIGHTED_NOTIME;
2018 // The result will be sorted
2019 destination->order = TOF_SORT;
2020 // Empty out storage for vectors that are now unused.
2021 destination->clearUnused();
2022}
2023
2024void EventList::compressFatEvents(const double tolerance, const Mantid::Types::Core::DateAndTime &timeStart,
2025 const double seconds, EventList *destination) {
2026 if (this->empty()) {
2027 // allocate memory in correct vector
2028 if (eventType != WEIGHTED)
2029 destination->weightedEvents = std::make_unique<std::vector<WeightedEvent>>();
2030 } else {
2031 switch (eventType) {
2032 case WEIGHTED_NOTIME:
2033 throw std::invalid_argument("Cannot compress events that do not have pulsetime");
2034 case TOF:
2035 this->sortPulseTimeTOFDelta(timeStart, seconds);
2036 destination->weightedEvents = std::make_unique<std::vector<WeightedEvent>>();
2037 compressFatEventsHelper(*this->events, *destination->weightedEvents, tolerance, timeStart, seconds);
2038 break;
2039 case WEIGHTED:
2040 this->sortPulseTimeTOFDelta(timeStart, seconds);
2041 if (destination == this) {
2042 // Put results in a temp output
2043 auto out = std::make_unique<std::vector<WeightedEvent>>();
2044 compressFatEventsHelper(*this->weightedEvents, *out, tolerance, timeStart, seconds);
2045 // Put it back
2046 this->weightedEvents.swap(out);
2047 } else {
2048 destination->weightedEvents = std::make_unique<std::vector<WeightedEvent>>();
2049 compressFatEventsHelper(*this->weightedEvents, *destination->weightedEvents, tolerance, timeStart, seconds);
2050 }
2051 break;
2052 }
2053 }
2054 // In all cases, you end up WEIGHTED_NOTIME.
2055 destination->eventType = WEIGHTED;
2056 // The sort order is pulsetimetof as we've compressed out the tolerance
2057 destination->order = PULSETIMETOF_SORT;
2058 // Empty out storage for vectors that are now unused.
2059 destination->clearUnused();
2060}
2061
2062// --------------------------------------------------------------------------
2072template <class T>
2073typename std::vector<T>::const_iterator static findFirstEvent(const std::vector<T> &events, T seek_tof) {
2074 return std::find_if_not(events.cbegin(), events.cend(), [seek_tof](const T &x) { return x < seek_tof; });
2075}
2076
2077// --------------------------------------------------------------------------
2087template <class T>
2088typename std::vector<T>::const_iterator EventList::findFirstPulseEvent(const std::vector<T> &events,
2089 const double seek_pulsetime) {
2090 auto itev = events.cbegin();
2091 auto itev_end = events.cend(); // cache for speed
2092
2093 // if tof < X[0], that means that you need to skip some events
2094 while ((itev != itev_end) && (static_cast<double>(itev->pulseTime().totalNanoseconds()) < seek_pulsetime))
2095 itev++;
2096 // Better fix would be to use a binary search instead of the linear one used
2097 // here.
2098 return itev;
2099}
2100
2101// --------------------------------------------------------------------------
2114template <class T>
2115typename std::vector<T>::const_iterator
2116EventList::findFirstTimeAtSampleEvent(const std::vector<T> &events, const double seek_time, const double &tofFactor,
2117 const double &tofOffset) const {
2118 auto itev = events.cbegin();
2119 auto itev_end = events.cend(); // cache for speed
2120
2121 // if tof < X[0], that means that you need to skip some events
2122 while ((itev != itev_end) &&
2123 (static_cast<double>(calculateCorrectedFullTime(*itev, tofFactor, tofOffset)) < seek_time))
2124 itev++;
2125 // Better fix would be to use a binary search instead of the linear one used
2126 // here.
2127 return itev;
2128}
2129
2130// --------------------------------------------------------------------------
2140template <class T> typename std::vector<T>::iterator static findFirstEvent(std::vector<T> &events, T seek_tof) {
2141 return std::find_if_not(events.begin(), events.end(), [seek_tof](const T &x) { return x < seek_tof; });
2142}
2143
2144// --------------------------------------------------------------------------
2154template <class T>
2155void EventList::histogramForWeightsHelper(const std::vector<T> &events, std::span<double const> X, MantidVec &Y,
2156 MantidVec &E) {
2157 // For slight speed=up.
2158 size_t x_size = X.size();
2159
2160 if (x_size <= 1) {
2161 // X was not set. Return an empty array.
2162 Y.resize(0, 0);
2163 return;
2164 }
2165
2166 // If the sizes are the same, then the "resize" command will NOT clear the original values.
2167 bool mustFill = (Y.size() == x_size - 1);
2168 // Clear the Y data, assign all to 0.
2169 Y.resize(x_size - 1, 0.0);
2170 // Clear the Error data, assign all to 0.
2171 // Note: Errors will be squared until the last step.
2172 E.resize(x_size - 1, 0.0);
2173
2174 if (mustFill) {
2175 // We must make sure the starting point is 0.0
2176 std::fill(Y.begin(), Y.end(), 0.0);
2177 std::fill(E.begin(), E.end(), 0.0);
2178 }
2179
2180 //---------------------- Histogram without weights
2181 //---------------------------------
2182
2183 // Do we even have any events to do?
2184 if (!events.empty()) {
2185 // Iterate through all events (sorted by tof)
2186 auto itev = findFirstEvent(events, T(X[0]));
2187 auto itev_end = events.cend();
2188 // The above can still take you to end() if no events above X[0], so check
2189 // again.
2190 if (itev == itev_end)
2191 return;
2192
2193 // Find the first bin
2194 size_t bin = 0;
2195 // The tof is greater the first bin boundary, so we need to find the first
2196 // bin
2197 double tof = itev->tof();
2198 while (bin < x_size - 1) {
2199 // Within range?
2200 if ((tof >= X[bin]) && (tof < X[bin + 1])) {
2201 // Add up the weight (convert to double before adding, to preserve
2202 // precision)
2203 Y[bin] += double(itev->m_weight);
2204 E[bin] += double(itev->m_errorSquared); // square of error
2205 break;
2206 }
2207 ++bin;
2208 }
2209 // Go to the next event, we've already binned this first one.
2210 ++itev;
2211
2212 // Keep going through all the events
2213 while ((itev != itev_end) && (bin < x_size - 1)) {
2214 tof = itev->tof();
2215 while (bin < x_size - 1) {
2216 // Within range? Since both events and X are sorted, they are going to
2217 // have
2218 // tof >= X[bin] because the previous event was.
2219 if (tof < X[bin + 1]) {
2220 // Add up the weight (convert to double before adding, to preserve
2221 // precision)
2222 Y[bin] += double(itev->m_weight);
2223 E[bin] += double(itev->m_errorSquared); // square of error
2224 break;
2225 }
2226 ++bin;
2227 }
2228 ++itev;
2229 }
2230 } // end if (there are any events to histogram)
2231
2232 // Now do the sqrt of all errors
2233 std::transform(E.cbegin(), E.cend(), E.begin(), static_cast<double (*)(double)>(sqrt));
2234}
2235
2236// --------------------------------------------------------------------------
2250template <class T>
2251void EventList::histogramForWeightsHelper(const std::vector<T> &events, const double step, std::span<double const> X,
2252 MantidVec &Y, MantidVec &E) {
2253 size_t x_size = X.size();
2254
2255 if (x_size <= 1) {
2256 // X was not set. Return an empty array.
2257 Y.resize(0, 0);
2258 return;
2259 }
2260
2261 // If the sizes are the same, then the "resize" command will NOT clear the original values.
2262 bool mustFill = (Y.size() == x_size - 1);
2263 Y.resize(x_size - 1, 0.0);
2264 E.resize(x_size - 1, 0.0);
2265 if (mustFill) {
2266 // We must make sure the starting point is 0.0
2267 std::fill(Y.begin(), Y.end(), 0.0);
2268 std::fill(E.begin(), E.end(), 0.0);
2269 }
2270
2271 if (events.empty())
2272 return;
2273
2274 const auto xmin = X.front();
2275 const auto xmax = X.back();
2276
2277 auto findBin = FindBin(step, xmin);
2278
2279 for (const T &ev : events) {
2280 const double tof = ev.tof();
2281 if (tof < xmin || tof >= xmax)
2282 continue;
2283
2284 std::optional<size_t> n_bin = findBin(X, tof, true);
2285
2286 if (n_bin) {
2287 Y[n_bin.value()] += ev.weight();
2288 E[n_bin.value()] += ev.errorSquared();
2289 }
2290 }
2291
2292 // Now do the sqrt of all errors
2293 std::transform(E.cbegin(), E.cend(), E.begin(), static_cast<double (*)(double)>(sqrt));
2294}
2295
2296// --------------------------------------------------------------------------
2307 bool skipError) const {
2308 // All types of weights need to be sorted by Pulse Time
2309 this->sortPulseTime();
2310
2311 switch (eventType) {
2312 case TOF:
2313 // Make the single ones
2315 if (!skipError)
2316 this->generateErrorsHistogram(Y, E);
2317 break;
2318
2319 case WEIGHTED:
2320 throw std::runtime_error("Cannot histogram by pulse time on Weighted "
2321 "Events currently"); // This could be supported.
2322
2323 case WEIGHTED_NOTIME:
2324 throw std::runtime_error("Cannot histogram by pulse time on Weighted Events NoTime");
2325 }
2326}
2327
2340 const double &tofFactor, const double &tofOffset, bool skipError) const {
2341 // All types of weights need to be sorted by time at sample
2342 this->sortTimeAtSample(tofFactor, tofOffset);
2343
2344 switch (eventType) {
2345 case TOF:
2346 // Make the single ones
2347 this->generateCountsHistogramTimeAtSample(X, Y, tofFactor, tofOffset);
2348 if (!skipError)
2349 this->generateErrorsHistogram(Y, E);
2350 break;
2351
2352 case WEIGHTED:
2353 throw std::runtime_error("Cannot histogram by time at sample on Weighted "
2354 "Events currently"); // This could be supported.
2355
2356 case WEIGHTED_NOTIME:
2357 throw std::runtime_error("Cannot histogram by time at sample on Weighted Events NoTime");
2358 }
2359}
2360
2361// --------------------------------------------------------------------------
2371void EventList::generateHistogram(std::span<double const> X, MantidVec &Y, MantidVec &E, bool skipError) const {
2372 // All types of weights need to be sorted by TOF
2373
2374 this->sortTof();
2375
2376 switch (eventType) {
2377 case TOF:
2378 // Make the single ones
2379 this->generateCountsHistogram(X, Y);
2380 if (!skipError)
2381 this->generateErrorsHistogram(Y, E);
2382 break;
2383
2384 case WEIGHTED:
2386 break;
2387
2388 case WEIGHTED_NOTIME:
2390 break;
2391 }
2392}
2393
2394// --------------------------------------------------------------------------
2409void EventList::generateHistogram(const double step, std::span<double const> X, MantidVec &Y, MantidVec &E,
2410 bool skipError) const {
2411 // if events are already sorted, use faster sorted histogram method
2412 if (isSortedByTof() || empty())
2413 return generateHistogram(X, Y, E, skipError);
2414
2415 switch (eventType) {
2416 case TOF:
2417 this->generateCountsHistogram(step, X, Y);
2418 if (!skipError)
2419 this->generateErrorsHistogram(Y, E);
2420 break;
2421
2422 case WEIGHTED:
2423 histogramForWeightsHelper(*this->weightedEvents, step, X, Y, E);
2424 break;
2425
2426 case WEIGHTED_NOTIME:
2428 break;
2429 }
2430}
2431
2432// --------------------------------------------------------------------------
2439void EventList::generateCountsHistogramPulseTime(std::span<double const> X, MantidVec &Y) const {
2440 // For slight speed=up.
2441 size_t x_size = X.size();
2442
2443 if (x_size <= 1) {
2444 // X was not set. Return an empty array.
2445 Y.resize(0, 0);
2446 return;
2447 }
2448
2449 // Sort the events by pulsetime
2450 this->sortPulseTime();
2451 // Clear the Y data, assign all to 0.
2452 Y.resize(x_size - 1, 0);
2453
2454 //---------------------- Histogram without weights
2455 //---------------------------------
2456
2457 if (!this->events->empty()) {
2458 // Iterate through all events (sorted by pulse time)
2459 auto itev = findFirstPulseEvent(*this->events, X[0]);
2460 auto itev_end = events->cend(); // cache for speed
2461 // The above can still take you to end() if no events above X[0], so check
2462 // again.
2463 if (itev == itev_end)
2464 return;
2465
2466 // Find the first bin
2467 size_t bin = 0;
2468
2469 // The tof is greater the first bin boundary, so we need to find the first
2470 // bin
2471 double pulsetime = static_cast<double>(itev->pulseTime().totalNanoseconds());
2472 while (bin < x_size - 1) {
2473 // Within range?
2474 if ((pulsetime >= X[bin]) && (pulsetime < X[bin + 1])) {
2475 Y[bin]++;
2476 break;
2477 }
2478 ++bin;
2479 }
2480 // Go to the next event, we've already binned this first one.
2481 ++itev;
2482
2483 // Keep going through all the events
2484 while ((itev != itev_end) && (bin < x_size - 1)) {
2485 pulsetime = static_cast<double>(itev->pulseTime().totalNanoseconds());
2486 while (bin < x_size - 1) {
2487 // Within range?
2488 if ((pulsetime >= X[bin]) && (pulsetime < X[bin + 1])) {
2489 Y[bin]++;
2490 break;
2491 }
2492 ++bin;
2493 }
2494 ++itev;
2495 }
2496 } // end if (there are any events to histogram)
2497}
2498
2513void EventList::generateCountsHistogramPulseTime(const double &xMin, const double &xMax, MantidVec &Y,
2514 const double TOF_min, const double TOF_max) const {
2515
2516 if (this->events->empty())
2517 return;
2518
2519 size_t nBins = Y.size();
2520
2521 if (nBins == 0)
2522 return;
2523
2524 double step = (xMax - xMin) / static_cast<double>(nBins);
2525
2526 for (const TofEvent &ev : *this->events) {
2527 double pulsetime = static_cast<double>(ev.pulseTime().totalNanoseconds());
2528 if (pulsetime < xMin || pulsetime >= xMax)
2529 continue;
2530 if (ev.tof() < TOF_min || ev.tof() >= TOF_max)
2531 continue;
2532
2533 auto n_bin = static_cast<size_t>((pulsetime - xMin) / step);
2534 Y[n_bin]++;
2535 }
2536}
2537
2538// --------------------------------------------------------------------------
2547void EventList::generateCountsHistogramTimeAtSample(std::span<double const> X, MantidVec &Y, const double &tofFactor,
2548 const double &tofOffset) const {
2549 // For slight speed=up.
2550 const size_t x_size = X.size();
2551
2552 if (x_size <= 1) {
2553 // X was not set. Return an empty array.
2554 Y.resize(0, 0);
2555 return;
2556 }
2557
2558 // Sort the events by pulsetime
2559 this->sortTimeAtSample(tofFactor, tofOffset);
2560 // Clear the Y data, assign all to 0.
2561 Y.resize(x_size - 1, 0);
2562
2563 //---------------------- Histogram without weights
2564 //---------------------------------
2565
2566 if (!this->events->empty()) {
2567 // Iterate through all events (sorted by pulse time)
2568 auto itev = findFirstTimeAtSampleEvent(*this->events, X[0], tofFactor, tofOffset);
2569 std::vector<TofEvent>::const_iterator itev_end = events->end(); // cache for speed
2570 // The above can still take you to end() if no events above X[0], so check
2571 // again.
2572 if (itev == itev_end)
2573 return;
2574
2575 // Find the first bin
2576 size_t bin = 0;
2577
2578 auto tAtSample = static_cast<double>(calculateCorrectedFullTime(*itev, tofFactor, tofOffset));
2579 while (bin < x_size - 1) {
2580 // Within range?
2581 if ((tAtSample >= X[bin]) && (tAtSample < X[bin + 1])) {
2582 Y[bin]++;
2583 break;
2584 }
2585 ++bin;
2586 }
2587 // Go to the next event, we've already binned this first one.
2588 ++itev;
2589
2590 // Keep going through all the events
2591 while ((itev != itev_end) && (bin < x_size - 1)) {
2592 tAtSample = static_cast<double>(calculateCorrectedFullTime(*itev, tofFactor, tofOffset));
2593 while (bin < x_size - 1) {
2594 // Within range?
2595 if ((tAtSample >= X[bin]) && (tAtSample < X[bin + 1])) {
2596 Y[bin]++;
2597 break;
2598 }
2599 ++bin;
2600 }
2601 ++itev;
2602 }
2603 } // end if (there are any events to histogram)
2604}
2605
2606// --------------------------------------------------------------------------
2612void EventList::generateCountsHistogram(std::span<double const> X, MantidVec &Y) const {
2613 // For slight speed=up.
2614 size_t x_size = X.size();
2615
2616 if (x_size <= 1) {
2617 // X was not set. Return an empty array.
2618 Y.resize(0, 0);
2619 return;
2620 }
2621
2622 // Sort the events by tof
2623 this->sortTof();
2624 // Clear the Y data, assign all to 0.
2625 Y.resize(x_size - 1, 0);
2626
2627 //---------------------- Histogram without weights
2628 //---------------------------------
2629
2630 // Do we even have any events to do?
2631 if (!this->events->empty()) {
2632 // Iterate through all events (sorted by tof) placing them in the correct
2633 // bin.
2634 auto itev = findFirstEvent(*this->events, TofEvent(X[0]));
2635 const auto itend = this->events->end();
2636 // Go through all the events,
2637 for (auto itx = X.begin(); itev != itend; ++itev) {
2638 const double tof = itev->tof();
2639 itx = std::find_if(itx, X.end(), [tof](const double x) { return tof < x; });
2640 if (itx == X.end()) {
2641 break;
2642 }
2643 const auto bin = static_cast<size_t>(std::max(std::distance(X.begin(), itx) - 1, std::ptrdiff_t{0}));
2644 ++Y[bin];
2645 }
2646 } // end if (there are any events to histogram)
2647}
2648
2659std::optional<size_t> EventList::findLinearBin(std::span<double const> X, const double tof, const double divisor,
2660 const double offset, const bool findExact) {
2661 const auto bin = static_cast<size_t>(tof * divisor - offset);
2662 if (bin >= X.size())
2663 return std::nullopt;
2664 else if (findExact)
2665 return findExactBin(X, tof, bin);
2666 else
2667 return bin;
2668}
2669
2688std::optional<size_t> EventList::findLogBin(std::span<double const> X, const double tof, const double divisor,
2689 const double offset, const bool findExact) {
2690 const auto bin = static_cast<size_t>(log(tof) * divisor - offset);
2691 if (bin >= X.size())
2692 return std::nullopt;
2693 else if (findExact)
2694 return findExactBin(X, tof, bin);
2695 else
2696 return bin;
2697}
2698
2706size_t EventList::findExactBin(std::span<double const> X, const double tof, const size_t n_bin) {
2707 // is tof slower than suggested bin
2708 auto tof_of_bin = X.begin() + n_bin; // boundary suggested
2709 if (tof < *tof_of_bin)
2710 return std::move(n_bin - 1);
2711
2712 // is tof higher than suggested bin
2713 ++tof_of_bin; // move to next boundary
2714 if (tof >= *tof_of_bin)
2715 return std::move(n_bin + 1);
2716
2717 // tof is in the bin
2718 return std::move(n_bin);
2719}
2720
2721// --------------------------------------------------------------------------
2732void EventList::generateCountsHistogram(const double step, std::span<double const> X, MantidVec &Y) const {
2733 // For slight speed=up.
2734 size_t x_size = X.size();
2735
2736 if (x_size <= 1) {
2737 // X was not set. Return an empty array.
2738 Y.resize(0, 0);
2739 return;
2740 }
2741
2742 // If the sizes are the same, then the "resize" command will NOT clear the original values.
2743 bool mustFill = (Y.size() == x_size - 1);
2744 // Clear the Y data, assign all to 0.
2745 Y.resize(x_size - 1, 0);
2746 if (mustFill) // starting point is no counts
2747 std::fill(Y.begin(), Y.end(), 0.0);
2748
2749 // Do we even have any events to do?
2750 if (this->events->empty())
2751 return;
2752
2753 const auto xmin = X.front();
2754 const auto xmax = X.back();
2755
2756 auto findBin = FindBin(step, xmin);
2757
2758 for (const TofEvent &ev : *this->events) {
2759 const double tof = ev.tof();
2760 if (tof < xmin || tof >= xmax)
2761 continue;
2762
2763 const std::optional<size_t> n_bin = findBin(X, tof, true);
2764
2765 if (n_bin)
2766 Y[n_bin.value()]++;
2767 }
2768}
2769
2770// --------------------------------------------------------------------------
2778void EventList::generateErrorsHistogram(std::span<double const> Y, MantidVec &E) const {
2779 // Fill the vector for the errors, containing sqrt(count)
2780 E.resize(Y.size(), 0);
2781
2782 // windows can get confused about std::sqrt
2783 std::transform(Y.begin(), Y.end(), E.begin(), static_cast<double (*)(double)>(sqrt));
2784
2785} //----------------------------------------------------------------------------------
2786
2797template <class T>
2798void EventList::integrateHelper(std::vector<T> &events, const double minX, const double maxX, const bool entireRange,
2799 double &sum, double &error) {
2800 sum = 0;
2801 error = 0;
2802 // Nothing in the list?
2803 if (events.empty())
2804 return;
2805
2806 // Iterators for limits - whole range by default
2807 auto lowit = events.cbegin();
2808 auto highit = events.cend();
2809
2810 // But maybe we don't want the entire range?
2811 if (!entireRange) {
2812 // If a silly range was given, return 0.
2813 if (maxX < minX)
2814 return;
2815
2816 // If the first element is lower that the xmin then search for new lowit
2817 if (lowit->tof() < minX)
2818 lowit = std::lower_bound(events.cbegin(), events.cend(), minX);
2819 // If the last element is higher that the xmax then search for new lowit
2820 if ((highit - 1)->tof() > maxX) {
2821 highit = std::upper_bound(lowit, events.cend(), T(maxX));
2822 }
2823 }
2824
2825 // Sum up all the weights
2826 for (auto it = lowit; it != highit; ++it) {
2827 sum += it->weight();
2828 error += it->errorSquared();
2829 }
2830 error = std::sqrt(error);
2831}
2832
2833// --------------------------------------------------------------------------
2842double EventList::integrate(const double minX, const double maxX, const bool entireRange) const {
2843 double sum(0), error(0);
2844 integrate(minX, maxX, entireRange, sum, error);
2845 return sum;
2846}
2847
2857void EventList::integrate(const double minX, const double maxX, const bool entireRange, double &sum,
2858 double &error) const {
2859 sum = 0;
2860 error = 0;
2861 if (!entireRange) {
2862 // The event list must be sorted by TOF!
2863 this->sortTof();
2864 }
2865
2866 // Convert the list
2867 switch (eventType) {
2868 case TOF:
2869 integrateHelper(*this->events, minX, maxX, entireRange, sum, error);
2870 break;
2871 case WEIGHTED:
2872 integrateHelper(*this->weightedEvents, minX, maxX, entireRange, sum, error);
2873 break;
2874 case WEIGHTED_NOTIME:
2875 integrateHelper(*this->weightedEventsNoTime, minX, maxX, entireRange, sum, error);
2876 break;
2877 default:
2878 throw std::runtime_error("EventList: invalid event type value was found.");
2879 }
2880}
2881
2882// ==============================================================================================
2883// ----------- Conversion Functions (changing tof values)
2884// ---------------------------------------
2885// ==============================================================================================
2886
2893void EventList::convertTof(std::function<double(double)> func, const int sorting) {
2894 // fix the histogram parameter. Changing X invalidates any histogram cached in the MRU, so drop
2895 // it first -- this is what the deprecated dataX() used to do implicitly.
2896 if (mru)
2897 mru->deleteIndex(this);
2898 auto &x = mutableX();
2899 transform(x.cbegin(), x.cend(), x.begin(), func);
2900
2901 // do nothing if sorting > 0
2902 if (sorting == 0) {
2903 this->setSortOrder(UNSORTED);
2904 } else if ((sorting < 0) && (this->getSortType() == TOF_SORT)) {
2905 this->reverse();
2906 }
2907
2908 if (this->getNumberEvents() == 0)
2909 return;
2910
2911 // Convert the list
2912 switch (eventType) {
2913 case TOF:
2914 this->convertTofHelper(*this->events, func);
2915 break;
2916 case WEIGHTED:
2917 this->convertTofHelper(*this->weightedEvents, func);
2918 break;
2919 case WEIGHTED_NOTIME:
2920 this->convertTofHelper(*this->weightedEventsNoTime, func);
2921 break;
2922 }
2923}
2924
2929template <class T> void EventList::convertTofHelper(std::vector<T> &events, const std::function<double(double)> &func) {
2930 // iterate through all events
2931 for (auto &ev : events)
2932 ev.m_tof = func(ev.m_tof);
2933}
2934
2935// --------------------------------------------------------------------------
2941void EventList::convertTof(const double factor, const double offset) {
2942 // fix the histogram parameter
2943 auto &x = mutableX();
2944 x *= factor;
2945 x += offset;
2946
2947 if ((factor < 0.) && (this->getSortType() == TOF_SORT))
2948 this->reverse();
2949
2950 if (this->getNumberEvents() == 0)
2951 return;
2952
2953 // Convert the list
2954 switch (eventType) {
2955 case TOF:
2956 this->convertTofHelper(*this->events, factor, offset);
2957 break;
2958 case WEIGHTED:
2959 this->convertTofHelper(*this->weightedEvents, factor, offset);
2960 break;
2961 case WEIGHTED_NOTIME:
2962 this->convertTofHelper(*this->weightedEventsNoTime, factor, offset);
2963 break;
2964 }
2965}
2966
2967// --------------------------------------------------------------------------
2976template <class T> void EventList::convertTofHelper(std::vector<T> &events, const double factor, const double offset) {
2977 // iterate through all events
2978 for (auto &event : events) {
2979 event.m_tof = event.m_tof * factor + offset;
2980 }
2981}
2982
2983// --------------------------------------------------------------------------
2990void EventList::scaleTof(const double factor) { this->convertTof(factor, 0.0); }
2991
2992// --------------------------------------------------------------------------
2997void EventList::addTof(const double offset) { this->convertTof(1.0, offset); }
2998
2999// --------------------------------------------------------------------------
3005template <class T> void EventList::addPulsetimeHelper(std::vector<T> &events, const double seconds) {
3006 // iterate through all events
3007 for (auto &event : events) {
3008 event.m_pulsetime += seconds;
3009 }
3010}
3011
3018template <class T> void EventList::addPulsetimesHelper(std::vector<T> &events, const std::vector<double> &seconds) {
3019 auto eventIterEnd{events.end()};
3020 auto secondsIter{seconds.cbegin()};
3021 for (auto eventIter = events.begin(); eventIter < eventIterEnd; ++eventIter, ++secondsIter) {
3022 eventIter->m_pulsetime += *secondsIter;
3023 }
3024}
3025
3026// --------------------------------------------------------------------------
3031void EventList::addPulsetime(const double seconds) {
3032 if (this->getNumberEvents() == 0)
3033 return;
3034
3035 // Convert the list
3036 switch (eventType) {
3037 case TOF:
3038 this->addPulsetimeHelper(*this->events, seconds);
3039 break;
3040 case WEIGHTED:
3041 this->addPulsetimeHelper(*this->weightedEvents, seconds);
3042 break;
3043 case WEIGHTED_NOTIME:
3044 throw std::runtime_error("EventList::addPulsetime() called on an event "
3045 "list with no pulse times. You must call this "
3046 "algorithm BEFORE CompressEvents.");
3047 break;
3048 }
3049}
3050
3051// --------------------------------------------------------------------------
3056void EventList::addPulsetimes(const std::vector<double> &seconds) {
3057 if (this->getNumberEvents() == 0)
3058 return;
3059 if (this->getNumberEvents() != seconds.size()) {
3060 throw std::runtime_error("");
3061 }
3062
3063 // Convert the list
3064 switch (eventType) {
3065 case TOF:
3066 this->addPulsetimesHelper(*this->events, seconds);
3067 break;
3068 case WEIGHTED:
3069 this->addPulsetimesHelper(*this->weightedEvents, seconds);
3070 break;
3071 case WEIGHTED_NOTIME:
3072 throw std::runtime_error("EventList::addPulsetime() called on an event "
3073 "list with no pulse times. You must call this "
3074 "algorithm BEFORE CompressEvents.");
3075 break;
3076 }
3077}
3078
3079// --------------------------------------------------------------------------
3087template <class T>
3088std::size_t EventList::maskTofHelper(std::vector<T> &events, const double tofMin, const double tofMax) {
3089 // quick checks to make sure that the masking range is even in the data
3090 if (tofMin > events.crbegin()->tof())
3091 return 0;
3092 if (tofMax < events.cbegin()->tof())
3093 return 0;
3094
3095 // Find the index of the first tofMin
3096 auto it_first = std::lower_bound(events.begin(), events.end(), tofMin);
3097 if ((it_first != events.end()) && (it_first->tof() < tofMax)) {
3098 // Something was found
3099 // Look for the first one > tofMax
3100 auto it_last = std::upper_bound(it_first, events.end(), T(tofMax));
3101
3102 if (it_first >= it_last) {
3103 throw std::runtime_error("Event filter is all messed up"); // TODO
3104 }
3105
3106 size_t tmp = std::size_t(std::distance(it_first, it_last));
3107 // it_last will either be at the end (if not found) or before it.
3108 // Erase this range from the vector
3109 events.erase(it_first, it_last);
3110
3111 // Done! Sorting is still valid, no need to redo.
3112 return tmp; //(it_last - it_first); the iterators get invalid after erase
3113 }
3114 return 0; // didn't remove any events
3115}
3116
3117// --------------------------------------------------------------------------
3124void EventList::maskTof(const double tofMin, const double tofMax) {
3125 if (tofMax <= tofMin)
3126 throw std::runtime_error("EventList::maskTof: tofMax must be > tofMin");
3127
3128 // don't do anything with an emply list
3129 if (this->getNumberEvents() == 0)
3130 return;
3131
3132 // Start by sorting by tof
3133 this->sortTof();
3134
3135 // Convert the list
3136 size_t numOrig = 0;
3137 size_t numDel = 0;
3138 switch (eventType) {
3139 case TOF:
3140 numOrig = this->events->size();
3141 numDel = this->maskTofHelper(*this->events, tofMin, tofMax);
3142 break;
3143 case WEIGHTED:
3144 numOrig = this->weightedEvents->size();
3145 numDel = this->maskTofHelper(*this->weightedEvents, tofMin, tofMax);
3146 break;
3147 case WEIGHTED_NOTIME:
3148 numOrig = this->weightedEventsNoTime->size();
3149 numDel = this->maskTofHelper(*this->weightedEventsNoTime, tofMin, tofMax);
3150 break;
3151 }
3152
3153 if (numDel >= numOrig)
3154 this->clear(false);
3155}
3156
3157// --------------------------------------------------------------------------
3164template <class T> std::size_t EventList::maskConditionHelper(std::vector<T> &events, const std::vector<bool> &mask) {
3165
3166 // runs through the two synchronized vectors and delete elements
3167 // for condition false
3168 auto itm = std::find(mask.begin(), mask.end(), false);
3169 auto first = events.begin() + (itm - mask.begin());
3170
3171 if (itm != mask.end()) {
3172 for (auto ite = first; ++ite != events.end() && ++itm != mask.end();) {
3173 if (*itm != false) {
3174 *first++ = std::move(*ite);
3175 }
3176 }
3177 }
3178
3179 const auto n = static_cast<size_t>(events.end() - first);
3180 if (n != 0)
3181 events.erase(first, events.end());
3182
3183 return n;
3184}
3185
3186// --------------------------------------------------------------------------
3192void EventList::maskCondition(const std::vector<bool> &mask) {
3193
3194 // mask size must match the number of events
3195 if (this->getNumberEvents() != mask.size())
3196 throw std::runtime_error("EventList::maskTof: tofMax must be > tofMin");
3197
3198 // don't do anything with an emply list
3199 if (this->getNumberEvents() == 0)
3200 return;
3201
3202 // Convert the list
3203 size_t numOrig = 0;
3204 size_t numDel = 0;
3205 switch (eventType) {
3206 case TOF:
3207 numOrig = this->events->size();
3208 numDel = this->maskConditionHelper(*this->events, mask);
3209 break;
3210 case WEIGHTED:
3211 numOrig = this->weightedEvents->size();
3212 numDel = this->maskConditionHelper(*this->weightedEvents, mask);
3213 break;
3214 case WEIGHTED_NOTIME:
3215 numOrig = this->weightedEventsNoTime->size();
3216 numDel = this->maskConditionHelper(*this->weightedEventsNoTime, mask);
3217 break;
3218 }
3219
3220 if (numDel >= numOrig)
3221 this->clear(false);
3222}
3223
3224// --------------------------------------------------------------------------
3230template <class T> void EventList::getTofsHelper(const std::vector<T> &events, std::vector<double> &tofs) {
3231 tofs.clear();
3232 for (auto itev = events.cbegin(); itev != events.cend(); ++itev)
3233 tofs.emplace_back(itev->m_tof);
3234}
3235
3239void EventList::getTofs(std::vector<double> &tofs) const {
3240 // Set the capacity of the vector to avoid multiple resizes
3241 tofs.reserve(this->getNumberEvents());
3242
3243 // Convert the list
3244 switch (eventType) {
3245 case TOF:
3246 this->getTofsHelper(*this->events, tofs);
3247 break;
3248 case WEIGHTED:
3249 this->getTofsHelper(*this->weightedEvents, tofs);
3250 break;
3251 case WEIGHTED_NOTIME:
3252 this->getTofsHelper(*this->weightedEventsNoTime, tofs);
3253 break;
3254 }
3255}
3256
3261std::vector<double> EventList::getTofs() const {
3262 std::vector<double> tofs;
3263 this->getTofs(tofs);
3264 return tofs;
3265}
3266
3267// --------------------------------------------------------------------------
3273template <class T> void EventList::getWeightsHelper(const std::vector<T> &events, std::vector<double> &weights) {
3274 weights.clear();
3275 weights.reserve(events.size());
3276 std::transform(events.cbegin(), events.cend(), std::back_inserter(weights),
3277 [](const auto &event) { return event.weight(); });
3278}
3279
3283void EventList::getWeights(std::vector<double> &weights) const {
3284 // Set the capacity of the vector to avoid multiple resizes
3285 weights.reserve(this->getNumberEvents());
3286
3287 // Convert the list
3288 switch (eventType) {
3289 case WEIGHTED:
3290 this->getWeightsHelper(*this->weightedEvents, weights);
3291 break;
3292 case WEIGHTED_NOTIME:
3293 this->getWeightsHelper(*this->weightedEventsNoTime, weights);
3294 break;
3295 default:
3296 // not a weighted event type, return 1.0 for all.
3297 weights.assign(this->getNumberEvents(), 1.0);
3298 break;
3299 }
3300}
3301
3306std::vector<double> EventList::getWeights() const {
3307 std::vector<double> weights;
3308 this->getWeights(weights);
3309 return weights;
3310}
3311
3312// --------------------------------------------------------------------------
3318template <class T>
3319void EventList::getWeightErrorsHelper(const std::vector<T> &events, std::vector<double> &weightErrors) {
3320 weightErrors.clear();
3321 weightErrors.reserve(events.size());
3322 std::transform(events.cbegin(), events.cend(), std::back_inserter(weightErrors),
3323 [](const auto &event) { return event.error(); });
3324}
3325
3329void EventList::getWeightErrors(std::vector<double> &weightErrors) const {
3330 // Set the capacity of the vector to avoid multiple resizes
3331 weightErrors.reserve(this->getNumberEvents());
3332
3333 // Convert the list
3334 switch (eventType) {
3335 case WEIGHTED:
3336 this->getWeightErrorsHelper(*this->weightedEvents, weightErrors);
3337 break;
3338 case WEIGHTED_NOTIME:
3339 this->getWeightErrorsHelper(*this->weightedEventsNoTime, weightErrors);
3340 break;
3341 default:
3342 // not a weighted event type, return 1.0 for all.
3343 weightErrors.assign(this->getNumberEvents(), 1.0);
3344 break;
3345 }
3346}
3347
3352std::vector<double> EventList::getWeightErrors() const {
3353 std::vector<double> weightErrors;
3354 this->getWeightErrors(weightErrors);
3355 return weightErrors;
3356}
3357
3363template <typename UnaryOperation>
3364std::vector<DateAndTime> EventList::eventTimesCalculator(const UnaryOperation &timesCalc) const {
3365 std::vector<DateAndTime> times;
3366 switch (eventType) {
3367 case TOF:
3368 times.reserve(events->size());
3369 std::transform(events->cbegin(), events->cend(), std::back_inserter(times), timesCalc);
3370 break;
3371 case WEIGHTED:
3372 times.reserve(weightedEvents->size());
3373 std::transform(weightedEvents->cbegin(), weightedEvents->cend(), std::back_inserter(times), timesCalc);
3374 break;
3375 case WEIGHTED_NOTIME:
3376 times.reserve(weightedEventsNoTime->size());
3377 std::transform(weightedEventsNoTime->cbegin(), weightedEventsNoTime->cend(), std::back_inserter(times), timesCalc);
3378 break;
3379 }
3380 return times;
3381}
3382
3387std::vector<Mantid::Types::Core::DateAndTime> EventList::getPulseTimes() const {
3388 auto timeCalc = [](const auto &event) { return event.pulseTime(); };
3389 return eventTimesCalculator(timeCalc);
3390}
3391
3393std::vector<DateAndTime> EventList::getPulseTOFTimes() const {
3394 auto timeCalc = [](const auto &event) { return event.pulseTOFTime(); };
3395 return eventTimesCalculator(timeCalc);
3396}
3397
3402std::vector<DateAndTime> EventList::getPulseTOFTimesAtSample(const double &factor, const double &shift) const {
3403 auto timeCalc = [factor, shift](const auto &event) { return event.pulseTOFTimeAtSample(factor, shift); };
3404 return eventTimesCalculator(timeCalc);
3405}
3406
3407// --------------------------------------------------------------------------
3408
3409namespace { // anonymous namespace
3410template <class T> double getTofMinimumHelper(const std::vector<T> &events) {
3411 const auto result = std::min_element(events.cbegin(), events.cend(),
3412 [](const auto &left, const auto &right) { return left.tof() < right.tof(); });
3413 return result->tof();
3414}
3415
3416template <class T> double getTofMaximumHelper(const std::vector<T> &events) {
3417 const auto result = std::max_element(events.cbegin(), events.cend(),
3418 [](const auto &left, const auto &right) { return left.tof() < right.tof(); });
3419 return result->tof();
3420}
3421} // anonymous namespace
3422
3426double EventList::getTofMin() const {
3427 // set up as the maximum available double
3428 double tMin = std::numeric_limits<double>::max();
3429
3430 // no events is a soft error
3431 if (this->empty())
3432 return tMin;
3433
3434 // when events are ordered by tof just need the first value
3435 if (this->order == TOF_SORT) {
3436 switch (eventType) {
3437 case TOF:
3438 return this->events->front().tof();
3439 case WEIGHTED:
3440 return this->weightedEvents->front().tof();
3441 case WEIGHTED_NOTIME:
3442 return this->weightedEventsNoTime->front().tof();
3443 }
3444 }
3445
3446 // now we are stuck with a linear search
3447 switch (eventType) {
3448 case TOF: {
3449 tMin = getTofMinimumHelper(*this->events);
3450 break;
3451 }
3452 case WEIGHTED: {
3453 tMin = getTofMinimumHelper(*this->weightedEvents);
3454 break;
3455 }
3456 case WEIGHTED_NOTIME: {
3457 tMin = getTofMinimumHelper(*this->weightedEventsNoTime);
3458 break;
3459 }
3460 }
3461
3462 return tMin;
3463}
3464
3468double EventList::getTofMax() const {
3469 // set up as the minimum available double
3470 double tMax = std::numeric_limits<double>::lowest();
3471
3472 // no events is a soft error
3473 if (this->empty())
3474 return tMax;
3475
3476 // when events are ordered by tof just need the first value
3477 if (this->order == TOF_SORT) {
3478 switch (eventType) {
3479 case TOF:
3480 return this->events->back().tof();
3481 case WEIGHTED:
3482 return this->weightedEvents->back().tof();
3483 case WEIGHTED_NOTIME:
3484 return this->weightedEventsNoTime->back().tof();
3485 }
3486 }
3487
3488 // now we are stuck with a linear search
3489 switch (eventType) {
3490 case TOF: {
3491 tMax = getTofMaximumHelper(*this->events);
3492 break;
3493 }
3494 case WEIGHTED: {
3495 tMax = getTofMaximumHelper(*this->weightedEvents);
3496 break;
3497 }
3498 case WEIGHTED_NOTIME: {
3499 tMax = getTofMaximumHelper(*this->weightedEventsNoTime);
3500 break;
3501 }
3502 }
3503
3504 return tMax;
3505}
3506
3507// --------------------------------------------------------------------------
3508namespace { // anonymous namespace
3509template <class T> DateAndTime getPulseMinimumHelper(const std::vector<T> &events) {
3510 const auto result = std::min_element(events.cbegin(), events.cend(), [](const auto &left, const auto &right) {
3511 return left.pulseTime() < right.pulseTime();
3512 });
3513 return result->pulseTime();
3514}
3515
3516template <class T> DateAndTime getPulseMaximumHelper(const std::vector<T> &events) {
3517 const auto result = std::max_element(events.cbegin(), events.cend(), [](const auto &left, const auto &right) {
3518 return left.pulseTime() < right.pulseTime();
3519 });
3520 return result->pulseTime();
3521}
3522} // anonymous namespace
3523
3527DateAndTime EventList::getPulseTimeMin() const {
3528 // no events is a soft error
3529 if (this->empty())
3530 return DateAndTime::maximum();
3531
3532 // when events are ordered by pulse time just need the first value
3533 if (this->order == PULSETIME_SORT) {
3534 switch (eventType) {
3535 case TOF:
3536 return this->events->front().pulseTime();
3537 case WEIGHTED:
3538 return this->weightedEvents->front().pulseTime();
3539 case WEIGHTED_NOTIME:
3540 return this->weightedEventsNoTime->front().pulseTime();
3541 }
3542 }
3543
3544 // now we are stuck with a linear search
3545 switch (eventType) {
3546 case TOF:
3547 return getPulseMinimumHelper(*this->events);
3548 case WEIGHTED:
3549 return getPulseMinimumHelper(*this->weightedEvents);
3550 case WEIGHTED_NOTIME:
3551 return getPulseMinimumHelper(*this->weightedEventsNoTime);
3552 }
3553
3554 return DateAndTime::maximum();
3555}
3556
3560DateAndTime EventList::getPulseTimeMax() const {
3561 // no events is a soft error
3562 if (this->empty())
3563 return DateAndTime::minimum();
3564
3565 // when events are ordered by pulse time just need the first value
3566 if (this->order == PULSETIME_SORT) {
3567 switch (eventType) {
3568 case TOF:
3569 return this->events->back().pulseTime();
3570 case WEIGHTED:
3571 return this->weightedEvents->back().pulseTime();
3572 case WEIGHTED_NOTIME:
3573 return this->weightedEventsNoTime->back().pulseTime();
3574 }
3575 }
3576
3577 // now we are stuck with a linear search
3578 switch (eventType) {
3579 case TOF:
3580 return getPulseMaximumHelper(*this->events);
3581 case WEIGHTED:
3582 return getPulseMaximumHelper(*this->weightedEvents);
3583 case WEIGHTED_NOTIME:
3584 return getPulseMaximumHelper(*this->weightedEventsNoTime);
3585 }
3586
3587 return DateAndTime::minimum();
3588}
3589
3590void EventList::getPulseTimeMinMax(Mantid::Types::Core::DateAndTime &tMin,
3591 Mantid::Types::Core::DateAndTime &tMax) const {
3592 // set up as the minimum available date time.
3593 tMax = DateAndTime::minimum();
3594 tMin = DateAndTime::maximum();
3595
3596 // no events is a soft error
3597 if (this->empty())
3598 return;
3599
3600 // when events are ordered by pulse time just need the first/last values
3601 if (this->order == PULSETIME_SORT) {
3602 switch (eventType) {
3603 case TOF:
3604 tMin = this->events->front().pulseTime();
3605 tMax = this->events->back().pulseTime();
3606 return;
3607 case WEIGHTED:
3608 tMin = this->weightedEvents->front().pulseTime();
3609 tMax = this->weightedEvents->back().pulseTime();
3610 return;
3611 case WEIGHTED_NOTIME:
3612 tMin = this->weightedEventsNoTime->front().pulseTime();
3613 tMax = this->weightedEventsNoTime->back().pulseTime();
3614 return;
3615 }
3616 }
3617
3618 // now we are stuck with a linear search
3619 // could this be done more efficiently than using ->at?
3620 size_t numEvents = this->getNumberEvents();
3621 DateAndTime temp = tMax; // start with the smallest possible value
3622 for (size_t i = 0; i < numEvents; i++) {
3623 switch (eventType) {
3624 case TOF:
3625 temp = this->events->at(i).pulseTime();
3626 break;
3627 case WEIGHTED:
3628 temp = this->weightedEvents->at(i).pulseTime();
3629 break;
3630 case WEIGHTED_NOTIME:
3631 temp = this->weightedEventsNoTime->at(i).pulseTime();
3632 break;
3633 }
3634 if (temp > tMax)
3635 tMax = temp;
3636 if (temp < tMin)
3637 tMin = temp;
3638 }
3639}
3640
3641DateAndTime EventList::getTimeAtSampleMax(const double &tofFactor, const double &tofOffset) const {
3642 // set up as the minimum available date time.
3643 DateAndTime tMax = DateAndTime::minimum();
3644
3645 // no events is a soft error
3646 if (this->empty())
3647 return tMax;
3648
3649 // when events are ordered by time at sample just need the first value
3650 if (this->order == TIMEATSAMPLE_SORT) {
3651 switch (eventType) {
3652 case TOF:
3653 return calculateCorrectedFullTime(this->events->back(), tofFactor, tofOffset);
3654 case WEIGHTED:
3655 return calculateCorrectedFullTime(this->weightedEvents->back(), tofFactor, tofOffset);
3656 case WEIGHTED_NOTIME:
3657 return calculateCorrectedFullTime(this->weightedEventsNoTime->back(), tofFactor, tofOffset);
3658 }
3659 }
3660
3661 // now we are stuck with a linear search
3662 size_t numEvents = this->getNumberEvents();
3663 DateAndTime temp = tMax; // start with the smallest possible value
3664 for (size_t i = 0; i < numEvents; i++) {
3665 switch (eventType) {
3666 case TOF:
3667 temp = calculateCorrectedFullTime(this->events->at(i), tofFactor, tofOffset);
3668 break;
3669 case WEIGHTED:
3670 temp = calculateCorrectedFullTime(this->weightedEvents->at(i), tofFactor, tofOffset);
3671 break;
3672 case WEIGHTED_NOTIME:
3673 temp = calculateCorrectedFullTime(this->weightedEventsNoTime->at(i), tofFactor, tofOffset);
3674 break;
3675 }
3676 if (temp > tMax)
3677 tMax = temp;
3678 }
3679 return tMax;
3680}
3681
3682DateAndTime EventList::getTimeAtSampleMin(const double &tofFactor, const double &tofOffset) const {
3683 // set up as the minimum available date time.
3684 DateAndTime tMin = DateAndTime::maximum();
3685
3686 // no events is a soft error
3687 if (this->empty())
3688 return tMin;
3689
3690 // when events are ordered by time at sample just need the first value
3691 if (this->order == TIMEATSAMPLE_SORT) {
3692 switch (eventType) {
3693 case TOF:
3694 return calculateCorrectedFullTime(this->events->front(), tofFactor, tofOffset);
3695 case WEIGHTED:
3696 return calculateCorrectedFullTime(this->weightedEvents->front(), tofFactor, tofOffset);
3697 case WEIGHTED_NOTIME:
3698 return calculateCorrectedFullTime(this->weightedEventsNoTime->front(), tofFactor, tofOffset);
3699 }
3700 }
3701
3702 // now we are stuck with a linear search
3703 size_t numEvents = this->getNumberEvents();
3704 DateAndTime temp = tMin; // start with the smallest possible value
3705 for (size_t i = 0; i < numEvents; i++) {
3706 switch (eventType) {
3707 case TOF:
3708 temp = calculateCorrectedFullTime(this->events->at(i), tofFactor, tofOffset);
3709 break;
3710 case WEIGHTED:
3711 temp = calculateCorrectedFullTime(this->weightedEvents->at(i), tofFactor, tofOffset);
3712 break;
3713 case WEIGHTED_NOTIME:
3714 temp = calculateCorrectedFullTime(this->weightedEventsNoTime->at(i), tofFactor, tofOffset);
3715 break;
3716 }
3717 if (temp < tMin)
3718 tMin = temp;
3719 }
3720 return tMin;
3721}
3722
3723// --------------------------------------------------------------------------
3729template <class T> void EventList::setTofsHelper(std::vector<T> &events, const std::vector<double> &tofs) {
3730 if (tofs.empty())
3731 return;
3732
3733 size_t x_size = tofs.size();
3734 if (events.size() != x_size)
3735 return; // should this throw an exception?
3736
3737 for (size_t i = 0; i < x_size; ++i)
3738 events[i].m_tof = tofs[i];
3739}
3740
3741// --------------------------------------------------------------------------
3748 this->order = UNSORTED;
3749
3750 // Convert the list
3751 switch (eventType) {
3752 case TOF:
3753 this->setTofsHelper(*this->events, tofs);
3754 break;
3755 case WEIGHTED:
3756 this->setTofsHelper(*this->weightedEvents, tofs);
3757 break;
3758 case WEIGHTED_NOTIME:
3759 this->setTofsHelper(*this->weightedEventsNoTime, tofs);
3760 break;
3761 }
3762}
3763
3764// ==============================================================================================
3765// ----------- MULTIPLY AND DIVIDE ---------------------------------------
3766// ==============================================================================================
3767
3768//------------------------------------------------------------------------------------------------
3776template <class T> void EventList::multiplyHelper(std::vector<T> &events, const double value, const double error) {
3777 // Square of the value
3778 const double valueSquared = value * value;
3779
3780 auto itev_end = events.end();
3781
3782 if (error == 0) {
3783 // Error-less calculation
3784 for (auto itev = events.begin(); itev != itev_end; itev++) {
3785 itev->m_errorSquared = static_cast<float>(itev->m_errorSquared * valueSquared);
3786 itev->m_weight *= static_cast<float>(value);
3787 }
3788 } else {
3789 // Carry the scalar error
3790 const double errorSquared = error * error; // Square of the value's error
3791 for (auto itev = events.begin(); itev != itev_end; itev++) {
3792 itev->m_errorSquared =
3793 static_cast<float>(itev->m_errorSquared * valueSquared + errorSquared * itev->m_weight * itev->m_weight);
3794 itev->m_weight *= static_cast<float>(value);
3795 }
3796 }
3797}
3798
3799//------------------------------------------------------------------------------------------------
3812 this->multiply(value);
3813 return *this;
3814}
3815
3816//------------------------------------------------------------------------------------------------
3845void EventList::multiply(const double value, const double error) {
3846 // Do nothing if multiplying by exactly one and there is no error
3847 if ((value == 1.0) && (error == 0.0))
3848 return;
3849
3850 switch (eventType) {
3851 case TOF:
3852 // Switch to weights if needed.
3853 this->switchTo(WEIGHTED);
3854 // Fall through
3855
3856 case WEIGHTED:
3857 multiplyHelper(*this->weightedEvents, value, error);
3858 break;
3859
3860 case WEIGHTED_NOTIME:
3862 break;
3863 }
3864}
3865
3866//------------------------------------------------------------------------------------------------
3875template <class T>
3876void EventList::multiplyHistogramHelper(std::vector<T> &events, std::span<double const> X, std::span<double const> Y,
3877 std::span<double const> E) {
3878 // Validate inputs
3879 if ((X.size() < 2) || (Y.size() != E.size()) || (X.size() != 1 + Y.size())) {
3880 std::stringstream msg;
3881 msg << "EventList::multiply() was given invalid size or "
3882 "inconsistent histogram arrays: X["
3883 << X.size() << "] "
3884 << "Y[" << Y.size() << " E[" << E.size() << "]";
3885 throw std::invalid_argument(msg.str());
3886 }
3887
3888 size_t x_size = X.size();
3889
3890 // Iterate through all events (sorted by tof)
3891 auto itev = findFirstEvent(events, T(X[0]));
3892 auto itev_end = events.end();
3893 // The above can still take you to end() if no events above X[0], so check
3894 // again.
3895 if (itev == itev_end)
3896 return;
3897
3898 // Find the first bin
3899 size_t bin = 0;
3900
3901 // Multiplier values
3902 double value;
3903 double error;
3904 double valueSquared;
3905 double errorSquared;
3906
3907 // If the tof is greater the first bin boundary, so we need to find the first
3908 // bin
3909 double tof = itev->tof();
3910 while (bin < x_size - 1) {
3911 // Within range?
3912 if ((tof >= X[bin]) && (tof < X[bin + 1]))
3913 break; // Stop increasing bin
3914 ++bin;
3915 }
3916
3917 // New bin! Find what you are multiplying!
3918 value = Y[bin];
3919 error = E[bin];
3920 valueSquared = value * value;
3921 errorSquared = error * error;
3922
3923 // Keep going through all the events
3924 while ((itev != itev_end) && (bin < x_size - 1)) {
3925 tof = itev->tof();
3926 while (bin < x_size - 1) {
3927 // Event is Within range?
3928 if ((tof >= X[bin]) && (tof < X[bin + 1])) {
3929 // Process this event. Multiply and calculate error.
3930 itev->m_errorSquared =
3931 static_cast<float>(itev->m_errorSquared * valueSquared + errorSquared * itev->m_weight * itev->m_weight);
3932 itev->m_weight *= static_cast<float>(value);
3933 break; // out of the bin-searching-while-loop
3934 }
3935 ++bin;
3936 if (bin >= x_size - 1)
3937 break;
3938
3939 // New bin! Find what you are multiplying!
3940 value = Y[bin];
3941 error = E[bin];
3942 valueSquared = value * value;
3943 errorSquared = error * error;
3944 }
3945 ++itev;
3946 }
3947}
3948
3949//------------------------------------------------------------------------------------------------
3970void EventList::multiply(std::span<double const> X, std::span<double const> Y, std::span<double const> E) {
3971 switch (eventType) {
3972 case TOF:
3973 // Switch to weights if needed.
3974 this->switchTo(WEIGHTED);
3975 // Fall through
3976
3977 case WEIGHTED:
3978 // Sorting by tof is necessary for the algorithm
3979 this->sortTof();
3981 break;
3982
3983 case WEIGHTED_NOTIME:
3984 // Sorting by tof is necessary for the algorithm
3985 this->sortTof();
3987 break;
3988 }
3989}
3990
3991//------------------------------------------------------------------------------------------------
4000template <class T>
4001void EventList::divideHistogramHelper(std::vector<T> &events, std::span<double const> X, std::span<double const> Y,
4002 std::span<double const> E) {
4003 // Validate inputs
4004 if ((X.size() < 2) || (Y.size() != E.size()) || (X.size() != 1 + Y.size())) {
4005 std::stringstream msg;
4006 msg << "EventList::divide() was given invalid size or "
4007 "inconsistent histogram arrays: X["
4008 << X.size() << "] "
4009 << "Y[" << Y.size() << " E[" << E.size() << "]";
4010 throw std::invalid_argument(msg.str());
4011 }
4012
4013 size_t x_size = X.size();
4014
4015 // Iterate through all events (sorted by tof)
4016 auto itev = findFirstEvent(events, T(X[0]));
4017 auto itev_end = events.end();
4018 // The above can still take you to end() if no events above X[0], so check
4019 // again.
4020 if (itev == itev_end)
4021 return;
4022
4023 // Find the first bin
4024 size_t bin = 0;
4025
4026 // Multiplier values
4027 double value;
4028 double error;
4029 double valError_over_value_squared;
4030
4031 // If the tof is greater the first bin boundary, so we need to find the first
4032 // bin
4033 double tof = itev->tof();
4034 while (bin < x_size - 1) {
4035 // Within range?
4036 if ((tof >= X[bin]) && (tof < X[bin + 1]))
4037 break; // Stop increasing bin
4038 ++bin;
4039 }
4040
4041 // New bin! Find what you are multiplying!
4042 value = Y[bin];
4043 error = E[bin];
4044
4045 // --- Division case ---
4046 if (value == 0) {
4047 value = std::numeric_limits<float>::quiet_NaN(); // Avoid divide by zero
4048 valError_over_value_squared = 0;
4049 } else
4050 valError_over_value_squared = error * error / (value * value);
4051
4052 // Keep going through all the events
4053 while ((itev != events.end()) && (bin < x_size - 1)) {
4054 tof = itev->tof();
4055 while (bin < x_size - 1) {
4056 // Event is Within range?
4057 if ((tof >= X[bin]) && (tof < X[bin + 1])) {
4058 // Process this event. Divide and calculate error.
4059 double newWeight = itev->m_weight / value;
4060 itev->m_errorSquared = static_cast<float>(
4061 newWeight * newWeight *
4062 ((itev->m_errorSquared / (itev->m_weight * itev->m_weight)) + valError_over_value_squared));
4063 itev->m_weight = static_cast<float>(newWeight);
4064 break; // out of the bin-searching-while-loop
4065 }
4066 ++bin;
4067 if (bin >= x_size - 1)
4068 break;
4069
4070 // New bin! Find what you are multiplying!
4071 value = Y[bin];
4072 error = E[bin];
4073
4074 // --- Division case ---
4075 if (value == 0) {
4076 value = std::numeric_limits<float>::quiet_NaN(); // Avoid divide by zero
4077 valError_over_value_squared = 0;
4078 } else
4079 valError_over_value_squared = error * error / (value * value);
4080 }
4081 ++itev;
4082 }
4083}
4084
4085//------------------------------------------------------------------------------------------------
4107void EventList::divide(std::span<double const> X, std::span<double const> Y, std::span<double const> E) {
4108 switch (eventType) {
4109 case TOF:
4110 // Switch to weights if needed.
4111 this->switchTo(WEIGHTED);
4112 // Fall through
4113
4114 case WEIGHTED:
4115 // Sorting by tof is necessary for the algorithm
4116 this->sortTof();
4117 divideHistogramHelper(*this->weightedEvents, X, Y, E);
4118 break;
4119
4120 case WEIGHTED_NOTIME:
4121 // Sorting by tof is necessary for the algorithm
4122 this->sortTof();
4124 break;
4125 }
4126}
4127
4128//------------------------------------------------------------------------------------------------
4138 if (value == 0.0)
4139 throw std::invalid_argument("EventList::divide() called with value of 0.0. Cannot divide by zero.");
4140 this->multiply(1.0 / value, 0.0);
4141 return *this;
4142}
4143
4144//------------------------------------------------------------------------------------------------
4154void EventList::divide(const double value, const double error) {
4155 if (value == 0.0)
4156 throw std::invalid_argument("EventList::divide() called with value of 0.0. Cannot divide by zero.");
4157 // Do nothing if dividing by exactly 1.0, no error
4158 else if (value == 1.0 && error == 0.0)
4159 return;
4160
4161 // We'll multiply by 1/value
4162 double invValue = 1.0 / value;
4163 // Relative error remains the same
4164 double invError = (error / value) * invValue;
4165
4166 this->multiply(invValue, invError);
4167}
4168
4169// ==============================================================================================
4170// ----------- SPLITTING AND FILTERING ---------------------------------------
4171// ==============================================================================================
4172//------------------------------------------------------------------------------------------------
4182void EventList::filterByPulseTime(Types::Core::DateAndTime start, Types::Core::DateAndTime stop,
4183 EventList &output) const {
4184 if (this == &output) {
4185 throw std::invalid_argument("In-place filtering is not allowed");
4186 }
4187
4188 // Start by sorting the event list by pulse time.
4189 this->sortPulseTime();
4190 // Clear the output
4191 output.clear();
4192 // Has to match the given type
4193 output.switchTo(eventType);
4194 output.setDetectorIDs(this->getDetectorIDs());
4195 output.setHistogram(m_histogram);
4196 output.setSortOrder(this->order);
4197
4198 // Iterate through all events (sorted by pulse time)
4199 switch (eventType) {
4200 case TOF:
4201 filterByPulseTimeHelper(*this->events, start, stop, *output.events);
4202 break;
4203 case WEIGHTED:
4204 filterByPulseTimeHelper(*this->weightedEvents, start, stop, *output.weightedEvents);
4205 break;
4206 case WEIGHTED_NOTIME:
4207 throw std::runtime_error("EventList::filterByPulseTime() called on an "
4208 "EventList that no longer has time information.");
4209 break;
4210 }
4211}
4212
4225void EventList::filterByPulseTime(Kernel::TimeROI const *timeRoi, EventList *output) const {
4226
4227 this->sortPulseTime();
4228 // Clear the output
4229
4230 output->clear();
4231 output->setDetectorIDs(this->getDetectorIDs());
4232 output->setHistogram(m_histogram);
4233 // Has to match the given type
4234 output->switchTo(eventType);
4235
4236 if ((timeRoi == nullptr) || (timeRoi->useAll())) {
4237 throw std::invalid_argument("TimeROI can not use all time");
4238 }
4239 const auto &intervals = timeRoi->toTimeIntervals();
4240 if (intervals.empty())
4241 return;
4242
4243 switch (eventType) {
4244 case TOF:
4245 filterByTimeROIHelper(*this->events, intervals, output);
4246 break;
4247 case WEIGHTED:
4248 filterByTimeROIHelper(*this->weightedEvents, intervals, output);
4249 break;
4250 case WEIGHTED_NOTIME:
4251 throw std::runtime_error("EventList::filterByPulseTime() called on an "
4252 "EventList that no longer has time information.");
4253 break;
4254 }
4255}
4256
4264template <class T>
4265void EventList::filterByPulseTimeHelper(std::vector<T> &events, DateAndTime start, DateAndTime stop,
4266 std::vector<T> &output) {
4267 std::copy_if(events.begin(), events.end(), std::back_inserter(output),
4268 [start, stop](const T &t) { return (t.m_pulsetime >= start) && (t.m_pulsetime < stop); });
4269}
4270
4277template <class T>
4278void EventList::filterByTimeROIHelper(std::vector<T> &events, const std::vector<Kernel::TimeInterval> &intervals,
4279 EventList *output) {
4280 // Iterate through the splitter at the same time
4281 auto itspl = intervals.cbegin();
4282 auto itspl_end = intervals.cend();
4283 // Iterate through all events (sorted by tof)
4284 auto itev = events.cbegin();
4285 auto itev_end = events.cend();
4286
4287 // This is the time of the first section. Anything before is thrown out.
4288 while (itspl != itspl_end) {
4289 // Get the splitting interval times and destination
4290 DateAndTime start = itspl->start();
4291 DateAndTime stop = itspl->stop();
4292 // Skip the events before the start of the time
4293 while ((itev != itev_end) && (itev->m_pulsetime < start))
4294 itev++;
4295
4296 // Go through all the events that are in the interval (if any)
4297 while ((itev != itev_end) && (itev->m_pulsetime < stop)) {
4298 // Copy the event into another
4299 const T eventCopy(*itev);
4300 output->addEventQuickly(eventCopy);
4301 ++itev;
4302 }
4303
4304 // Go to the next interval
4305 ++itspl;
4306 // But if we reached the end, then we are done.
4307 if (itspl == itspl_end)
4308 break;
4309
4310 // No need to keep looping through the filter if we are out of events
4311 if (itev == itev_end)
4312 break;
4313 }
4314 // Done!
4315}
4316
4322 if (timeRoi == nullptr) {
4323 throw std::runtime_error("TimeROI can not be a nullptr\n");
4324 }
4325 if (timeRoi->useAll()) {
4326 throw std::invalid_argument("TimeROI can not be empty\n");
4327 }
4328 // Start by sorting the event list by pulse time.
4329 this->sortPulseTime();
4330
4331 // Iterate through all events (sorted by pulse time)
4332 switch (eventType) {
4333 case TOF:
4334 filterInPlaceHelper(timeRoi, *this->events);
4335 break;
4336 case WEIGHTED:
4337 filterInPlaceHelper(timeRoi, *this->weightedEvents);
4338 break;
4339 case WEIGHTED_NOTIME:
4340 throw std::runtime_error("EventList::filterInPlace() called on an "
4341 "EventList that no longer has time information.");
4342 break;
4343 }
4344}
4345
4372template <class T>
4373void EventList::filterInPlaceHelper(Kernel::TimeROI const *timeRoi, typename std::vector<T> &events) {
4374
4375 const auto splitter = timeRoi->toTimeIntervals();
4376 // Iterate through the splitter at the same time
4377 auto itspl = splitter.cbegin();
4378 auto itspl_end = splitter.cend();
4379 DateAndTime start, stop;
4380
4381 // Iterate for the input
4382 auto itev = events.begin();
4383 auto itev_end = events.end();
4384
4385 // Iterator for the outputted list; will follow the input except when events
4386 // are dropped.
4387 auto itOut = events.begin();
4388
4389 // This is the time of the first section. Anything before is thrown out.
4390 while (itspl != itspl_end) {
4391 // Get the splitting interval times and destination
4392 start = itspl->start();
4393 stop = itspl->stop();
4394 // Skip the events before the start of the time
4395 while ((itev != itev_end) && (itev->m_pulsetime < start))
4396 itev++;
4397
4398 // Are we aligned in the input vs output?
4399 bool copyingInPlace = (itOut == itev);
4400 if (copyingInPlace) {
4401 while ((itev != itev_end) && (itev->m_pulsetime < stop))
4402 ++itev;
4403 // Make sure the iterators still match
4404 itOut = itev;
4405 } else {
4406 // Go through all the events that are in the interval (if any)
4407 while ((itev != itev_end) && (itev->m_pulsetime < stop)) {
4408 *itOut = *itev;
4409 ++itOut;
4410 ++itev;
4411 }
4412 }
4413
4414 // Go to the next interval
4415 ++itspl;
4416 // But if we reached the end, then we are done.
4417 if (itspl == itspl_end)
4418 break;
4419
4420 // No need to keep looping through the filter if we are out of events
4421 if (itev == itev_end)
4422 break;
4423
4424 } // Looping through entries in the splitter vector
4425
4426 // Ok, now resize the event list to reflect the fact that it (probably) shrank
4427 events.resize(std::size_t(std::distance(events.begin(), itOut)));
4428}
4429
4434void EventList::initializePartials(std::map<int, EventList *> partials) const {
4435
4436 // collect the state from events which is to be transferred to the partials
4437 bool removeDetIDs{true};
4438 const auto histogramLocal = this->getHistogram();
4439 const auto eventTypeLocal = this->getEventType();
4440
4441 // lambda expression initializing one partial
4442 auto initPartial = [&](EventList *partial) {
4443 partial->clear(removeDetIDs);
4444 partial->copyInfoFrom(*this);
4445 partial->setHistogram(histogramLocal);
4446 partial->switchTo(eventTypeLocal);
4447 };
4448
4449 // iterate over the partials
4450 std::for_each(partials.cbegin(), partials.cend(),
4451 [&](const std::pair<int, EventList *> &pair) { initPartial(pair.second); });
4452}
4453
4463void getEventsFrom(EventList &el, std::vector<TofEvent> *&events) { events = &el.getEvents(); }
4464void getEventsFrom(const EventList &el, std::vector<TofEvent> const *&events) { events = &el.getEvents(); }
4465
4475void getEventsFrom(EventList &el, std::vector<WeightedEvent> *&events) { events = &el.getWeightedEvents(); }
4476void getEventsFrom(const EventList &el, std::vector<WeightedEvent> const *&events) { events = &el.getWeightedEvents(); }
4477
4487void getEventsFrom(EventList &el, std::vector<WeightedEventNoTime> *&events) { events = &el.getWeightedEventsNoTime(); }
4488void getEventsFrom(const EventList &el, std::vector<WeightedEventNoTime> const *&events) {
4489 events = &el.getWeightedEventsNoTime();
4490}
4491
4499template <class T>
4500void EventList::convertUnitsViaTofHelper(typename std::vector<T> &events, Mantid::Kernel::Unit const *fromUnit,
4501 Mantid::Kernel::Unit const *toUnit) {
4502 for (auto &itev : events) {
4503 // Conver to TOF
4504 const double tof = fromUnit->singleToTOF(itev.m_tof);
4505 // And back from TOF to whatever
4506 itev.m_tof = toUnit->singleFromTOF(tof);
4507 }
4508}
4509
4510//--------------------------------------------------------------------------
4519 // Check for initialized
4520 if (!fromUnit || !toUnit)
4521 throw std::runtime_error("EventList::convertUnitsViaTof(): one of the units is NULL!");
4522 if (!fromUnit->isInitialized())
4523 throw std::runtime_error("EventList::convertUnitsViaTof(): fromUnit is not initialized!");
4524 if (!toUnit->isInitialized())
4525 throw std::runtime_error("EventList::convertUnitsViaTof(): toUnit is not initialized!");
4526
4527 switch (eventType) {
4528 case TOF:
4529 convertUnitsViaTofHelper(*this->events, fromUnit, toUnit);
4530 break;
4531 case WEIGHTED:
4532 convertUnitsViaTofHelper(*this->weightedEvents, fromUnit, toUnit);
4533 break;
4534 case WEIGHTED_NOTIME:
4535 convertUnitsViaTofHelper(*this->weightedEventsNoTime, fromUnit, toUnit);
4536 break;
4537 }
4538}
4539
4540//--------------------------------------------------------------------------
4547template <class T>
4548void EventList::convertUnitsQuicklyHelper(typename std::vector<T> &events, const double &factor, const double &power) {
4549 for (auto &event : events) {
4550 // Output unit = factor * (input) ^ power
4551 event.m_tof = factor * std::pow(event.m_tof, power);
4552 }
4553}
4554
4555//--------------------------------------------------------------------------
4561void EventList::convertUnitsQuickly(const double &factor, const double &power) {
4562 switch (eventType) {
4563 case TOF:
4564 convertUnitsQuicklyHelper(*this->events, factor, power);
4565 break;
4566 case WEIGHTED:
4567 convertUnitsQuicklyHelper(*this->weightedEvents, factor, power);
4568 break;
4569 case WEIGHTED_NOTIME:
4570 convertUnitsQuicklyHelper(*this->weightedEventsNoTime, factor, power);
4571 break;
4572 }
4573}
4574
4575HistogramData::Histogram &EventList::mutableHistogramRef() {
4576 if (mru)
4577 mru->deleteIndex(this);
4578 return m_histogram;
4579}
4580
4581void EventList::checkAndSanitizeHistogram(HistogramData::Histogram &histogram) {
4582 if (histogram.xMode() != HistogramData::Histogram::XMode::BinEdges)
4583 throw std::runtime_error("EventList: setting histogram with storage mode "
4584 "other than BinEdges is not possible");
4585 if (histogram.sharedY() || histogram.sharedE())
4586 throw std::runtime_error("EventList: setting histogram data with non-null "
4587 "Y or E data is not possible");
4588 // Avoid flushing of YMode: we only change X but YMode depends on events.
4589 if (histogram.yMode() == HistogramData::Histogram::YMode::Uninitialized)
4590 histogram.setYMode(m_histogram.yMode());
4591 if (histogram.yMode() != m_histogram.yMode())
4592 throw std::runtime_error("EventList: setting histogram data with different "
4593 "YMode is not possible");
4594}
4595
4597 throw std::runtime_error("EventList: setting Points as X data is not "
4598 "possible, only BinEdges are supported");
4599}
4600
4602 throw std::runtime_error("EventList: Cannot set Y or E data, these data are "
4603 "generated automatically based on the events");
4604}
4605
4606} // namespace Mantid::DataObjects
gsl_vector * tmp
std::vector< float > m_tof
sum of all time-of-flight within the bin
const std::vector< double > & rhs
const double m_tofFactor
Definition EventList.cpp:70
const double m_tofShift
Definition EventList.cpp:71
double value
The value of the point.
Definition FitMW.cpp:51
double error
double left
double right
#define fabs(x)
Definition Matrix.cpp:22
int count
counter
Definition Matrix.cpp:37
#define PARALLEL_THREAD_NUMBER
double tolerance
#define MSVC_DIAG_ON(x)
#define MSVC_DIAG_OFF(x)
#define GNU_DIAG_ON(x)
#define GNU_DIAG_OFF(x)
This is a collection of macros for turning compiler warnings off in a controlled manner.
IEventList : Interface to Mantid::DataObjects::EventList class, used to expose to PythonAPI.
Definition IEventList.h:27
A "spectrum" is an object that holds the data for a particular spectrum, in particular:
Definition ISpectrum.h:38
void setHistogram(T &&...data)
Sets the Histogram associated with this spectrum.
Definition ISpectrum.h:110
void addDetectorIDs(const std::set< detid_t > &detIDs)
Add a set of detector IDs to the set of detector IDs.
Definition ISpectrum.cpp:62
const HistogramData::HistogramX & x() const
Definition ISpectrum.h:185
virtual const HistogramData::HistogramE & e() const
Definition ISpectrum.h:187
void setDetectorIDs(const std::set< detid_t > &detIDs)
Set the detector IDs to be the set given.
Definition ISpectrum.cpp:94
void clearDetectorIDs()
Clear the detector IDs set.
void copyInfoFrom(const ISpectrum &other)
Copy spectrum number and detector IDs, but not X vector, from another ISpectrum.
Definition ISpectrum.cpp:24
const HistogramData::HistogramDx & dx() const
Definition ISpectrum.h:188
Kernel::cow_ptr< HistogramData::HistogramX > sharedX() const
Definition ISpectrum.h:199
virtual const HistogramData::HistogramY & y() const
Definition ISpectrum.h:186
const std::set< detid_t > & getDetectorIDs() const
Get a const reference to the detector IDs set.
virtual void copyDataInto(DataObjects::EventList &) const
Override in child classes for polymorphic copying of data.
void setSharedX(const Kernel::cow_ptr< HistogramData::HistogramX > &x) &
Definition ISpectrum.h:203
HistogramData::HistogramX & mutableX() &
Definition ISpectrum.h:189
A class for holding :
Definition EventList.h:57
void sortTimeAtSample(const double &tofFactor, const double &tofShift, bool forceResort=false) const
Sort events by time at sample.
size_t getMemorySize() const override
Memory used by this event list.
static void getWeightErrorsHelper(const std::vector< T > &events, std::vector< double > &weightErrors)
Get the weight error member of all events in a list.
void addPulsetimes(const std::vector< double > &seconds) override
Add an offset to the pulsetime (wall-clock time) of each event in the list.
Mantid::Types::Core::DateAndTime getTimeAtSampleMax(const double &tofFactor, const double &tofOffset) const override
Get the maximum time at sample.
std::vector< Types::Core::DateAndTime > getPulseTimes() const override
Get the pulse times of each event in this EventList.
static std::optional< size_t > findLinearBin(std::span< double const > X, const double tof, const double divisor, const double offset, const bool findExact=true)
Find the bin which this TOF value falls in with linear binning, assumes TOF is in range of X.
void setTofs(const MantidVec &tofs) override
Set a list of TOFs to the current event list.
void maskTof(const double tofMin, const double tofMax) override
Mask out events that have a tof between tofMin and tofMax (inclusively).
EventList(const Mantid::API::EventType event_type=Mantid::API::EventType::TOF)
Constructor (empty)
void multiply(const double value, const double error=0.0) override
Multiply the weights in this event list by a scalar variable with an error; though the error can be 0...
void checkWorksWithPoints() const override
HistogramData::CountStandardDeviations countStandardDeviations() const override
void switchToWeightedEvents()
Switch the EventList to use WeightedEvents instead of TofEvent.
static size_t findExactBin(std::span< double const > X, const double tof, const size_t n_bin)
Find the exact bin which a TOF falls in starting from the provided estimated one.
EventList & operator=(const EventList &)
Copy into this event list from another.
void compressFatEvents(const double tolerance, const Types::Core::DateAndTime &timeStart, const double seconds, EventList *destination)
HistogramData::Histogram & mutableHistogramRef() override
void convertUnitsViaTofHelper(typename std::vector< T > &events, Mantid::Kernel::Unit const *fromUnit, Mantid::Kernel::Unit const *toUnit)
Helper function for the conversion to TOF.
static void histogramForWeightsHelper(const std::vector< T > &events, std::span< double const > X, MantidVec &Y, MantidVec &E)
Generates both the Y and E (error) histograms for an EventList with WeightedEvents.
HistogramData::Counts counts() const override
std::vector< double > getWeights() const override
Return the list of event weight values.
void convertUnitsQuickly(const double &factor, const double &power)
Convert the event's TOF (x) value according to a simple output = a * (input^b) relationship.
void compressEvents(double tolerance, EventList *destination)
Compress the event list by grouping events with the same TOF (within a given tolerance).
double getTofMax() const override
static void filterByTimeROIHelper(std::vector< T > &events, const std::vector< Kernel::TimeInterval > &intervals, EventList *output)
Filter a vector of events into another based on TimeROI.
virtual size_t histogram_size() const
Return the size of the histogram data.
HistogramData::Histogram histogram() const override
Returns the Histogram associated with this spectrum.
~EventList() override
Destructor.
std::vector< Types::Event::TofEvent > & getEvents()
Return the list of TofEvents contained.
HistogramData::FrequencyStandardDeviations frequencyStandardDeviations() const override
void setSortOrder(const EventSortType order) const
Manually set the event list sort order value.
static void processWeightedEvents(const std::vector< T > &events, std::vector< WeightedEventNoTime > &out, const std::shared_ptr< std::vector< double > > histogram_bin_edges, struct FindBin findBin)
HistogramData::Histogram m_histogram
Histogram object holding the histogram data. Currently only X.
Definition EventList.h:346
void generateCountsHistogramPulseTime(const double &xMin, const double &xMax, MantidVec &Y, const double TofMin=std::numeric_limits< double >::lowest(), const double TofMax=std::numeric_limits< double >::max()) const
With respect to PulseTime fill a histogram given equal histogram bins.
double getTofMin() const override
static void compressEventsHelper(const std::vector< T > &events, std::vector< WeightedEventNoTime > &out, double tolerance)
Compress the event list by grouping events with the same TOF.
Mantid::API::EventType eventType
What type of event is in our list.
Definition EventList.h:358
EventSortType getSortType() const
Return the type of sorting used in this event list.
void switchToWeightedEventsNoTime()
Switch the EventList to use WeightedEventNoTime's instead of TofEvent.
static void setTofsHelper(std::vector< T > &events, const std::vector< double > &tofs)
Set a list of TOFs to the current event list.
void addTof(const double offset) override
Add an offset to the TOF of each event in the list.
bool equals(const EventList &rhs, const double tolTof, const double tolWeight, const int64_t tolPulse) const
Kernel::cow_ptr< HistogramData::HistogramE > sharedE() const override
static std::size_t maskConditionHelper(std::vector< T > &events, const std::vector< bool > &mask)
Mask out events by the condition vector.
EventList & operator/=(const double value)
Operator to divide the weights in this EventList by an error-less scalar.
void checkAndSanitizeHistogram(HistogramData::Histogram &histogram) override
void copyDataInto(EventList &sink) const override
Used by copyDataFrom for dynamic dispatch for its source.
const HistogramData::HistogramY & y() const override
HistogramData::Frequencies frequencies() const override
void filterInPlace(const Kernel::TimeROI *timeRoi)
Use a SplittingIntervalVec to filter the event list in place.
std::size_t getNumberEvents() const override
Return the number of events in the list.
Mantid::API::EventType getEventType() const override
Return the type of Event vector contained within.
const MantidVec & readDx() const override
Deprecated, use dx() instead.
void scaleTof(const double factor) override
Convert the units in the TofEvent's m_tof field to some other value, by scaling by a multiplier.
void getPulseTimeMinMax(Mantid::Types::Core::DateAndTime &tMin, Mantid::Types::Core::DateAndTime &tM) const
static void multiplyHelper(std::vector< T > &events, const double value, const double error=0.0)
Helper method for multiplying an event list by a scalar value with/without error.
HistogramData::FrequencyVariances frequencyVariances() const override
std::vector< WeightedEventNoTime > & getWeightedEventsNoTime()
Return the list of WeightedEvent contained.
void setMRU(EventWorkspaceMRU *newMRU)
Sets the MRU list for this event list.
EventWorkspaceMRU * mru
MRU lists of the parent EventWorkspace.
Definition EventList.h:364
void convertUnitsViaTof(Mantid::Kernel::Unit const *fromUnit, Mantid::Kernel::Unit const *toUnit)
Converts the X units in each event by going through TOF.
static void integrateHelper(std::vector< T > &events, const double minX, const double maxX, const bool entireRange, double &sum, double &error)
Integrate the events between a range of X values, or all events.
Mantid::Types::Core::DateAndTime getPulseTimeMax() const override
Kernel::cow_ptr< HistogramData::HistogramX > ptrX() const override
Deprecated, use sharedX() instead. Returns a pointer to the x data.
void generateHistogram(std::span< double const > X, MantidVec &Y, MantidVec &E, bool skipError=false) const override
Generates both the Y and E (error) histograms w.r.t TOF for an EventList with or without WeightedEven...
static void createWeightedEvents(std::vector< WeightedEventNoTime > &out, const std::vector< double > &tof, const std::vector< T > &weight, const std::vector< T > &error)
MantidVec & dataX() override
Deprecated, use mutableX() instead.
void generateCountsHistogram(std::span< double const > X, MantidVec &Y) const
Fill a histogram given specified histogram bounds.
void generateHistogramPulseTime(std::span< double const > X, MantidVec &Y, MantidVec &E, bool skipError=false) const override
Generates both the Y and E (error) histograms w.r.t Pulse Time for an EventList with or without Weigh...
void clearUnused()
Clear any unused event lists (the ones that do not match the currently used type).
MantidVec & dataDx() override
Deprecated, use mutableDx() instead.
bool operator==(const EventList &rhs) const
Equality operator between EventList's.
void generateCountsHistogramTimeAtSample(std::span< double const > X, MantidVec &Y, const double &tofFactor, const double &tofOffset) const
With respect to Time at Sample, fill a histogram given specified histogram bounds.
EventSortType order
Last sorting order.
Definition EventList.h:361
static void divideHistogramHelper(std::vector< T > &events, std::span< double const > X, std::span< double const > Y, std::span< double const > E)
Helper method for dividing an event list by a histogram with error.
void convertTofHelper(std::vector< T > &events, const std::function< double(double)> &func)
void generateHistogramTimeAtSample(std::span< double const > X, MantidVec &Y, MantidVec &E, const double &tofFactor, const double &tofOffset, bool skipError=false) const override
Generates both the Y and E (error) histograms w.r.t Time at sample position.
void clearData() override
Mask the spectrum to this value. Removes all events.
EventList & operator+=(const Types::Event::TofEvent &event)
Append an event to the histogram.
static void minusHelper(std::vector< T1 > &events, const std::vector< T2 > &more_events)
SUBTRACT another EventList from this event list.
void addPulsetimesHelper(std::vector< T > &events, const std::vector< double > &seconds)
Add an offset per event to the pulsetime (wall-clock time) of each event in the list.
static std::optional< size_t > findLogBin(std::span< double const > X, const double tof, const double divisor, const double offset, const bool findExact=true)
Find the bin which this TOF value falls in with log binning, assumes TOF is in range of X.
void addPulsetime(const double seconds) override
Add an offset to the pulsetime (wall-clock time) of each event in the list.
HistogramData::Histogram getHistogram() const
Returns a copy of the Histogram associated with this spectrum.
void reserve(size_t num) override
Reserve a certain number of entries in event list of the specified eventType.
void setX(const Kernel::cow_ptr< HistogramData::HistogramX > &X) override
Deprecated, use setSharedX() instead.
std::vector< double > getWeightErrors() const override
Return the list of event weight error values.
void sortTof() const
Sort events by TOF in one thread.
HistogramData::CountVariances countVariances() const override
MantidVec & dataE() override
Deprecated, use mutableE() instead.
Definition EventList.h:199
static void compressFatEventsHelper(const std::vector< T > &events, std::vector< WeightedEvent > &out, const double tolerance, const Mantid::Types::Core::DateAndTime &timeStart, const double seconds)
MantidVec & dataY() override
Deprecated, use mutableY() instead.
Definition EventList.h:193
Mantid::Types::Core::DateAndTime getTimeAtSampleMin(const double &tofFactor, const double &tofOffset) const override
Get the minimum time at sample.
const HistogramData::HistogramE & e() const override
std::vector< Types::Core::DateAndTime > getPulseTOFTimes() const
Get the Pulse-time + TOF for each event in this EventList.
bool isSortedByTof() const override
Return true if the event list is sorted by TOF.
bool operator!=(const EventList &rhs) const
Inequality comparator.
std::mutex m_sortMutex
Mutex that is locked while sorting an event list.
Definition EventList.h:367
static void filterByPulseTimeHelper(std::vector< T > &events, Types::Core::DateAndTime start, Types::Core::DateAndTime stop, std::vector< T > &output)
std::unique_ptr< std::vector< WeightedEvent > > weightedEvents
List of WeightedEvent's.
Definition EventList.h:352
void initializePartials(std::map< int, EventList * > partials) const
Initialize the detector ID's and event type of the destination event lists when splitting this list.
void reverse()
Reverse the histogram boundaries and the associated events if they are sorted by time-of-flight.
std::vector< Types::Core::DateAndTime > eventTimesCalculator(const UnaryOperation &timesCalc) const
Compute a time (for instance, pulse-time plus TOF) associated to each event in the list.
void switchTo(Mantid::API::EventType newType) override
Switch the EventList to use the given EventType (TOF, WEIGHTED, or WEIGHTED_NOTIME)
void filterInPlaceHelper(Kernel::TimeROI const *timeRoi, typename std::vector< T > &events)
Perform an in-place filtering on a vector of either TofEvent's or WeightedEvent's.
bool empty() const
Much like stl containers, returns true if there is nothing in the event list.
const MantidVec & readX() const override
Deprecated, use x() instead. Returns the x data const.
WeightedEvent getEvent(size_t event_number)
Return the given event in the list.
void integrate(const double minX, const double maxX, const bool entireRange, double &sum, double &error) const
Integrate the events between a range of X values, or all events.
Kernel::cow_ptr< HistogramData::HistogramY > sharedY() const override
void createFromHistogram(const ISpectrum *inSpec, bool GenerateZeros, bool GenerateMultipleEvents, int MaxEventsPerBin)
Create an EventList from a histogram.
void convertUnitsQuicklyHelper(typename std::vector< T > &events, const double &factor, const double &power)
Convert the event's TOF (x) value according to a simple output = a * (input^b) relationship.
MantidVec * makeDataE() const
Calculates and returns a pointer to the E histogrammed data.
void checkIsYAndEWritable() const override
void copyDataFrom(const ISpectrum &source) override
Copy data from another EventList, via ISpectrum reference.
void filterByPulseTime(Types::Core::DateAndTime start, Types::Core::DateAndTime stop, EventList &output) const
Filter this EventList into an output EventList, using keeping only events within the >= start and < e...
void addPulsetimeHelper(std::vector< T > &events, const double seconds)
Add an offset to the pulsetime (wall-clock time) of each event in the list.
std::vector< double > getTofs() const override
Get the times-of-flight of each event in this EventList.
static void multiplyHistogramHelper(std::vector< T > &events, std::span< double const > X, std::span< double const > Y, std::span< double const > E)
Helper method for multiplying an event list by a histogram with error.
EventList & operator*=(const double value)
Operator to multiply the weights in this EventList by an error-less scalar.
void convertTof(std::function< double(double)> func, const int sorting=0) override
Mantid::Types::Core::DateAndTime getPulseTimeMin() const override
std::unique_ptr< std::vector< WeightedEventNoTime > > weightedEventsNoTime
List of WeightedEvent's.
Definition EventList.h:355
static void getWeightsHelper(const std::vector< T > &events, std::vector< double > &weights)
Get the weight member of all events in a list.
void maskCondition(const std::vector< bool > &mask) override
Mask out events by the condition vector.
void sortPulseTime() const
Sort events by Frame.
std::vector< T >::const_iterator findFirstTimeAtSampleEvent(const std::vector< T > &events, const double seek_time, const double &tofFactor, const double &tofOffset) const
Utility function: Returns the iterator into events of the first TofEvent with time at sample > seek_t...
void sortPulseTimeTOFDelta(const Types::Core::DateAndTime &start, const double seconds) const
Sort by the pulse time with a tolerance.
static std::size_t maskTofHelper(std::vector< T > &events, const double tofMin, const double tofMax)
Mask out events that have a tof between tofMin and tofMax (inclusively).
void addEventQuickly(const Types::Event::TofEvent &event)
Append an event to the histogram, without clearing the cache, to make it faster.
Definition EventList.h:105
static std::vector< T >::const_iterator findFirstPulseEvent(const std::vector< T > &events, const double seek_pulsetime)
Utility function: Returns the iterator into events of the first TofEvent with pulsetime() > seek_puls...
std::vector< Types::Core::DateAndTime > getPulseTOFTimesAtSample(const double &factor, const double &shift) const
Get the Pulse-time + time-of-flight of the neutron up to the sample, for each event in this EventList...
void generateErrorsHistogram(std::span< double const > Y, MantidVec &E) const
Generate the Error histogram for the provided counts histogram.
EventList & operator-=(const EventList &more_events)
SUBTRACT another EventList from this event list.
void sort(const EventSortType order) const
Sort events by TOF or Frame.
MantidVec * makeDataY() const
Calculates and returns a pointer to the Y histogrammed data.
std::unique_ptr< std::vector< Types::Event::TofEvent > > events
List of TofEvent (no weights).
Definition EventList.h:349
static void getTofsHelper(const std::vector< T > &events, std::vector< double > &tofs)
Get the m_tof member of all events in a list.
void clear(const bool removeDetIDs=true) override
Clear the list of events and any associated detector ID's.
std::vector< WeightedEvent > & getWeightedEvents()
Return the list of WeightedEvent contained.
void divide(const double value, const double error=0.0) override
Divide the weights in this event list by a scalar with an (optional) error.
This is a container for the MRU (most-recently-used) list of generated histograms.
void insertY(size_t thread_num, YType data, const EventList *index)
Insert a new histogram into the MRU.
EType findE(size_t thread_num, const EventList *index)
Find a Y histogram in the MRU.
void insertE(size_t thread_num, EType data, const EventList *index)
Insert a new histogram into the MRU.
void ensureEnoughBuffersE(size_t thread_num) const
This function makes sure that there are enough data buffers (MRU's) for E for the number of threads r...
YType findY(size_t thread_num, const EventList *index)
Find a Y histogram in the MRU.
void deleteIndex(const EventList *index)
Delete any entries in the MRU at the given index.
void ensureEnoughBuffersY(size_t thread_num) const
This function makes sure that there are enough data buffers (MRU's) for Y for the number of threads r...
1D histogram implementation.
Definition Histogram1D.h:19
Info about a single neutron detection event, including a weight and error value, but excluding the pu...
Definition Events.h:91
Info about a single neutron detection event, including a weight and error value:
Definition Events.h:39
TimeROI : Object that holds information about when the time measurement was active.
Definition TimeROI.h:18
const std::vector< Kernel::TimeInterval > toTimeIntervals() const
This method is to lend itself to helping with transition.
Definition TimeROI.cpp:557
bool useAll() const
TimeROI selects all time to be used.
Definition TimeROI.cpp:693
The base units (abstract) class.
Definition Unit.h:40
virtual double singleToTOF(const double x) const =0
Convert a single X value to TOF.
virtual double singleFromTOF(const double tof) const =0
Convert a single tof value to this unit.
bool isInitialized() const
Definition Unit.h:184
Implements a copy on write data template.
Definition cow_ptr.h:41
EventType
What kind of event list is being stored.
Definition IEventList.h:19
std::size_t numEvents(Nexus::File &file, bool &hasTotalCounts, bool &oldNeXusFileNames, const std::string &prefix)
Get the number of events in the currently opened group.
bool compareEventPulseTime(const TofEvent &e1, const TofEvent &e2)
Compare two events' FRAME id, return true if e1 should be before e2.
MANTID_DATAOBJECTS_DLL void getEventsFrom(EventList &el, std::vector< Types::Event::TofEvent > *&events)
static std::vector< T >::const_iterator findFirstEvent(const std::vector< T > &events, T seek_tof)
Utility function: Returns the iterator into events of the first TofEvent with tof() > seek_tof Will r...
EventSortType
How the event list is sorted.
Definition EventList.h:32
bool compareEventPulseTimeTOF(const TofEvent &e1, const TofEvent &e2)
Compare two events' FRAME id, return true if e1 should be before e2.
MANTID_KERNEL_DLL Types::Core::DateAndTime averageSorted(const std::vector< Types::Core::DateAndTime > &times)
averageSorted Assuming that the vector is sorted, find the average time
Helper class which provides the Collimation Length for SANS instruments.
std::vector< double > MantidVec
typedef for the data storage used in Mantid matrix workspaces
Definition cow_ptr.h:172
int32_t specnum_t
Typedef for a spectrum Number.
Definition IDTypes.h:14
std::optional< size_t > operator()(std::span< double const > X, const double tof, const bool findExact)
FindBin(double step, double xmin)
std::optional< size_t >(* findBin)(std::span< double const >, const double, const double, const double, const bool)
bool operator()(const TofEvent &e1, const TofEvent &e2)
comparePulseTimeTOFDelta(const Types::Core::DateAndTime &start, const double seconds)