22#include <boost/tuple/tuple.hpp>
60 using std::placeholders::_1;
61 using std::placeholders::_2;
62 using std::placeholders::_3;
81 const double sigma = (fwhm * 0.42463) / 2.0;
82 const double sigma_factor = M_SQRT1_2 / (fwhm * 0.42463);
90 double pixel_value = std::erf(0.5 * sigma_factor) *
sigma;
92 while (pixel_value > 0.02) {
93 kernel_one_side.emplace_back(pixel_value);
96 (std::erf((pixel_count + 0.5) * sigma_factor) - std::erf((pixel_count - 0.5) * sigma_factor)) * 0.5 *
sigma;
101 kernel.resize(kernel_one_side.size() * 2 - 1);
102 std::reverse_copy(kernel_one_side.cbegin(), kernel_one_side.cend(), kernel.begin());
103 std::copy(kernel_one_side.cbegin() + 1, kernel_one_side.cend(), kernel.begin() + kernel_one_side.size());
116 if (validity.size() == kernel.size() && std::find(validity.cbegin(), validity.cend(),
false) != validity.cend()) {
118 for (
size_t i = 0; i < kernel.size(); ++i) {
119 kernel[i] *= validity[i];
132 double sum_kernel_recip = 1.0 / std::accumulate(kernel.cbegin(), kernel.cend(), 0.0);
133 std::transform(kernel.cbegin(), kernel.cend(), kernel.begin(),
134 [sum_kernel_recip](
const auto &
value) { return value * sum_kernel_recip; });
153const std::string
SmoothMD::summary()
const {
return "Smooth an MDHistoWorkspace according to a weight function"; }
166 const bool useWeights = (weightingWS !=
nullptr);
167 uint64_t nPoints = toSmooth->getNPoints();
171 progress.reportIncrement(
size_t(
double(nPoints) * 0.1));
175 auto iterators = toSmooth->createIterators(nThreads,
nullptr);
178 for (
int it = 0; it < int(iterators.size()); ++it) {
184 throw std::logic_error(
"Failed to cast IMDIterator to MDHistoWorkspaceIterator");
189 size_t iteratorIndex = iterator->getLinearIndex();
194 if (weightingWS->getSignalAt(iteratorIndex) == 0) {
196 outWS->setSignalAt(iteratorIndex, std::numeric_limits<double>::quiet_NaN());
197 outWS->setErrorSquaredAt(iteratorIndex, std::numeric_limits<double>::quiet_NaN());
206 std::vector<int> widthVectorInt;
207 widthVectorInt.resize(widthVector.size());
208 std::transform(widthVector.cbegin(), widthVector.cend(), widthVectorInt.begin(),
209 [](
double w) ->
int { return static_cast<int>(w); });
211 std::vector<size_t> neighbourIndexes = iterator->findNeighbourIndexesByWidth(widthVectorInt);
213 size_t nNeighbours = neighbourIndexes.size();
214 double sumSignal = iterator->getSignal();
215 double sumSqError = iterator->getError();
216 for (
auto neighbourIndex : neighbourIndexes) {
218 if (weightingWS->getSignalAt(neighbourIndex) == 0) {
224 sumSignal += toSmooth->getSignalAt(neighbourIndex);
225 double error = toSmooth->getErrorAt(neighbourIndex);
230 outWS->setSignalAt(iteratorIndex, sumSignal /
double(nNeighbours + 1));
232 outWS->setErrorSquaredAt(iteratorIndex, sumSqError /
double(nNeighbours + 1));
236 }
while (iterator->next());
258 const bool useWeights = weightingWS.get() != 0;
259 uint64_t nPoints = toSmooth->getNPoints();
266 progress.reportIncrement(
size_t(
double(nPoints) * 0.1));
269 std::vector<KernelVector> gaussian_kernels;
270 gaussian_kernels.reserve(widthVector.size());
271 std::transform(widthVector.cbegin(), widthVector.cend(), std::back_inserter(gaussian_kernels),
272 [](
const auto width) { return gaussianKernel(width); });
276 auto write_ws = tempWS;
277 for (
size_t dimension_number = 0; dimension_number < widthVector.size(); ++dimension_number) {
279 auto iterators = toSmooth->createIterators(nThreads,
nullptr);
283 if (dimension_number % 2 == 0) {
292 for (
int it = 0; it < int(iterators.size()); ++it) {
297 throw std::logic_error(
"Failed to cast IMDIterator to MDHistoWorkspaceIterator");
303 size_t iteratorIndex = iterator->getLinearIndex();
308 if (weightingWS->getSignalAt(iteratorIndex) == 0) {
310 write_ws->setSignalAt(iteratorIndex, std::numeric_limits<double>::quiet_NaN());
311 write_ws->setErrorSquaredAt(iteratorIndex, std::numeric_limits<double>::quiet_NaN());
317 std::pair<std::vector<size_t>, std::vector<bool>> indexesAndValidity = iterator->findNeighbourIndexesByWidth1D(
318 static_cast<int>(gaussian_kernels[dimension_number].size()),
static_cast<int>(dimension_number));
319 std::vector<size_t> neighbourIndexes = std::get<0>(indexesAndValidity);
320 std::vector<bool> indexValidity = std::get<1>(indexesAndValidity);
321 auto normalised_kernel =
renormaliseKernel(gaussian_kernels[dimension_number], indexValidity);
324 double sumSignal = 0;
325 double sumSquareError = 0;
327 for (
size_t i = 0; i < neighbourIndexes.size(); ++i) {
328 if (indexValidity[i]) {
329 sumSignal += read_ws->getSignalAt(neighbourIndexes[i]) * normalised_kernel[i];
330 error = read_ws->getErrorAt(neighbourIndexes[i]) * normalised_kernel[i];
334 write_ws->setSignalAt(iteratorIndex, sumSignal);
335 write_ws->setErrorSquaredAt(iteratorIndex, sumSquareError);
339 }
while (iterator->next());
353 "An input MDHistoWorkspace to smooth.");
355 auto widthVectorValidator = std::make_shared<CompositeValidator>();
356 auto boundedValidator = std::make_shared<ArrayBoundedValidator<double>>(1, 1000);
357 widthVectorValidator->add(boundedValidator);
361 "Width vector. Either specify the width in n-pixels for each "
362 "dimension, or provide a single entry (n-pixels) for all "
363 "dimensions. Must be odd integers if Hat function is chosen.");
365 const std::array<std::string, 2> allFunctionTypes = {{
"Hat",
"Gaussian"}};
366 const std::string first = allFunctionTypes.front();
368 std::stringstream docBuffer;
369 docBuffer <<
"Smoothing function. Defaults to " << first;
375 std::array<std::string, 1> unitOptions = {{
"pixels"}};
377 std::stringstream docUnits;
378 docUnits <<
"The units that WidthVector has been specified in. Allowed "
380 for (
auto const &unitOption : unitOptions) {
381 docUnits << unitOption <<
", ";
389 "Multidimensional weighting workspace. Optional.");
392 "An output smoothed MDHistoWorkspace.");
407 std::vector<double> widthVector = this->
getProperty(
"WidthVector");
408 if (widthVector.size() == 1) {
411 widthVector = std::vector<double>(toSmooth->getNumDims(), widthVector.front());
415 const std::string smoothFunctionName = this->
getProperty(
"Function");
419 auto smoothed = smoothFunction(toSmooth, widthVector, weightingWS);
430 std::map<std::string, std::string> product;
435 std::string function_type = this->
getProperty(
"Function");
438 const std::string widthVectorPropertyName =
"WidthVector";
439 std::vector<double> widthVector = this->
getProperty(widthVectorPropertyName);
441 if (widthVector.size() != 1 && widthVector.size() != toSmoothWs->getNumDims()) {
442 product.emplace(widthVectorPropertyName, widthVectorPropertyName +
" can either have one entry or needs to "
443 "have entries for each dimension of the "
445 }
else if (function_type ==
"Hat") {
447 for (
auto const widthEntry : widthVector) {
449 if (modf(widthEntry, &intpart) != 0.0) {
450 std::stringstream message;
451 message << widthVectorPropertyName
452 <<
" entries must be (odd) integers "
453 "when Hat function is chosen. "
456 product.emplace(widthVectorPropertyName, message.str());
457 }
else if (
static_cast<unsigned long>(widthEntry) % 2 == 0) {
458 std::stringstream message;
459 message << widthVectorPropertyName
460 <<
" entries must be odd integers "
461 "when Hat function is chosen. "
469 const std::string normalisationWorkspacePropertyName =
"InputNormalizationWorkspace";
473 const size_t nDimsNorm = normWs->getNumDims();
474 const size_t nDimsSmooth = toSmoothWs->getNumDims();
475 if (nDimsNorm != nDimsSmooth) {
476 std::stringstream message;
477 message << normalisationWorkspacePropertyName
478 <<
" has a different number of dimensions than InputWorkspace. "
479 "Shapes of inputs must be the same. Cannot continue "
481 product.insert(std::make_pair(normalisationWorkspacePropertyName, message.str()));
484 for (
size_t i = 0; i < nDimsNorm; ++i) {
485 const size_t nBinsNorm = normWs->getDimension(i)->getNBins();
486 const size_t nBinsSmooth = toSmoothWs->getDimension(i)->getNBins();
487 if (nBinsNorm != nBinsSmooth) {
488 std::stringstream message;
489 message << normalisationWorkspacePropertyName <<
". Number of bins from dimension with index " << i
490 <<
" do not match. " << nBinsSmooth <<
" expected. Got " << nBinsNorm
491 <<
". Shapes of inputs must be the same. Cannot "
492 "continue smoothing.";
493 product.emplace(normalisationWorkspacePropertyName, message.str());
#define DECLARE_ALGORITHM(classname)
double value
The value of the point.
#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_FOR_NO_WSP_CHECK()
#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_CHECK_INTERRUPT_REGION
Adds a check after a Parallel region to see if it was interupted.
std::vector< double > KernelVector
boost::optional< IMDHistoWorkspace_const_sptr > OptionalIMDHistoWorkspace_const_sptr
std::map< std::string, SmoothFunction > SmoothFunctionMap
std::function< IMDHistoWorkspace_sptr(IMDHistoWorkspace_const_sptr, const WidthVector &, IMDHistoWorkspace_sptr)> SmoothFunction
std::vector< double > WidthVector
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
void progress(double p, const std::string &msg="", double estimatedTime=0.0, int progressPrecision=0)
Sends ProgressNotification.
Helper class for reporting progress from algorithms.
A property class for workspaces.
An implementation of IMDIterator that iterates through a MDHistoWorkspace.
Support for a property that holds an array of values.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
ListValidator is a validator that requires the value of a property to be one of a defined list of pos...
Validator to check that a property is not left empty.
The concrete, templated class for properties.
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
SmoothMD : Algorithm for smoothing MDHistoWorkspaces.
const std::string category() const override
Algorithm's category for identification.
const std::string summary() const override
Algorithm's summary for use in the GUI and help.
std::shared_ptr< Mantid::API::IMDHistoWorkspace > gaussianSmooth(const std::shared_ptr< const Mantid::API::IMDHistoWorkspace > &toSmooth, const std::vector< double > &widthVector, const std::shared_ptr< Mantid::API::IMDHistoWorkspace > &weightingWS)
Gaussian function smoothing.
int version() const override
Algorithm's version for identification.
void exec() override
Execute the algorithm.
std::map< std::string, std::string > validateInputs() override
validateInputs
std::shared_ptr< Mantid::API::IMDHistoWorkspace > hatSmooth(const std::shared_ptr< const Mantid::API::IMDHistoWorkspace > &toSmooth, const std::vector< double > &widthVector, const std::shared_ptr< Mantid::API::IMDHistoWorkspace > &weightingWS)
Hat function smoothing.
void init() override
Initialize the algorithm's properties.
std::shared_ptr< IMDHistoWorkspace > IMDHistoWorkspace_sptr
shared pointer to Mantid::API::IMDHistoWorkspace
std::shared_ptr< const IMDHistoWorkspace > IMDHistoWorkspace_const_sptr
shared pointer to Mantid::API::IMDHistoWorkspace (const version)
DLLExport std::vector< double > gaussianKernel(const double fwhm)
DLLExport std::vector< double > renormaliseKernel(std::vector< double > kernel, const std::vector< bool > &validity)
DLLExport std::vector< double > normaliseKernel(std::vector< double > kernel)
@ Input
An input workspace.
@ Output
An output workspace.