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