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