Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Logarithmic event grouping to CompressEvents #37203

Merged
merged 11 commits into from Apr 30, 2024
29 changes: 26 additions & 3 deletions Framework/DataHandling/src/CompressEvents.cpp
Expand Up @@ -12,6 +12,8 @@
#include "MantidKernel/BoundedValidator.h"
#include "MantidKernel/DateAndTimeHelpers.h"
#include "MantidKernel/DateTimeValidator.h"
#include "MantidKernel/EnumeratedString.h"
#include "MantidKernel/ListValidator.h"

#include "tbb/parallel_for.h"

Expand All @@ -26,6 +28,12 @@ using namespace Kernel;
using namespace API;
using namespace DataObjects;

namespace {
const std::vector<std::string> binningModeNames{"Default", "Linear", "Logarithmic"};
enum class BinningMode { DEFAULT, LINEAR, LOGARITHMIC, enum_count };
typedef Mantid::Kernel::EnumeratedString<BinningMode, &binningModeNames> BINMODE;
} // namespace

void CompressEvents::init() {
declareProperty(std::make_unique<WorkspaceProperty<EventWorkspace>>("InputWorkspace", "", Direction::Input),
"The name of the EventWorkspace on which to perform the algorithm");
Expand All @@ -36,10 +44,12 @@ void CompressEvents::init() {
// Tolerance must be >= 0.0
auto mustBePositive = std::make_shared<BoundedValidator<double>>();
mustBePositive->setLower(0.0);
declareProperty(std::make_unique<PropertyWithValue<double>>("Tolerance", 1e-5, mustBePositive, Direction::Input),
declareProperty(std::make_unique<PropertyWithValue<double>>("Tolerance", 1e-5, Direction::Input),
"The tolerance on each event's X value (normally TOF, but may be a "
"different unit if you have used ConvertUnits).\n"
"Any events within Tolerance will be summed into a single event.");
"Any events within Tolerance will be summed into a single event. When compressing where positive is "
"linear tolerance, negative is logorithmic tolerance, and zero indicates that time-of-flight must be "
"identical to compress.");

declareProperty(
std::make_unique<PropertyWithValue<double>>("WallClockTolerance", EMPTY_DBL(), mustBePositive, Direction::Input),
Expand All @@ -54,15 +64,28 @@ void CompressEvents::init() {
"starting filtering. Ignored if WallClockTolerance is not specified. "
"Default is start of run",
Direction::Input);

declareProperty("BinningMode", binningModeNames[size_t(BinningMode::DEFAULT)],
std::make_shared<Mantid::Kernel::StringListValidator>(binningModeNames),
"Binning behavior can be specified in the usual way through sign of tolerance and other properties "
"('Default'); or can be set to one of the allowed binning modes. This will override all other "
"specification or default behavior.");
}

void CompressEvents::exec() {
// Get the input workspace
EventWorkspace_sptr inputWS = getProperty("InputWorkspace");
EventWorkspace_sptr outputWS = getProperty("OutputWorkspace");
const double toleranceTof = getProperty("Tolerance");
double toleranceTof = getProperty("Tolerance");
const double toleranceWallClock = getProperty("WallClockTolerance");
const bool compressFat = !isEmpty(toleranceWallClock);

BINMODE mode = getPropertyValue("BinningMode");
if (mode == BinningMode::LINEAR)
toleranceTof = std::fabs(toleranceTof);
else if (mode == BinningMode::LOGARITHMIC)
toleranceTof = -1. * std::fabs(toleranceTof);

Types::Core::DateAndTime startTime;

if (compressFat) {
Expand Down
71 changes: 70 additions & 1 deletion Framework/DataHandling/test/CompressEventsTest.h
Expand Up @@ -32,7 +32,6 @@ class CompressEventsTest : public CxxTest::TestSuite {
void test_InvalidInputs() {
CompressEvents alg;
TS_ASSERT_THROWS_NOTHING(alg.initialize());
TS_ASSERT_THROWS(alg.setPropertyValue("Tolerance", "-1.0"), const std::invalid_argument &);
TS_ASSERT_THROWS_NOTHING(alg.setPropertyValue("Tolerance", "0.0"));
}

Expand Down Expand Up @@ -128,4 +127,74 @@ class CompressEventsTest : public CxxTest::TestSuite {
void test_InPlace_ZeroTolerance_WithPulseTime() {
doTest("CompressEvents_input", "CompressEvents_input", 0.0, 50, .001);
}

void doLogarithmicTest(const std::string &binningMode, const double tolerance, double wallClockTolerance = 0.) {
EventWorkspace_sptr input, output;
EventType eventType = WEIGHTED_NOTIME;
if (wallClockTolerance > 0.)
eventType = WEIGHTED;

/** Create event workspace with:
* 1 pixels (or another number)
* 64 histogrammed bins from 0.0 in steps of 1.0
* 128 events; two in each bin, at time 1.0, 2.0, etc.
* PulseTime = 1 second, 2 seconds, etc.
*/
input = WorkspaceCreationHelper::createEventWorkspace(1, 64, 64, 0, 1, 2);
AnalysisDataService::Instance().addOrReplace("CompressEvents_input", input);

TS_ASSERT_EQUALS(input->getNumberEvents(), 128);
const double inputIntegral = input->getSpectrum(0).integrate(0., 100., true);

CompressEvents alg;
alg.initialize();
alg.setPropertyValue("InputWorkspace", "CompressEvents_input");
alg.setPropertyValue("OutputWorkspace", "CompressEvents_output");
alg.setProperty("Tolerance", tolerance);
alg.setPropertyValue("BinningMode", binningMode);
if (wallClockTolerance > 0.) {
alg.setProperty("WallClockTolerance", wallClockTolerance);
alg.setProperty("StartTime",
"2010-01-01T00:00:00"); // copied from createEventWorkspace
}

TS_ASSERT_THROWS_NOTHING(alg.execute());
TS_ASSERT(alg.isExecuted());

output = AnalysisDataService::Instance().retrieveWS<EventWorkspace>("CompressEvents_output");

TS_ASSERT_EQUALS(output->getNumberEvents(), 7);
TS_ASSERT_EQUALS(output->getEventType(), eventType);
TS_ASSERT_DELTA(output->getSpectrum(0).integrate(0., 100., true), inputIntegral, 1.e-6);

EventList el = output->getSpectrum(0);
TS_ASSERT_DELTA(el.getEvent(0).weight(), 2.0, 1e-6);
TS_ASSERT_DELTA(el.getEvent(0).errorSquared(), 2.0, 1e-6);
TS_ASSERT_DELTA(el.getEvent(0).tof(), 0.5, 1e-6);
for (int i = 1; i < 7; i++) {
TS_ASSERT_DELTA(el.getEvent(i).weight(), 1.0 * pow(2, i), 1e-6);
TS_ASSERT_DELTA(el.getEvent(i).errorSquared(), 1.0 * pow(2, i), 1e-6);
TS_ASSERT_DELTA(el.getEvent(i).tof(), 0.75 * pow(2, i), 1e-6);
}

if (wallClockTolerance > 0.) {
int64_t firstTime = 631152000000000000;
TS_ASSERT_EQUALS(el.getEvent(0).pulseTime().totalNanoseconds(), firstTime);
TS_ASSERT_EQUALS(el.getEvent(1).pulseTime().totalNanoseconds(), firstTime + 1000000000);
TS_ASSERT_EQUALS(el.getEvent(2).pulseTime().totalNanoseconds(), firstTime + 2500000000);
TS_ASSERT_EQUALS(el.getEvent(3).pulseTime().totalNanoseconds(), firstTime + 5500000000);
TS_ASSERT_EQUALS(el.getEvent(4).pulseTime().totalNanoseconds(), firstTime + 11500000000);
TS_ASSERT_EQUALS(el.getEvent(5).pulseTime().totalNanoseconds(), firstTime + 23500000000);
TS_ASSERT_EQUALS(el.getEvent(6).pulseTime().totalNanoseconds(), firstTime + 47500000000);
} else {
for (int i = 0; i < 7; i++) {
TS_ASSERT_EQUALS(el.getEvent(i).pulseTime().totalNanoseconds(), 0);
peterfpeterson marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

void test_Logarithmic_binning() { doLogarithmicTest("Logarithmic", 1.); }
void test_Logarithmic_binning_default() { doLogarithmicTest("Default", -1.); }
void test_Logarithmic_binning_WithPulseTime() { doLogarithmicTest("Logarithmic", 1., 64); }
void test_Logarithmic_binning_default_WithPulseTime() { doLogarithmicTest("Default", -1., 64); }
};