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
26#include <utility>
27
28namespace Mantid {
29
30namespace PropertyNames {
31const std::string INPUT_WKSP("InputWorkspace");
32const std::string OUTPUT_WKSP("OutputWorkspace");
33const std::string PARAMS("Params");
34const std::string PRSRV_EVENTS("PreserveEvents");
35const std::string FULL_BIN_ONLY("FullBinsOnly");
36const std::string IGNR_BIN_ERR("IgnoreBinErrors");
37const std::string RVRS_LOG_BIN("UseReverseLogarithmic");
38const std::string POWER("Power");
39const std::string BINMODE("BinningMode");
40} // namespace PropertyNames
41
42namespace {
43const std::vector<std::string> binningModeNames{"Default", "Linear", "Logarithmic", "ReverseLogarithmic", "Power"};
44enum class BinningMode { DEFAULT, LINEAR, LOGARITHMIC, REVERSELOG, POWER, enum_count };
46} // namespace
47
48namespace Algorithms {
49
50// Register the class into the algorithm factory
52
53using namespace Kernel;
54using namespace API;
55using DataObjects::EventList;
56using DataObjects::EventWorkspace;
59using HistogramData::BinEdges;
60using HistogramData::Frequencies;
61using HistogramData::FrequencyStandardDeviations;
62using HistogramData::Histogram;
63using HistogramData::Exception::InvalidBinEdgesError;
64
65//---------------------------------------------------------------------------------------------
66// Public static methods
67//---------------------------------------------------------------------------------------------
68
79std::vector<double> Rebin::rebinParamsFromInput(const std::vector<double> &inParams,
80 const API::MatrixWorkspace &inputWS, Kernel::Logger &logger,
81 const std::string &binModeName) {
82 // EnumeratedString<Rebin::BinningMode, binningModeNames> binMode = binModeName;
83 std::vector<double> rbParams;
84 // The validator only passes parameters with size 2n+1. No need to check again here
85 if (inParams.size() >= 3) {
86 // Input are min, delta1, mid1, delta2, mid2, ... , max
87 rbParams = inParams;
88 } else if (inParams.size() == 1) {
89 double xmin = 0.;
90 double xmax = 0.;
91 inputWS.getXMinMax(xmin, xmax);
92
93 logger.information() << "Using the current min and max as default " << xmin << ", " << xmax << '\n';
94 rbParams.resize(3);
95 rbParams[0] = xmin;
96 rbParams[1] = inParams[0];
97 rbParams[2] = xmax;
98 }
99
100 // if linear or power binning specified, require positive bin width
101 // if logarithmic binning specified, require "negative" bin width
102 BINMODE binMode = binModeName;
103 if (binMode != BinningMode::DEFAULT) {
104 logger.information() << "Bin mode set, forcing bin parameters to match.";
105 for (size_t i = 0; i < rbParams.size() - 2; i += 2) { // e.g. xmin, xstep1, xmid1, xstep2, xmid2, xstep3, xmax
106 if (binMode == BinningMode::LINEAR || binMode == BinningMode::POWER) {
107 rbParams[i + 1] = fabs(rbParams[i + 1]);
108 } else if (binMode == BinningMode::LOGARITHMIC || binMode == BinningMode::REVERSELOG) {
109 rbParams[i + 1] = -fabs(rbParams[i + 1]);
110 }
111 }
112 } // end if
113 for (size_t i = 0; i < rbParams.size() - 2; i += 2) {
114 // make sure logarithmic binning does not change signs
115 if (rbParams[i] < 0 && rbParams[i + 1] < 0 && rbParams[i + 2] > 0) {
116 std::stringstream msg;
117 msg << "Cannot create logarithmic binning that changes sign (xmin=";
118 msg << rbParams[i] << ", xmax=" << rbParams[i + 2] << ")";
119 throw std::runtime_error(msg.str());
120 }
121 } // end for
122 return rbParams;
123}
124
125//---------------------------------------------------------------------------------------------
126// Public methods
127//---------------------------------------------------------------------------------------------
128
130std::map<std::string, std::string> Rebin::validateInputs() {
131 std::map<std::string, std::string> helpMessages;
132
133 // determing the binning mode, if present, or use default setting
134 BINMODE binMode;
137 } else {
138 binMode = "Default";
139 }
140
141 // validate the rebin params, and outside default mode, reset them
143 const auto eventInputWS = std::dynamic_pointer_cast<const EventWorkspace>(inputWS);
144 std::vector<double> rbParams = getProperty(PropertyNames::PARAMS);
145 std::vector<double> validParams;
146 bool paramsWereReset = false;
147 if (inputWS == nullptr) {
148 // The workspace could exist, but not be a MatrixWorkspace, e.g. it might be
149 // a group workspace. In that case we don't want a validation error for the
150 // Rebin parameters.
151 const std::string &inputWsName = getProperty(PropertyNames::INPUT_WKSP);
152 if (!AnalysisDataService::Instance().doesExist(inputWsName)) {
153 helpMessages[PropertyNames::INPUT_WKSP] = "Input workspace not in ADS.";
154 }
155 } else {
156 try {
157 validParams = rebinParamsFromInput(rbParams, *inputWS, g_log, binMode);
158 // if the binmode has been set, force the rebin params to be consistent
159 if (binMode != BinningMode::DEFAULT) {
161 paramsWereReset = true;
162 }
163 } catch (std::exception &err) {
164 helpMessages[PropertyNames::PARAMS] = err.what();
165 }
166 }
167
168 // if user specifies a binning mode, set this flag for them
169 if (binMode == BinningMode::REVERSELOG) {
172 }
173 } else if (binMode != BinningMode::DEFAULT) {
176 }
177 }
178
179 // validate power property, if present
180 double power = 0.0;
182 if (isDefault(PropertyNames::POWER) && binMode == BinningMode::POWER) {
183 std::string msg = "The binning mode was set to 'Power', but no power was given.";
184 helpMessages[PropertyNames::POWER] = msg;
185 helpMessages[PropertyNames::BINMODE] = msg;
186 return helpMessages;
187 }
188 if (binMode != BinningMode::DEFAULT && binMode != BinningMode::POWER) {
189 g_log.information() << "Discarding input power for incompatible binning mode.";
191 } else {
193 }
194 }
195 // estimate the number of bins needed and compare to available memory
196 if (inputWS && !validParams.empty()) {
197 size_t numBins = VectorHelper::estimateNumberOfBins(validParams, power);
198 const bool preserveEvents = static_cast<bool>(getProperty(PropertyNames::PRSRV_EVENTS));
199 if (power != 0. && numBins > 10'001) { // this number of bins should only be considered an issue for power binning
200 helpMessages[PropertyNames::POWER] = "This binning is expected to give " + std::to_string(numBins) + " bins.";
201 } else if (!(eventInputWS && preserveEvents)) {
202 size_t numSpec = inputWS->getNumberHistograms();
203 std::size_t binSpaceInBytes = 2 * numSpec * numBins * sizeof(double); // memory required in bytes
204 std::string memMsg = MemoryStats().checkAvailableMemory(binSpaceInBytes);
205 if (!memMsg.empty()) {
206 memMsg = "This binning is expected to create " + std::to_string(numBins * numSpec) + " bins. " + memMsg +
207 " Consider grouping spectra before rebinning, or using a coarser binning.";
208 helpMessages[PropertyNames::PARAMS] = memMsg;
209 }
210 }
211 } else {
212 try {
213 const auto &paramsToValidate = paramsWereReset ? validParams : rbParams;
214 VectorHelper::validateRebinParameters(paramsToValidate, static_cast<bool>(power));
215 } catch (std::exception &err) {
216 helpMessages[PropertyNames::PARAMS] = err.what();
217 }
218 }
219
220 return helpMessages;
221}
222
228 "Workspace containing the input data");
230 "The name to give the output workspace");
231
233 std::make_unique<ArrayProperty<double>>(PropertyNames::PARAMS, std::make_shared<RebinParamsValidator>()),
234 "A comma separated list of first bin boundary, width, last bin boundary. "
235 "Optionally this can be followed by a comma and more widths and last boundary pairs. "
236 "Optionally this can also be a single number, which is the bin width. In this case, the boundary of "
237 "binning will be determined by minimum and maximum TOF values among all events, or previous binning "
238 "boundary, in case of event Workspace, or non-event Workspace, respectively. "
239 "Negative width values indicate logarithmic binning.");
240
242 "Keep the output workspace as an EventWorkspace, if the input has events. If the input and output "
243 "EventWorkspace names are the same, only the X bins are set, which is very quick. If false, then the "
244 "workspace gets converted to a Workspace2D histogram.");
245
246 declareProperty(PropertyNames::FULL_BIN_ONLY, false, "Omit the final bin if its width is smaller than the step size");
247
249 "Ignore errors related to zero/negative bin widths in input/output workspaces. When ignored, the "
250 "signal and errors are set to zero");
251
254 "For logarithmic intervals, the splitting starts from the end and goes back to the start, ie the bins are bigger "
255 "at the start getting exponentially smaller until they reach the end. For these bins, the FullBinsOnly flag is "
256 "ignored.");
257
258 auto powerValidator = std::make_shared<Mantid::Kernel::BoundedValidator<double>>();
259 powerValidator->setLower(0);
260 powerValidator->setUpper(1);
261 declareProperty(PropertyNames::POWER, 0., powerValidator,
262 "Splits the interval in bins which actual width is equal to requested width / (i ^ power); default "
263 "is linear. Power must be between 0 and 1.");
264
267 "Optional. "
268 "Binning behavior can be specified in the usual way through sign of binwidth and other properties ('Default'); "
269 "or can be set to one of the allowed binning modes. "
270 "This will override all other specification or default behavior.");
271}
272
279 // Get the input workspace
282
283 // Are we preserving event workspace-iness?
284 bool PreserveEvents = getProperty(PropertyNames::PRSRV_EVENTS);
285
286 // Rebinning in-place
287 bool inPlace = (inputWS == outputWS);
288
289 std::vector<double> rbParams = rebinParamsFromInput(getProperty(PropertyNames::PARAMS), *inputWS, g_log);
290
291 const bool dist = inputWS->isDistribution();
292 const bool isHist = inputWS->isHistogramData();
293
294 // workspace independent determination of length
295 const auto histnumber = static_cast<int>(inputWS->getNumberHistograms());
296
297 bool fullBinsOnly = getProperty(PropertyNames::FULL_BIN_ONLY);
298 bool useReverseLog = getProperty(PropertyNames::RVRS_LOG_BIN);
299 double power = getProperty(PropertyNames::POWER);
300
301 double xmin = 0.;
302 double xmax = 0.;
303 inputWS->getXMinMax(xmin, xmax);
304
305 // create new output X axis
306 std::vector<double> xAxisTmp;
307 VectorHelper::createAxisFromRebinParams(rbParams, xAxisTmp, true, fullBinsOnly, xmin, xmax, useReverseLog, power);
308 HistogramData::BinEdges XValues_new(std::move(xAxisTmp));
309
310 // Now, determine if the input workspace is actually an EventWorkspace
311 EventWorkspace_const_sptr eventInputWS = std::dynamic_pointer_cast<const EventWorkspace>(inputWS);
312
313 if (eventInputWS != nullptr) {
314 //------- EventWorkspace as input -------------------------------------
315
316 if (PreserveEvents) {
317 if (!inPlace) {
318 outputWS = inputWS->clone();
319 }
320 auto eventOutputWS = std::dynamic_pointer_cast<EventWorkspace>(outputWS);
321 // This only sets the X axis. Actual rebinning will be done upon data
322 // access.
323 eventOutputWS->setAllX(XValues_new);
324 } else {
325 //--------- Different output, OR you're inplace but not preserving Events
326 g_log.information() << "Creating a Workspace2D from the EventWorkspace " << eventInputWS->getName() << ".\n";
327 outputWS = DataObjects::create<DataObjects::Workspace2D>(*inputWS, histnumber, XValues_new);
328
329 // Initialize progress reporting.
330 Progress prog(this, 0.0, 1.0, histnumber);
331
332 bool useUnsortingHistogram = (rbParams.size() < 4) && !useReverseLog && power == 0.0;
333 g_log.information() << "Generating histogram without sorting=" << useUnsortingHistogram << "\n";
334
335 // Go through all the histograms and set the data
336 PARALLEL_FOR_IF(Kernel::threadSafe(*inputWS, *outputWS))
337 for (int i = 0; i < histnumber; ++i) {
339 // Get a const event list reference. eventInputWS->dataY() doesn't work.
340 const EventList &el = eventInputWS->getSpectrum(i);
341 MantidVec y_data, e_data;
342 // The EventList takes care of histogramming.
343 // as_const: XValues_new is shared with every output spectrum, so binding it
344 // non-const to a span would deep-copy the X data on each iteration
345 if (useUnsortingHistogram)
346 el.generateHistogram(rbParams[1], std::as_const(XValues_new), y_data, e_data);
347 else
348 el.generateHistogram(std::as_const(XValues_new), y_data, e_data);
349
350 // Copy the data over.
351 outputWS->mutableY(i) = y_data;
352 outputWS->mutableE(i) = e_data;
353
354 // Report progress
355 prog.report(name());
357 }
359 }
360
361 // Assign it to the output workspace property
363
364 } // END ---- EventWorkspace
365
366 else
367
368 { //------- Workspace2D or other MatrixWorkspace ---------------------------
369
370 if (!isHist) {
371 g_log.information() << "Rebin: Converting Data to Histogram.\n";
372 Mantid::API::Algorithm_sptr ChildAlg = createChildAlgorithm("ConvertToHistogram");
373 ChildAlg->initialize();
374 ChildAlg->setProperty("InputWorkspace", inputWS);
375 ChildAlg->execute();
376 inputWS = ChildAlg->getProperty("OutputWorkspace");
377 }
378
379 // make output Workspace the same type is the input, but with new length of
380 // signal array
381 outputWS = DataObjects::create<API::HistoWorkspace>(*inputWS, histnumber, XValues_new);
382
383 bool ignoreBinErrors = getProperty(PropertyNames::IGNR_BIN_ERR);
384
385 Progress prog(this, 0.0, 1.0, histnumber);
386 PARALLEL_FOR_IF(Kernel::threadSafe(*inputWS, *outputWS))
387 for (int hist = 0; hist < histnumber; ++hist) {
389
390 try {
391 outputWS->setHistogram(hist, HistogramData::rebin(inputWS->histogram(hist), XValues_new));
392 } catch (InvalidBinEdgesError &) {
393 if (ignoreBinErrors)
394 outputWS->setBinEdges(hist, XValues_new);
395 else
396 throw;
397 }
398 prog.report(name());
400 }
402 outputWS->setDistribution(dist);
403
404 // Now propagate any masking correctly to the output workspace
405 // More efficient to have this in a separate loop because
406 // MatrixWorkspace::maskBins blocks multi-threading
407 for (int i = 0; i < histnumber; ++i) {
408 if (inputWS->hasMaskedBins(i)) // Does the current spectrum have any masked bins?
409 {
410 this->propagateMasks(inputWS, outputWS, i, ignoreBinErrors);
411 }
412 }
413
414 if (!isHist) {
415 g_log.information() << "Rebin: Converting Data back to Data Points.\n";
416 Mantid::API::Algorithm_sptr ChildAlg = createChildAlgorithm("ConvertToPointData");
417 ChildAlg->initialize();
418 ChildAlg->setProperty<MatrixWorkspace_sptr>("InputWorkspace", outputWS);
419 ChildAlg->execute();
420 outputWS = ChildAlg->getProperty("OutputWorkspace");
421 }
422
423 // Assign it to the output workspace property
425
426 } // END ---- Workspace2D
427}
428
439 const int hist, const bool ignoreErrors) {
440 // Not too happy with the efficiency of this way of doing it, but it's a lot
441 // simpler to use the
442 // existing rebin algorithm to distribute the weights than to re-implement it
443 // for this
444
445 MantidVec masked_bins, weights;
446 // Get a reference to the list of masked bins for this spectrum
447 const MatrixWorkspace::MaskList &mask = inputWS->maskedBins(hist);
448 // Now iterate over the list, building up a vector of the masked bins
449 auto it = mask.cbegin();
450 auto &XValues = inputWS->x(hist);
451 masked_bins.emplace_back(XValues[(*it).first]);
452 weights.emplace_back((*it).second);
453 masked_bins.emplace_back(XValues[(*it).first + 1]);
454 for (++it; it != mask.end(); ++it) {
455 const double currentX = XValues[(*it).first];
456 // Add an intermediate bin with zero weight if masked bins aren't
457 // consecutive
458 if (masked_bins.back() != currentX) {
459 weights.emplace_back(0.0);
460 masked_bins.emplace_back(currentX);
461 }
462 weights.emplace_back((*it).second);
463 masked_bins.emplace_back(XValues[(*it).first + 1]);
464 }
465
467 auto errSize = weights.size();
468 Histogram oldHist(BinEdges(std::move(masked_bins)), Frequencies(std::move(weights)),
469 FrequencyStandardDeviations(errSize, 0));
470 // Use rebin function to redistribute the weights. Note that distribution flag
471 // is set
472
473 try {
474 auto newHist = HistogramData::rebin(oldHist, outputWS->binEdges(hist));
475 auto &newWeights = newHist.y();
476
477 // Now process the output vector and fill the new masking list
478 for (size_t index = 0; index < newWeights.size(); ++index) {
479 if (newWeights[index] > 0.0)
480 outputWS->flagMasked(hist, index, newWeights[index]);
481 }
482 } catch (InvalidBinEdgesError &) {
483 if (!ignoreErrors)
484 throw;
485 }
486}
487} // namespace Algorithms
488} // 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:278
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:438
void init() override
Initialisation method.
Definition Rebin.cpp:226
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:130
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:79
A class for holding :
Definition EventList.h:58
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