Mantid
Loading...
Searching...
No Matches
Rebin.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 +
8#include "MantidHistogramData/Exception.h"
9#include "MantidHistogramData/Rebin.h"
10
22#include "MantidKernel/Memory.h"
25
26namespace Mantid {
27
28namespace PropertyNames {
29const std::string INPUT_WKSP("InputWorkspace");
30const std::string OUTPUT_WKSP("OutputWorkspace");
31const std::string PARAMS("Params");
32const std::string PRSRV_EVENTS("PreserveEvents");
33const std::string FULL_BIN_ONLY("FullBinsOnly");
34const std::string IGNR_BIN_ERR("IgnoreBinErrors");
35const std::string RVRS_LOG_BIN("UseReverseLogarithmic");
36const std::string POWER("Power");
37const std::string BINMODE("BinningMode");
38} // namespace PropertyNames
39
40namespace {
41const std::vector<std::string> binningModeNames{"Default", "Linear", "Logarithmic", "ReverseLogarithmic", "Power"};
42enum class BinningMode { DEFAULT, LINEAR, LOGARITHMIC, REVERSELOG, POWER, enum_count };
44} // namespace
45
46namespace Algorithms {
47
48// Register the class into the algorithm factory
50
51using namespace Kernel;
52using namespace API;
53using DataObjects::EventList;
54using DataObjects::EventWorkspace;
57using HistogramData::BinEdges;
58using HistogramData::Frequencies;
59using HistogramData::FrequencyStandardDeviations;
60using HistogramData::Histogram;
61using HistogramData::Exception::InvalidBinEdgesError;
62
63//---------------------------------------------------------------------------------------------
64// Public static methods
65//---------------------------------------------------------------------------------------------
66
77std::vector<double> Rebin::rebinParamsFromInput(const std::vector<double> &inParams,
78 const API::MatrixWorkspace &inputWS, Kernel::Logger &logger,
79 const std::string &binModeName) {
80 // EnumeratedString<Rebin::BinningMode, binningModeNames> binMode = binModeName;
81 std::vector<double> rbParams;
82 // The validator only passes parameters with size 2n+1. No need to check again here
83 if (inParams.size() >= 3) {
84 // Input are min, delta1, mid1, delta2, mid2, ... , max
85 rbParams = inParams;
86 } else if (inParams.size() == 1) {
87 double xmin = 0.;
88 double xmax = 0.;
89 inputWS.getXMinMax(xmin, xmax);
90
91 logger.information() << "Using the current min and max as default " << xmin << ", " << xmax << '\n';
92 rbParams.resize(3);
93 rbParams[0] = xmin;
94 rbParams[1] = inParams[0];
95 rbParams[2] = xmax;
96 }
97
98 // if linear or power binning specified, require positive bin width
99 // if logarithmic binning specified, require "negative" bin width
100 BINMODE binMode = binModeName;
101 if (binMode != BinningMode::DEFAULT) {
102 logger.information() << "Bin mode set, forcing bin parameters to match.";
103 for (size_t i = 0; i < rbParams.size() - 2; i += 2) { // e.g. xmin, xstep1, xmid1, xstep2, xmid2, xstep3, xmax
104 if (binMode == BinningMode::LINEAR || binMode == BinningMode::POWER) {
105 rbParams[i + 1] = fabs(rbParams[i + 1]);
106 } else if (binMode == BinningMode::LOGARITHMIC || binMode == BinningMode::REVERSELOG) {
107 rbParams[i + 1] = -fabs(rbParams[i + 1]);
108 }
109 }
110 } // end if
111 for (size_t i = 0; i < rbParams.size() - 2; i += 2) {
112 // make sure logarithmic binning does not change signs
113 if (rbParams[i] < 0 && rbParams[i + 1] < 0 && rbParams[i + 2] > 0) {
114 std::stringstream msg;
115 msg << "Cannot create logarithmic binning that changes sign (xmin=";
116 msg << rbParams[i] << ", xmax=" << rbParams[i + 2] << ")";
117 throw std::runtime_error(msg.str());
118 }
119 } // end for
120 return rbParams;
121}
122
123//---------------------------------------------------------------------------------------------
124// Public methods
125//---------------------------------------------------------------------------------------------
126
128std::map<std::string, std::string> Rebin::validateInputs() {
129 std::map<std::string, std::string> helpMessages;
130
131 // determing the binning mode, if present, or use default setting
132 BINMODE binMode;
135 } else {
136 binMode = "Default";
137 }
138
139 // validate the rebin params, and outside default mode, reset them
141 const auto eventInputWS = std::dynamic_pointer_cast<const EventWorkspace>(inputWS);
142 std::vector<double> rbParams = getProperty(PropertyNames::PARAMS);
143 std::vector<double> validParams;
144 bool paramsWereReset = false;
145 if (inputWS == nullptr) {
146 // The workspace could exist, but not be a MatrixWorkspace, e.g. it might be
147 // a group workspace. In that case we don't want a validation error for the
148 // Rebin parameters.
149 const std::string &inputWsName = getProperty(PropertyNames::INPUT_WKSP);
150 if (!AnalysisDataService::Instance().doesExist(inputWsName)) {
151 helpMessages[PropertyNames::INPUT_WKSP] = "Input workspace not in ADS.";
152 }
153 } else {
154 try {
155 validParams = rebinParamsFromInput(rbParams, *inputWS, g_log, binMode);
156 // if the binmode has been set, force the rebin params to be consistent
157 if (binMode != BinningMode::DEFAULT) {
159 paramsWereReset = true;
160 }
161 } catch (std::exception &err) {
162 helpMessages[PropertyNames::PARAMS] = err.what();
163 }
164 }
165
166 // if user specifies a binning mode, set this flag for them
167 if (binMode == BinningMode::REVERSELOG) {
170 }
171 } else if (binMode != BinningMode::DEFAULT) {
174 }
175 }
176
177 // validate power property, if present
178 double power = 0.0;
180 if (isDefault(PropertyNames::POWER) && binMode == BinningMode::POWER) {
181 std::string msg = "The binning mode was set to 'Power', but no power was given.";
182 helpMessages[PropertyNames::POWER] = msg;
183 helpMessages[PropertyNames::BINMODE] = msg;
184 return helpMessages;
185 }
186 if (binMode != BinningMode::DEFAULT && binMode != BinningMode::POWER) {
187 g_log.information() << "Discarding input power for incompatible binning mode.";
189 } else {
191 }
192 }
193 // estimate the number of bins needed and compare to available memory
194 if (inputWS && !validParams.empty()) {
195 size_t numBins = VectorHelper::estimateNumberOfBins(validParams, power);
196 const bool preserveEvents = static_cast<bool>(getProperty(PropertyNames::PRSRV_EVENTS));
197 if (power != 0. && numBins > 10'001) { // this number of bins should only be considered an issue for power binning
198 helpMessages[PropertyNames::POWER] = "This binning is expected to give " + std::to_string(numBins) + " bins.";
199 } else if (!(eventInputWS && preserveEvents)) {
200 size_t numSpec = inputWS->getNumberHistograms();
201 std::size_t binSpaceInBytes = 2 * numSpec * numBins * sizeof(double); // memory required in bytes
202 std::string memMsg = MemoryStats().checkAvailableMemory(binSpaceInBytes);
203 if (!memMsg.empty()) {
204 memMsg = "This binning is expected to create " + std::to_string(numBins * numSpec) + " bins. " + memMsg +
205 " Consider grouping spectra before rebinning, or using a coarser binning.";
206 helpMessages[PropertyNames::PARAMS] = memMsg;
207 }
208 }
209 } else {
210 try {
211 const auto &paramsToValidate = paramsWereReset ? validParams : rbParams;
212 VectorHelper::validateRebinParameters(paramsToValidate, static_cast<bool>(power));
213 } catch (std::exception &err) {
214 helpMessages[PropertyNames::PARAMS] = err.what();
215 }
216 }
217
218 return helpMessages;
219}
220
226 "Workspace containing the input data");
228 "The name to give the output workspace");
229
231 std::make_unique<ArrayProperty<double>>(PropertyNames::PARAMS, std::make_shared<RebinParamsValidator>()),
232 "A comma separated list of first bin boundary, width, last bin boundary. "
233 "Optionally this can be followed by a comma and more widths and last boundary pairs. "
234 "Optionally this can also be a single number, which is the bin width. In this case, the boundary of "
235 "binning will be determined by minimum and maximum TOF values among all events, or previous binning "
236 "boundary, in case of event Workspace, or non-event Workspace, respectively. "
237 "Negative width values indicate logarithmic binning.");
238
240 "Keep the output workspace as an EventWorkspace, if the input has events. If the input and output "
241 "EventWorkspace names are the same, only the X bins are set, which is very quick. If false, then the "
242 "workspace gets converted to a Workspace2D histogram.");
243
244 declareProperty(PropertyNames::FULL_BIN_ONLY, false, "Omit the final bin if its width is smaller than the step size");
245
247 "Ignore errors related to zero/negative bin widths in input/output workspaces. When ignored, the "
248 "signal and errors are set to zero");
249
252 "For logarithmic intervals, the splitting starts from the end and goes back to the start, ie the bins are bigger "
253 "at the start getting exponentially smaller until they reach the end. For these bins, the FullBinsOnly flag is "
254 "ignored.");
255
256 auto powerValidator = std::make_shared<Mantid::Kernel::BoundedValidator<double>>();
257 powerValidator->setLower(0);
258 powerValidator->setUpper(1);
259 declareProperty(PropertyNames::POWER, 0., powerValidator,
260 "Splits the interval in bins which actual width is equal to requested width / (i ^ power); default "
261 "is linear. Power must be between 0 and 1.");
262
265 "Optional. "
266 "Binning behavior can be specified in the usual way through sign of binwidth and other properties ('Default'); "
267 "or can be set to one of the allowed binning modes. "
268 "This will override all other specification or default behavior.");
269}
270
277 // Get the input workspace
280
281 // Are we preserving event workspace-iness?
282 bool PreserveEvents = getProperty(PropertyNames::PRSRV_EVENTS);
283
284 // Rebinning in-place
285 bool inPlace = (inputWS == outputWS);
286
287 std::vector<double> rbParams = rebinParamsFromInput(getProperty(PropertyNames::PARAMS), *inputWS, g_log);
288
289 const bool dist = inputWS->isDistribution();
290 const bool isHist = inputWS->isHistogramData();
291
292 // workspace independent determination of length
293 const auto histnumber = static_cast<int>(inputWS->getNumberHistograms());
294
295 bool fullBinsOnly = getProperty(PropertyNames::FULL_BIN_ONLY);
296 bool useReverseLog = getProperty(PropertyNames::RVRS_LOG_BIN);
297 double power = getProperty(PropertyNames::POWER);
298
299 double xmin = 0.;
300 double xmax = 0.;
301 inputWS->getXMinMax(xmin, xmax);
302
303 // create new output X axis
304 std::vector<double> xAxisTmp;
305 VectorHelper::createAxisFromRebinParams(rbParams, xAxisTmp, true, fullBinsOnly, xmin, xmax, useReverseLog, power);
306 HistogramData::BinEdges XValues_new(std::move(xAxisTmp));
307
308 // Now, determine if the input workspace is actually an EventWorkspace
309 EventWorkspace_const_sptr eventInputWS = std::dynamic_pointer_cast<const EventWorkspace>(inputWS);
310
311 if (eventInputWS != nullptr) {
312 //------- EventWorkspace as input -------------------------------------
313
314 if (PreserveEvents) {
315 if (!inPlace) {
316 outputWS = inputWS->clone();
317 }
318 auto eventOutputWS = std::dynamic_pointer_cast<EventWorkspace>(outputWS);
319 // This only sets the X axis. Actual rebinning will be done upon data
320 // access.
321 eventOutputWS->setAllX(XValues_new);
322 } else {
323 //--------- Different output, OR you're inplace but not preserving Events
324 g_log.information() << "Creating a Workspace2D from the EventWorkspace " << eventInputWS->getName() << ".\n";
325 outputWS = DataObjects::create<DataObjects::Workspace2D>(*inputWS, histnumber, XValues_new);
326
327 // Initialize progress reporting.
328 Progress prog(this, 0.0, 1.0, histnumber);
329
330 bool useUnsortingHistogram = (rbParams.size() < 4) && !useReverseLog && power == 0.0;
331 g_log.information() << "Generating histogram without sorting=" << useUnsortingHistogram << "\n";
332
333 // Go through all the histograms and set the data
334 PARALLEL_FOR_IF(Kernel::threadSafe(*inputWS, *outputWS))
335 for (int i = 0; i < histnumber; ++i) {
337 // Get a const event list reference. eventInputWS->dataY() doesn't work.
338 const EventList &el = eventInputWS->getSpectrum(i);
339 MantidVec y_data, e_data;
340 // The EventList takes care of histogramming.
341 if (useUnsortingHistogram)
342 el.generateHistogram(rbParams[1], XValues_new.rawData(), y_data, e_data);
343 else
344 el.generateHistogram(XValues_new.rawData(), y_data, e_data);
345
346 // Copy the data over.
347 outputWS->mutableY(i) = y_data;
348 outputWS->mutableE(i) = e_data;
349
350 // Report progress
351 prog.report(name());
353 }
355 }
356
357 // Assign it to the output workspace property
359
360 } // END ---- EventWorkspace
361
362 else
363
364 { //------- Workspace2D or other MatrixWorkspace ---------------------------
365
366 if (!isHist) {
367 g_log.information() << "Rebin: Converting Data to Histogram.\n";
368 Mantid::API::Algorithm_sptr ChildAlg = createChildAlgorithm("ConvertToHistogram");
369 ChildAlg->initialize();
370 ChildAlg->setProperty("InputWorkspace", inputWS);
371 ChildAlg->execute();
372 inputWS = ChildAlg->getProperty("OutputWorkspace");
373 }
374
375 // make output Workspace the same type is the input, but with new length of
376 // signal array
377 outputWS = DataObjects::create<API::HistoWorkspace>(*inputWS, histnumber, XValues_new);
378
379 bool ignoreBinErrors = getProperty(PropertyNames::IGNR_BIN_ERR);
380
381 Progress prog(this, 0.0, 1.0, histnumber);
382 PARALLEL_FOR_IF(Kernel::threadSafe(*inputWS, *outputWS))
383 for (int hist = 0; hist < histnumber; ++hist) {
385
386 try {
387 outputWS->setHistogram(hist, HistogramData::rebin(inputWS->histogram(hist), XValues_new));
388 } catch (InvalidBinEdgesError &) {
389 if (ignoreBinErrors)
390 outputWS->setBinEdges(hist, XValues_new);
391 else
392 throw;
393 }
394 prog.report(name());
396 }
398 outputWS->setDistribution(dist);
399
400 // Now propagate any masking correctly to the output workspace
401 // More efficient to have this in a separate loop because
402 // MatrixWorkspace::maskBins blocks multi-threading
403 for (int i = 0; i < histnumber; ++i) {
404 if (inputWS->hasMaskedBins(i)) // Does the current spectrum have any masked bins?
405 {
406 this->propagateMasks(inputWS, outputWS, i, ignoreBinErrors);
407 }
408 }
409
410 if (!isHist) {
411 g_log.information() << "Rebin: Converting Data back to Data Points.\n";
412 Mantid::API::Algorithm_sptr ChildAlg = createChildAlgorithm("ConvertToPointData");
413 ChildAlg->initialize();
414 ChildAlg->setProperty<MatrixWorkspace_sptr>("InputWorkspace", outputWS);
415 ChildAlg->execute();
416 outputWS = ChildAlg->getProperty("OutputWorkspace");
417 }
418
419 // Assign it to the output workspace property
421
422 } // END ---- Workspace2D
423}
424
435 const int hist, const bool ignoreErrors) {
436 // Not too happy with the efficiency of this way of doing it, but it's a lot
437 // simpler to use the
438 // existing rebin algorithm to distribute the weights than to re-implement it
439 // for this
440
441 MantidVec masked_bins, weights;
442 // Get a reference to the list of masked bins for this spectrum
443 const MatrixWorkspace::MaskList &mask = inputWS->maskedBins(hist);
444 // Now iterate over the list, building up a vector of the masked bins
445 auto it = mask.cbegin();
446 auto &XValues = inputWS->x(hist);
447 masked_bins.emplace_back(XValues[(*it).first]);
448 weights.emplace_back((*it).second);
449 masked_bins.emplace_back(XValues[(*it).first + 1]);
450 for (++it; it != mask.end(); ++it) {
451 const double currentX = XValues[(*it).first];
452 // Add an intermediate bin with zero weight if masked bins aren't
453 // consecutive
454 if (masked_bins.back() != currentX) {
455 weights.emplace_back(0.0);
456 masked_bins.emplace_back(currentX);
457 }
458 weights.emplace_back((*it).second);
459 masked_bins.emplace_back(XValues[(*it).first + 1]);
460 }
461
463 auto errSize = weights.size();
464 Histogram oldHist(BinEdges(std::move(masked_bins)), Frequencies(std::move(weights)),
465 FrequencyStandardDeviations(errSize, 0));
466 // Use rebin function to redistribute the weights. Note that distribution flag
467 // is set
468
469 try {
470 auto newHist = HistogramData::rebin(oldHist, outputWS->binEdges(hist));
471 auto &newWeights = newHist.y();
472
473 // Now process the output vector and fill the new masking list
474 for (size_t index = 0; index < newWeights.size(); ++index) {
475 if (newWeights[index] > 0.0)
476 outputWS->flagMasked(hist, index, newWeights[index]);
477 }
478 } catch (InvalidBinEdgesError &) {
479 if (!ignoreErrors)
480 throw;
481 }
482}
483} // namespace Algorithms
484} // namespace Mantid
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:542
std::map< DeltaEMode::Type, std::string > index
#define fabs(x)
Definition Matrix.cpp:22
#define PARALLEL_START_INTERRUPT_REGION
Begins a block to skip processing is the algorithm has been interupted Note the end of the block if n...
#define PARALLEL_END_INTERRUPT_REGION
Ends a block to skip processing is the algorithm has been interupted Note the start of the block if n...
#define PARALLEL_FOR_IF(condition)
Empty definitions - to enable set your complier to enable openMP.
#define PARALLEL_CHECK_INTERRUPT_REGION
Adds a check after a Parallel region to see if it was interupted.
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
bool existsProperty(const std::string &name) const override
Checks whether the named property is already in the list of managed property.
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
virtual std::shared_ptr< Algorithm > createChildAlgorithm(const std::string &name, const double startProgress=-1., const double endProgress=-1., const bool enableLogging=true, const int &version=-1)
Create a Child Algorithm.
Kernel::Logger & g_log
Definition Algorithm.h:423
bool isDefault(const std::string &name) const
Base MatrixWorkspace Abstract Class.
std::map< size_t, double > MaskList
Masked bins for each spectrum are stored as a set of pairs containing <bin index, weight>
virtual void getXMinMax(double &xmin, double &xmax) const
Helper class for reporting progress from algorithms.
Definition Progress.h:25
A property class for workspaces.
void exec() override
Executes the rebin algorithm.
Definition Rebin.cpp:276
void propagateMasks(const API::MatrixWorkspace_const_sptr &inputWS, const API::MatrixWorkspace_sptr &outputWS, const int hist, const bool IgnoreBinErrors=false)
Takes the masks in the input workspace and apportions the weights into the new bins that overlap with...
Definition Rebin.cpp:434
void init() override
Initialisation method.
Definition Rebin.cpp:224
const std::string name() const override
Algorithm's name for identification overriding a virtual method.
Definition Rebin.h:42
std::map< std::string, std::string > validateInputs() override
Validate that the input properties are sane.
Definition Rebin.cpp:128
static std::vector< double > rebinParamsFromInput(const std::vector< double > &inParams, const API::MatrixWorkspace &inputWS, Kernel::Logger &logger, const std::string &binModeName="Default")
Return the rebin parameters from a user input.
Definition Rebin.cpp:77
A class for holding :
Definition EventList.h:57
Support for a property that holds an array of values.
A concrete property based on user options of a finite list of strings.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
The Logger class is in charge of the publishing messages from the framework through various channels.
Definition Logger.h:51
void information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
This class is responsible for memory statistics.
Definition Memory.h:28
std::string checkAvailableMemory(std::size_t const requestedMemoryBytes) const
Check if there is enough space in memory to hold the requested amount of memory.
Definition Memory.cpp:571
void report()
Increments the loop counter by 1, then sends the progress notification on behalf of its algorithm.
std::shared_ptr< const MatrixWorkspace > MatrixWorkspace_const_sptr
shared pointer to the matrix workspace base class (const version)
std::shared_ptr< Algorithm > Algorithm_sptr
Typedef for a shared pointer to an Algorithm.
Definition Algorithm.h:52
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
std::shared_ptr< const EventWorkspace > EventWorkspace_const_sptr
shared pointer to a const Workspace2D
std::shared_ptr< EventWorkspace > EventWorkspace_sptr
shared pointer to the EventWorkspace class
std::size_t MANTID_KERNEL_DLL estimateNumberOfBins(std::vector< double > const &params, double const power=-1)
Returns a size_t with the estimated number of bins that would be needed for a rebinning operation.
void MANTID_KERNEL_DLL validateRebinParameters(std::vector< double > const &, bool const =false)
Validate rebinning parameters, throwing an error if any assumptions are invalidated.
std::size_t MANTID_KERNEL_DLL createAxisFromRebinParams(const std::vector< double > &params, std::vector< double > &xnew, const bool resize_xnew=true, const bool full_bins_only=false, const double xMinHint=std::nan(""), const double xMaxHint=std::nan(""), const bool useReverseLogarithmic=false, const double power=-1)
Creates a new output X array given a 'standard' set of rebinning parameters.
std::enable_if< std::is_pointer< Arg >::value, bool >::type threadSafe(Arg workspace)
Thread-safety check Checks the workspace to ensure it is suitable for multithreaded access.
const std::string IGNR_BIN_ERR("IgnoreBinErrors")
const std::string FULL_BIN_ONLY("FullBinsOnly")
const std::string PRSRV_EVENTS("PreserveEvents")
const std::string PARAMS("Params")
const std::string POWER("Power")
const std::string RVRS_LOG_BIN("UseReverseLogarithmic")
const std::string BINMODE("BinningMode")
const std::string OUTPUT_WKSP("OutputWorkspace")
const std::string INPUT_WKSP("InputWorkspace")
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
std::string to_string(const wide_integer< Bits, Signed > &n)
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54