From ca696540ea264d96b282ed7db8aa844c9c8009d1 Mon Sep 17 00:00:00 2001 From: Owen Arnold <owen.arnold@stfc.ac.uk> Date: Tue, 31 Mar 2015 10:30:12 +0100 Subject: [PATCH] refs #11393. Normalization workspace option. --- .../inc/MantidMDAlgorithms/SmoothMD.h | 3 +- .../Framework/MDAlgorithms/src/SmoothMD.cpp | 100 ++++- .../MDAlgorithms/test/SmoothMDTest.h | 403 +++++++++++------- .../docs/source/algorithms/SmoothMD-v1.rst | 2 + 4 files changed, 355 insertions(+), 153 deletions(-) diff --git a/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/SmoothMD.h b/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/SmoothMD.h index 987d303b006..61c447729a3 100644 --- a/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/SmoothMD.h +++ b/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/SmoothMD.h @@ -4,6 +4,7 @@ #include "MantidKernel/System.h" #include "MantidAPI/Algorithm.h" #include <boost/shared_ptr.hpp> +#include <boost/optional.hpp> namespace Mantid { @@ -49,7 +50,7 @@ namespace MDAlgorithms std::map<std::string, std::string> validateInputs(); boost::shared_ptr<Mantid::API::IMDHistoWorkspace> hatSmooth(boost::shared_ptr<const Mantid::API::IMDHistoWorkspace> toSmooth, - const std::vector<int> &widthVector); + const std::vector<int> &widthVector, boost::optional<boost::shared_ptr<const Mantid::API::IMDHistoWorkspace> > weighting); private: diff --git a/Code/Mantid/Framework/MDAlgorithms/src/SmoothMD.cpp b/Code/Mantid/Framework/MDAlgorithms/src/SmoothMD.cpp index 3f7b1dd010a..8d7fcbc8123 100644 --- a/Code/Mantid/Framework/MDAlgorithms/src/SmoothMD.cpp +++ b/Code/Mantid/Framework/MDAlgorithms/src/SmoothMD.cpp @@ -17,6 +17,7 @@ #include <string> #include <sstream> #include <utility> +#include <limits> #include <boost/function.hpp> #include <boost/bind.hpp> #include <boost/scoped_ptr.hpp> @@ -26,9 +27,17 @@ using namespace Mantid::Kernel; using namespace Mantid::API; using namespace Mantid::MDEvents; +// Typedef for with vector typedef std::vector<int> WidthVector; + +// Typedef for an optional md histo workspace +typedef boost::optional<IMDHistoWorkspace_const_sptr> OptionalIMDHistoWorkspace_const_sptr; + +// Typedef for a smoothing function typedef boost::function<IMDHistoWorkspace_sptr( - IMDHistoWorkspace_const_sptr, const WidthVector &)> SmoothFunction; + IMDHistoWorkspace_const_sptr, const WidthVector &, OptionalIMDHistoWorkspace_const_sptr)> SmoothFunction; + +// Typedef for a smoothing function map keyed by name. typedef std::map<std::string, SmoothFunction> SmoothFunctionMap; namespace { @@ -50,7 +59,7 @@ std::vector<std::string> functions() { */ SmoothFunctionMap makeFunctionMap(Mantid::MDAlgorithms::SmoothMD * instance) { SmoothFunctionMap map; - map.insert(std::make_pair("Hat", boost::bind( &Mantid::MDAlgorithms::SmoothMD::hatSmooth, instance, _1, _2 ))); + map.insert(std::make_pair("Hat", boost::bind( &Mantid::MDAlgorithms::SmoothMD::hatSmooth, instance, _1, _2, _3 ))); return map; } @@ -93,11 +102,13 @@ const std::string SmoothMD::summary() const { * width. * @param toSmooth : Workspace to smooth * @param widthVector : Width vector + * @param weightingWS : Weighting workspace (optional) * @return Smoothed MDHistoWorkspace */ IMDHistoWorkspace_sptr SmoothMD::hatSmooth(IMDHistoWorkspace_const_sptr toSmooth, - const WidthVector &widthVector) { + const WidthVector &widthVector, OptionalIMDHistoWorkspace_const_sptr weightingWS) { + const bool useWeights = weightingWS.is_initialized(); uint64_t nPoints = toSmooth->getNPoints(); Progress progress(this, 0, 1, size_t( double(nPoints) * 1.1 ) ); // Create the output workspace. @@ -118,31 +129,53 @@ IMDHistoWorkspace_sptr SmoothMD::hatSmooth(IMDHistoWorkspace_const_sptr toSmooth do { // Gets all vertex-touching neighbours + size_t iteratorIndex = iterator->getLinearIndex(); + + if(useWeights) { + + // Check that we could measuer here. + if ( (*weightingWS)->getSignalAt(iteratorIndex) == 0 ) { + + outWS->setSignalAt(iteratorIndex, std::numeric_limits<double>::quiet_NaN()); + + outWS->setErrorSquaredAt(iteratorIndex, std::numeric_limits<double>::quiet_NaN()); + + continue; // Skip we couldn't measure here. + } + } std::vector<size_t> neighbourIndexes = iterator->findNeighbourIndexesByWidth( widthVector.front()); // TODO we should use the whole width vector // not just the first element. - const size_t nNeighbours = neighbourIndexes.size(); + size_t nNeighbours = neighbourIndexes.size(); double sumSignal = iterator->getSignal(); double sumSqError = iterator->getError(); for (size_t i = 0; i < neighbourIndexes.size(); ++i) { + if(useWeights) { + if ( (*weightingWS)->getSignalAt(neighbourIndexes[i]) == 0 ) + { + // Nothing measured here. We cannot use that neighbouring point. + nNeighbours -= 1; + continue; + } + } sumSignal += toSmooth->getSignalAt(neighbourIndexes[i]); double error = toSmooth->getErrorAt(neighbourIndexes[i]); sumSqError += (error * error); + } // Calculate the mean - outWS->setSignalAt(iterator->getLinearIndex(), + outWS->setSignalAt(iteratorIndex, sumSignal / double(nNeighbours + 1)); // Calculate the sample variance - outWS->setErrorSquaredAt(iterator->getLinearIndex(), + outWS->setErrorSquaredAt(iteratorIndex, sumSqError / double(nNeighbours + 1)); progress.report(); - } while (iterator->next()); PARALLEL_END_INTERUPT_REGION } @@ -185,6 +218,10 @@ void SmoothMD::init() { Direction::Input), docBuffer.str()); + declareProperty(new WorkspaceProperty<API::IMDHistoWorkspace>( + "InputNormalizationWorkspace", "", Direction::Input, PropertyMode::Optional), + "Multidimensional weighting workspace. Optional."); + declareProperty(new WorkspaceProperty<API::IMDHistoWorkspace>( "OutputWorkspace", "", Direction::Output), "An output smoothed MDHistoWorkspace."); @@ -197,6 +234,15 @@ void SmoothMD::exec() { // Get the input workspace to smooth IMDHistoWorkspace_sptr toSmooth = this->getProperty("InputWorkspace"); + + // Get the input weighting workspace + IMDHistoWorkspace_sptr weightingWS = this->getProperty("InputNormalizationWorkspace"); + OptionalIMDHistoWorkspace_const_sptr optionalWeightingWS; + if(weightingWS) + { + optionalWeightingWS = weightingWS; + } + // Get the width vector const std::vector<int> widthVector = this->getProperty("WidthVector"); @@ -205,7 +251,7 @@ void SmoothMD::exec() { SmoothFunctionMap functionMap = makeFunctionMap(this); SmoothFunction smoothFunction = functionMap[smoothFunctionName]; // invoke the smoothing operation - auto smoothed = smoothFunction(toSmooth, widthVector); + auto smoothed = smoothFunction(toSmooth, widthVector, optionalWeightingWS); setProperty("OutputWorkspace", smoothed); } @@ -218,6 +264,7 @@ std::map<std::string, std::string> SmoothMD::validateInputs() { std::map<std::string, std::string> product; + // Check the width vector const std::string widthVectorPropertyName = "WidthVector"; std::vector<int> widthVector = this->getProperty(widthVectorPropertyName); for (auto it = widthVector.begin(); it != widthVector.end(); ++it) { @@ -229,6 +276,43 @@ std::map<std::string, std::string> SmoothMD::validateInputs() { product.insert(std::make_pair(widthVectorPropertyName, message.str())); } } + + // Check the dimensionality of the normalization workspace + const std::string normalisationWorkspacePropertyName = "InputNormalizationWorkspace"; + IMDHistoWorkspace_sptr toSmoothWs = this->getProperty("InputWorkspace"); + IMDHistoWorkspace_sptr normWs = this->getProperty(normalisationWorkspacePropertyName); + if(normWs) + { + const size_t nDimsNorm = normWs->getNumDims(); + const size_t nDimsSmooth = toSmoothWs->getNumDims(); + if(nDimsNorm != nDimsSmooth) { + std::stringstream message; + message << normalisationWorkspacePropertyName + << " has a different number of dimensions than InputWorkspace. Shapes of inputs must be the same. Cannot continue smoothing."; + product.insert(std::make_pair(normalisationWorkspacePropertyName, message.str())); + } + else + { + // Loop over dimensions and check nbins. + for(size_t i = 0; i < nDimsNorm; ++i) + { + const size_t nBinsNorm = normWs->getDimension(i)->getNBins(); + const size_t nBinsSmooth = toSmoothWs->getDimension(i)->getNBins(); + if(nBinsNorm != nBinsSmooth) + { + std::stringstream message; + message << normalisationWorkspacePropertyName + << ". Number of bins from dimension with index "<< i << " do not match. " + << nBinsSmooth << " expected. Got " << nBinsNorm + << ". Shapes of inputs must be the same. Cannot continue smoothing."; + product.insert(std::make_pair(normalisationWorkspacePropertyName, message.str())); + break; + } + } + } + } + + return product; } diff --git a/Code/Mantid/Framework/MDAlgorithms/test/SmoothMDTest.h b/Code/Mantid/Framework/MDAlgorithms/test/SmoothMDTest.h index 4bebbc28ebd..dd00e34212b 100644 --- a/Code/Mantid/Framework/MDAlgorithms/test/SmoothMDTest.h +++ b/Code/Mantid/Framework/MDAlgorithms/test/SmoothMDTest.h @@ -7,7 +7,7 @@ #include "MantidTestHelpers/MDEventsTestHelper.h" #include "MantidAPI/IMDHistoWorkspace.h" #include <vector> - +#include <boost/math/special_functions/fpclassify.hpp> using Mantid::MDAlgorithms::SmoothMD; using namespace Mantid::API; @@ -26,196 +26,299 @@ public: } void test_function_is_of_right_type() { - SmoothMD alg; - alg.initialize(); - TSM_ASSERT_THROWS("Function can only be of known types for SmoothMD", alg.setProperty("Function", "magic_function"), std::invalid_argument&); + SmoothMD alg; + alg.initialize(); + TSM_ASSERT_THROWS("Function can only be of known types for SmoothMD", + alg.setProperty("Function", "magic_function"), + std::invalid_argument &); } - void test_reject_negative_width_vector_entry() - { - SmoothMD alg; - alg.initialize(); - TSM_ASSERT_THROWS("N-pixels contains zero", alg.setProperty("WidthVector", std::vector<int>(1, 0)), std::invalid_argument&); + void test_reject_negative_width_vector_entry() { + SmoothMD alg; + alg.initialize(); + TSM_ASSERT_THROWS("N-pixels contains zero", + alg.setProperty("WidthVector", std::vector<int>(1, 0)), + std::invalid_argument &); } - void test_mandatory_width_vector_entry() - { - SmoothMD alg; - alg.initialize(); - TSM_ASSERT_THROWS("Empty WidthVector", alg.setProperty("WidthVector", std::vector<int>()), std::invalid_argument&); + void test_mandatory_width_vector_entry() { + SmoothMD alg; + alg.initialize(); + TSM_ASSERT_THROWS("Empty WidthVector", + alg.setProperty("WidthVector", std::vector<int>()), + std::invalid_argument &); } - void test_width_entry_must_be_odd() - { - auto toSmooth = MDEventsTestHelper::makeFakeMDHistoWorkspace(1 /*signal*/, 1 /*numDims*/, 4 /*numBins in each dimension*/); + void test_width_entry_must_be_odd() { + auto toSmooth = MDEventsTestHelper::makeFakeMDHistoWorkspace( + 1 /*signal*/, 1 /*numDims*/, 4 /*numBins in each dimension*/); - SmoothMD alg; - alg.setChild(true); - alg.initialize(); - alg.setPropertyValue("OutputWorkspace", "dummy"); - alg.setProperty("InputWorkspace", toSmooth); - alg.setProperty("WidthVector", std::vector<int>(1, 4)); // Width vector contains even number == 4 - TSM_ASSERT_THROWS("One bad entry. Should throw.", alg.execute(), std::runtime_error&); + SmoothMD alg; + alg.setChild(true); + alg.initialize(); + alg.setPropertyValue("OutputWorkspace", "dummy"); + alg.setProperty("InputWorkspace", toSmooth); + alg.setProperty( + "WidthVector", + std::vector<int>(1, 4)); // Width vector contains even number == 4 + TSM_ASSERT_THROWS("One bad entry. Should throw.", alg.execute(), + std::runtime_error &); + + std::vector<int> widthVector; + widthVector.push_back(3); // OK + widthVector.push_back(5); // OK + widthVector.push_back(2); // Not OK + + alg.setProperty("WidthVector", + widthVector); // Width vector contains even number + TSM_ASSERT_THROWS("Some good entries, but should still throw", + alg.execute(), std::runtime_error &); + } + + void test_simple_smooth_hat_function() { + auto toSmooth = MDEventsTestHelper::makeFakeMDHistoWorkspace( + 1 /*signal*/, 2 /*numDims*/, 3 /*numBins in each dimension*/); + + /* + 2D MDHistoWorkspace Input + + 1 - 1 - 1 + 1 - 1 - 1 + 1 - 1 - 1 + */ - std::vector<int> widthVector; - widthVector.push_back(3); // OK - widthVector.push_back(5); // OK - widthVector.push_back(2); // Not OK + SmoothMD alg; + alg.setChild(true); + alg.initialize(); + std::vector<int> widthVector(1, 3); + alg.setProperty("WidthVector", widthVector); + alg.setProperty("InputWorkspace", toSmooth); + alg.setPropertyValue("OutputWorkspace", "dummy"); + alg.execute(); + IMDHistoWorkspace_sptr out = alg.getProperty("OutputWorkspace"); - alg.setProperty("WidthVector", widthVector); // Width vector contains even number - TSM_ASSERT_THROWS("Some good entries, but should still throw", alg.execute(), std::runtime_error&); + /* + 2D MDHistoWorkspace Expected + + 1 - 1 - 1 + 1 - 1 - 1 + 1 - 1 - 1 + */ + for (size_t i = 0; i < out->getNPoints(); ++i) { + TS_ASSERT_EQUALS(1, out->getSignalAt(i)); + TS_ASSERT_EQUALS(1, out->getErrorAt(i)); + } } - void test_simple_smooth_hat_function() - { - auto toSmooth = MDEventsTestHelper::makeFakeMDHistoWorkspace(1 /*signal*/, 2 /*numDims*/, 3 /*numBins in each dimension*/); + void test_smooth_hat_function_3_pix_width() { + auto toSmooth = MDEventsTestHelper::makeFakeMDHistoWorkspace( + 1 /*signal*/, 2 /*numDims*/, 3 /*numBins in each dimension*/); + toSmooth->setSignalAt(4, 2.0); - /* - 2D MDHistoWorkspace Input + /* + 2D MDHistoWorkspace Input - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - */ + 1 - 1 - 1 + 1 - 2 - 1 + 1 - 1 - 1 + */ - SmoothMD alg; - alg.setChild(true); - alg.initialize(); - std::vector<int> widthVector(1, 3); - alg.setProperty("WidthVector", widthVector); - alg.setProperty("InputWorkspace", toSmooth); - alg.setPropertyValue("OutputWorkspace", "dummy"); - alg.execute(); - IMDHistoWorkspace_sptr out = alg.getProperty("OutputWorkspace"); - - /* - 2D MDHistoWorkspace Expected - - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - */ - for(size_t i = 0; i < out->getNPoints(); ++i) - { - TS_ASSERT_EQUALS(1, out->getSignalAt(i)); - TS_ASSERT_EQUALS(1, out->getErrorAt(i)); - } + SmoothMD alg; + alg.setChild(true); + alg.initialize(); + std::vector<int> widthVector(1, 3); + alg.setProperty("WidthVector", widthVector); + alg.setProperty("InputWorkspace", toSmooth); + alg.setPropertyValue("OutputWorkspace", "dummy"); + alg.execute(); + IMDHistoWorkspace_sptr out = alg.getProperty("OutputWorkspace"); + + /* + 2D MDHistoWorkspace Expected + + 5/4 - 7/6 - 5/4 + 7/6 - 10/9 - 7/6 + 5/4 - 7/6 - 5/4 + */ + + TS_ASSERT_EQUALS(5.0 / 4, out->getSignalAt(0)); + TS_ASSERT_EQUALS(7.0 / 6, out->getSignalAt(1)); + TS_ASSERT_EQUALS(10.0 / 9, out->getSignalAt(4)); + } + + void test_smooth_hat_function_5_pix_width() { + auto toSmooth = MDEventsTestHelper::makeFakeMDHistoWorkspace( + 1 /*signal*/, 2 /*numDims*/, 5 /*numBins in each dimension*/); + toSmooth->setSignalAt(12, 4.0); + + /* + 2D MDHistoWorkspace Input + 1 - 1 - 1 - 1 - 1 + 1 - 1 - 1 - 1 - 1 + 1 - 1 - 4 - 1 - 1 + 1 - 1 - 1 - 1 - 1 + 1 - 1 - 1 - 1 - 1 + + */ + + SmoothMD alg; + alg.setChild(true); + alg.initialize(); + std::vector<int> widthVector(1, 5); // Smooth with width == 5 + alg.setProperty("WidthVector", widthVector); + alg.setProperty("InputWorkspace", toSmooth); + alg.setPropertyValue("OutputWorkspace", "dummy"); + alg.execute(); + IMDHistoWorkspace_sptr out = alg.getProperty("OutputWorkspace"); + + /* + 2D MDHistoWorkspace Expected + + key: + x = 12/9 + y = 18/15 + z = 28/25 + ` = ignore + + x - ` - y - ` - x + ` - ` - ` - ` - ` + y - ` - z - ` - y + ` - ` - ` - ` - ` + x - ` - y - ` - x + */ + + // Check vertexes + double x = 12.0 / 9; + TS_ASSERT_EQUALS(x, out->getSignalAt(0)); + TS_ASSERT_EQUALS(x, out->getSignalAt(4)); + TS_ASSERT_EQUALS(x, out->getSignalAt(20)); + TS_ASSERT_EQUALS(x, out->getSignalAt(24)); + + // Check edges + double y = 18.0 / 15; + TS_ASSERT_EQUALS(y, out->getSignalAt(2)); + TS_ASSERT_EQUALS(y, out->getSignalAt(10)); + TS_ASSERT_EQUALS(y, out->getSignalAt(14)); + TS_ASSERT_EQUALS(y, out->getSignalAt(22)); + + // Check centre + double z = 28.0 / 25; + TS_ASSERT_EQUALS(z, out->getSignalAt(12)); } - void test_smooth_hat_function_3_pix_width() - { - auto toSmooth = MDEventsTestHelper::makeFakeMDHistoWorkspace(1 /*signal*/, 2 /*numDims*/, 3 /*numBins in each dimension*/); - toSmooth->setSignalAt(4, 2.0); + void test_dimensional_check_of_weight_ws() { - /* - 2D MDHistoWorkspace Input + MDHistoWorkspace_sptr a = + MDEventsTestHelper::makeFakeMDHistoWorkspace(1.0 /*signal value*/, 1 /*dimensionality*/, + 9); - 1 - 1 - 1 - 1 - 2 - 1 - 1 - 1 - 1 - */ + MDHistoWorkspace_sptr b = MDEventsTestHelper::makeFakeMDHistoWorkspace( + 1.0 /*signal value*/, 2 /*dimensionality*/, 9); // one dimension larger SmoothMD alg; alg.setChild(true); alg.initialize(); - std::vector<int> widthVector(1, 3); + std::vector<int> widthVector(1, 3); // Smooth with width == 3 alg.setProperty("WidthVector", widthVector); - alg.setProperty("InputWorkspace", toSmooth); + alg.setProperty("InputWorkspace", a); + alg.setProperty("InputNormalizationWorkspace", b); alg.setPropertyValue("OutputWorkspace", "dummy"); - alg.execute(); - IMDHistoWorkspace_sptr out = alg.getProperty("OutputWorkspace"); - - /* - 2D MDHistoWorkspace Expected - 5/4 - 7/6 - 5/4 - 7/6 - 10/9 - 7/6 - 5/4 - 7/6 - 5/4 - */ + TSM_ASSERT_THROWS("Input unsmoothed and input Normalisation workspaces must have the same dimensionality", + alg.execute(), std::runtime_error &); - TS_ASSERT_EQUALS(5.0/4, out->getSignalAt(0)); - TS_ASSERT_EQUALS(7.0/6, out->getSignalAt(1)); - TS_ASSERT_EQUALS(10.0/9, out->getSignalAt(4)); } - void test_smooth_hat_function_5_pix_width() - { - auto toSmooth = MDEventsTestHelper::makeFakeMDHistoWorkspace(1 /*signal*/, 2 /*numDims*/, 5 /*numBins in each dimension*/); - toSmooth->setSignalAt(12, 4.0); + void test_shape_check_of_weight_ws() { - /* - 2D MDHistoWorkspace Input + const size_t nd = 1; - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 4 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 + MDHistoWorkspace_sptr a = + MDEventsTestHelper::makeFakeMDHistoWorkspace(1.0 /*signal value*/, nd, + 10); - */ + MDHistoWorkspace_sptr b = MDEventsTestHelper::makeFakeMDHistoWorkspace( + 1.0 /*signal value*/, nd, 10 + 1); // one bin longer SmoothMD alg; alg.setChild(true); alg.initialize(); - std::vector<int> widthVector(1, 5); // Smooth with width == 5 + std::vector<int> widthVector(1, 3); // Smooth with width == 3 alg.setProperty("WidthVector", widthVector); - alg.setProperty("InputWorkspace", toSmooth); + alg.setProperty("InputWorkspace", a); + alg.setProperty("InputNormalizationWorkspace", b); alg.setPropertyValue("OutputWorkspace", "dummy"); - alg.execute(); - IMDHistoWorkspace_sptr out = alg.getProperty("OutputWorkspace"); - - /* - 2D MDHistoWorkspace Expected - - key: - x = 12/9 - y = 18/15 - z = 28/25 - ` = ignore - - x - ` - y - ` - x - ` - ` - ` - ` - ` - y - ` - z - ` - y - ` - ` - ` - ` - ` - x - ` - y - ` - x - */ - - // Check vertexes - double x = 12.0/9; - TS_ASSERT_EQUALS(x, out->getSignalAt(0)); - TS_ASSERT_EQUALS(x, out->getSignalAt(4)); - TS_ASSERT_EQUALS(x, out->getSignalAt(20)); - TS_ASSERT_EQUALS(x, out->getSignalAt(24)); - - // Check edges - double y = 18.0/15; - TS_ASSERT_EQUALS(y, out->getSignalAt(2)); - TS_ASSERT_EQUALS(y, out->getSignalAt(10)); - TS_ASSERT_EQUALS(y, out->getSignalAt(14)); - TS_ASSERT_EQUALS(y, out->getSignalAt(22)); - - // Check centre - double z = 28.0/25; - TS_ASSERT_EQUALS(z, out->getSignalAt(12)); + + TSM_ASSERT_THROWS("Input unsmoothed and input Normalisation workspaces must have the same shape", + alg.execute(), std::runtime_error &); + } -}; + void test_smooth_with_normalization_guidance() { + + const size_t nd = 1; + MDHistoWorkspace_sptr toSmooth = + MDEventsTestHelper::makeFakeMDHistoWorkspace(2.0 /*signal value*/, nd, + 10); + toSmooth->setSignalAt(7, 3); + + MDHistoWorkspace_sptr normWs = MDEventsTestHelper::makeFakeMDHistoWorkspace( + 1.0 /*signal value*/, nd, 10); + normWs->setSignalAt(9, 0); + + /* + 1D MDHistoWorkspace for normalization + + 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 0 + + 1D MDHistoWorkspace for smoothing + + 2 - 2 - 2 - 2 - 2 - 2 - 2 - 3 - 2 - 2 + */ + + SmoothMD alg; + alg.setChild(true); + alg.initialize(); + std::vector<int> widthVector(1, 3); // Smooth with width == 3 + alg.setProperty("WidthVector", widthVector); + alg.setProperty("InputWorkspace", toSmooth); + alg.setProperty("InputNormalizationWorkspace", normWs); + alg.setPropertyValue("OutputWorkspace", "dummy"); + alg.execute(); + IMDHistoWorkspace_sptr out = alg.getProperty("OutputWorkspace"); + + TSM_ASSERT_EQUALS("Second index should have a smoothed using 2 " + "neighbours nothing ignored", + (toSmooth->getSignalAt(0) + toSmooth->getSignalAt(1) + toSmooth->getSignalAt(2)) / 3, + out->getSignalAt(1)); + + + TSM_ASSERT_EQUALS("Second to last index should have a smoothed using 1 " + "neighbour only neighour at 9 should be ignored", + (toSmooth->getSignalAt(8) + toSmooth->getSignalAt(7)) / 2, + out->getSignalAt(8)); + + TSM_ASSERT("Last index should have a smoothed Value of NaN", + boost::math::isnan(out->getSignalAt(9))); + } +}; class SmoothMDTestPerformance : public CxxTest::TestSuite { private: - - IMDHistoWorkspace_sptr m_toSmooth; + IMDHistoWorkspace_sptr m_toSmooth; public: // This pair of boilerplate methods prevent the suite being created statically // This means the constructor isn't called when running other tests - static SmoothMDTestPerformance *createSuite() { return new SmoothMDTestPerformance(); } + static SmoothMDTestPerformance *createSuite() { + return new SmoothMDTestPerformance(); + } static void destroySuite(SmoothMDTestPerformance *suite) { delete suite; } - SmoothMDTestPerformance() - { - m_toSmooth = MDEventsTestHelper::makeFakeMDHistoWorkspace(1 /*signal*/, 2 /*numDims*/, 500 /*numBins in each dimension*/); + SmoothMDTestPerformance() { + m_toSmooth = MDEventsTestHelper::makeFakeMDHistoWorkspace( + 1 /*signal*/, 2 /*numDims*/, 500 /*numBins in each dimension*/); } void test_execute_hat_function() { @@ -230,8 +333,20 @@ public: IMDHistoWorkspace_sptr out = alg.getProperty("OutputWorkspace"); TS_ASSERT(out); } -}; - + void test_execute_hat_function_with_normalisation() { + SmoothMD alg; + alg.setChild(true); + alg.initialize(); + std::vector<int> widthVector(1, 3); // Smooth with width == 3 + alg.setProperty("WidthVector", widthVector); + alg.setProperty("InputWorkspace", m_toSmooth); + alg.setProperty("InputNormalizationWorkspace", m_toSmooth); + alg.setPropertyValue("OutputWorkspace", "dummy"); + alg.execute(); + IMDHistoWorkspace_sptr out = alg.getProperty("OutputWorkspace"); + TS_ASSERT(out); + } +}; #endif /* MANTID_MDALGORITHMS_SMOOTHMDTEST_H_ */ diff --git a/Code/Mantid/docs/source/algorithms/SmoothMD-v1.rst b/Code/Mantid/docs/source/algorithms/SmoothMD-v1.rst index e6f4ea2c1c5..f2d284d7ce9 100644 --- a/Code/Mantid/docs/source/algorithms/SmoothMD-v1.rst +++ b/Code/Mantid/docs/source/algorithms/SmoothMD-v1.rst @@ -12,6 +12,8 @@ Description Provides smoothing of :ref:`MDHistoWorkspace <MDHistoWorkspace>` in n-dimensions. The WidthVector relates to the number of pixels to include in the width for each dimension. *WidthVector* **must contain entries that are odd numbers**. +A *InputNormalizationWorkspace* may optionally be provided. Such workspaces must have exactly the same shape as the *InputWorkspace*. Where the signal values from this workspace are zero, the corresponding smoothed value will be NaN. Any un-smoothed values from the *InputWorkspace* corresponding to zero in the *InputNormalizationWorkspace* will be ignored during neighbour calculations, so effectively omitted from the smoothing altogether. + .. figure:: /images/PreSmooth.png :alt: PreSmooth.png :width: 400px -- GitLab