diff --git a/Code/Mantid/Framework/DataObjects/inc/MantidDataObjects/MDHistoWorkspaceIterator.h b/Code/Mantid/Framework/DataObjects/inc/MantidDataObjects/MDHistoWorkspaceIterator.h
index 4eae0c223115210a887c3023af9a217476b9237f..5a914cbb28f10b974934fbdf3e2352fc8f2c0e46 100644
--- a/Code/Mantid/Framework/DataObjects/inc/MantidDataObjects/MDHistoWorkspaceIterator.h
+++ b/Code/Mantid/Framework/DataObjects/inc/MantidDataObjects/MDHistoWorkspaceIterator.h
@@ -6,10 +6,15 @@
 #include "MantidDataObjects/MDHistoWorkspace.h"
 #include "MantidGeometry/MDGeometry/MDImplicitFunction.h"
 #include "MantidDataObjects/SkippingPolicy.h"
+#include <map>
+#include <vector>
 
 namespace Mantid {
 namespace DataObjects {
 
+// Typdef for a map for mapping width of neighbours (key) to permutations needed in the calcualtion.
+typedef std::map<std::vector<int>, std::vector<int64_t> > PermutationsMap;
+
 /** An implementation of IMDIterator that iterates through
   a MDHistoWorkspace. It treats the bin in the workspace as
   a box containing a single "event", at the center of each bin
@@ -109,8 +114,14 @@ public:
 
   std::vector<size_t> findNeighbourIndexesFaceTouching() const;
 
+  std::vector<size_t> findNeighbourIndexesByWidth(const int& width) const;
+
+  std::vector<size_t> findNeighbourIndexesByWidth(const std::vector<int>& widths) const;
+
   virtual bool isWithinBounds(size_t index) const;
 
+  size_t permutationCacheSize() const;
+
 protected:
   /// The MDHistoWorkspace being iterated.
   const MDHistoWorkspace *m_ws;
@@ -148,12 +159,17 @@ protected:
   /// Array to find indices from linear indices
   size_t *m_indexMaker;
 
-  /// Neighbour finding permutations.
-  mutable std::vector<int64_t> m_permutationsVertexTouching;
+  /// Neigbour finding permutations for face touching neighbours (3 by 3 width).
   mutable std::vector<int64_t> m_permutationsFaceTouching;
 
+  /// Neighbour finding permutations map for vertex touching. Keyed via the width (n-pixels) of neighbours required.
+  mutable PermutationsMap m_permutationsVertexTouchingMap;
+
   /// Skipping policy.
   SkippingPolicy_scptr m_skippingPolicy;
+
+  /// Create or fetch permutations relating to a given neighbour width.
+  std::vector<int64_t> createPermutations(const std::vector<int>& widths) const;
 };
 
 } // namespace DataObjects
diff --git a/Code/Mantid/Framework/DataObjects/src/MDHistoWorkspaceIterator.cpp b/Code/Mantid/Framework/DataObjects/src/MDHistoWorkspaceIterator.cpp
index 0213d77f9be76d9fd52887e0033a19ad5d34d5ed..c30405614890f9203f1efb2565a8265682b7cd2d 100644
--- a/Code/Mantid/Framework/DataObjects/src/MDHistoWorkspaceIterator.cpp
+++ b/Code/Mantid/Framework/DataObjects/src/MDHistoWorkspaceIterator.cpp
@@ -3,6 +3,10 @@
 #include "MantidKernel/VMD.h"
 #include "MantidKernel/Utils.h"
 #include "MantidGeometry/MDGeometry/IMDDimension.h"
+#include <algorithm>
+#include <utility>
+#include <functional>
+#include <numeric>
 
 using namespace Mantid::Kernel;
 using namespace Mantid::API;
@@ -10,15 +14,7 @@ using Mantid::Geometry::IMDDimension_const_sptr;
 
 namespace Mantid {
 namespace DataObjects {
-namespace {
-size_t integerPower(const size_t base, const size_t pow) {
-  size_t result = 1;
-  for (size_t i = 0; i < pow; ++i) {
-    result *= base;
-  }
-  return result;
-}
-}
+
 
 //----------------------------------------------------------------------------------------------
 /** Constructor
@@ -159,39 +155,24 @@ MDHistoWorkspaceIterator::init(const MDHistoWorkspace *workspace,
       next();
   }
 
-  // --- Calculate index permutations for neighbour finding vertex touching ---
-  auto temp = std::vector<int64_t>(integerPower(3, m_nd), 0);
-  m_permutationsVertexTouching.swap(temp);
-
   // --- Calculate index permutations for neighbour finding face touching ---
-  temp = std::vector<int64_t>(2 * m_nd);
+  auto temp = std::vector<int64_t>(2 * m_nd);
   m_permutationsFaceTouching.swap(temp);
 
   int64_t offset = 1;
-  m_permutationsVertexTouching[0] = 0;
-  m_permutationsVertexTouching[1] = 1;
-  m_permutationsVertexTouching[2] = -1;
 
   m_permutationsFaceTouching[0] = -1;
   m_permutationsFaceTouching[1] = 1;
 
   // Figure out what possible indexes deltas to generate indexes that are next
   // to the current one.
-  size_t nVertexTouchingPermutations = 3;
   for (size_t j = 1; j < m_nd; ++j) {
     offset =
         offset * static_cast<int64_t>(m_ws->getDimension(j - 1)->getNBins());
-    size_t counter = nVertexTouchingPermutations;
-    for (size_t k = 0; k < nVertexTouchingPermutations; k += 1, counter += 2) {
-      int64_t newVariant = m_permutationsVertexTouching[k] + offset;
-      m_permutationsVertexTouching[counter] = newVariant;
-      m_permutationsVertexTouching[counter + 1] = (-1 * newVariant);
-    }
 
     m_permutationsFaceTouching[j * 2] = offset;
     m_permutationsFaceTouching[(j * 2) + 1] = -offset;
 
-    nVertexTouchingPermutations *= 3;
   }
 }
 
@@ -412,24 +393,7 @@ size_t MDHistoWorkspaceIterator::getLinearIndex() const { return m_pos; }
  */
 std::vector<size_t> MDHistoWorkspaceIterator::findNeighbourIndexes() const {
 
-  Utils::NestedForLoop::GetIndicesFromLinearIndex(m_nd, m_pos, m_indexMaker,
-                                                  m_indexMax, m_index);
-
-  // Filter out indexes that are are not actually neighbours.
-  std::vector<size_t> neighbourIndexes; // Accumulate neighbour indexes.
-  for (size_t i = 0; i < m_permutationsVertexTouching.size(); ++i) {
-    if (m_permutationsVertexTouching[i] == 0) {
-      continue;
-    }
-
-    size_t neighbour_index = m_pos + m_permutationsVertexTouching[i];
-    if (neighbour_index < m_ws->getNPoints() &&
-        Utils::isNeighbourOfSubject(m_nd, neighbour_index, m_index,
-                                    m_indexMaker, m_indexMax)) {
-      neighbourIndexes.push_back(neighbour_index);
-    }
-  }
-  return neighbourIndexes;
+  return this->findNeighbourIndexesByWidth(3 /*immediate neighbours only*/);
 }
 
 /**
@@ -448,6 +412,7 @@ MDHistoWorkspaceIterator::findNeighbourIndexesFaceTouching() const {
                                                   m_indexMax, m_index);
 
   std::vector<size_t> neighbourIndexes; // Accumulate neighbour indexes.
+  std::vector<int> widths(m_nd, 3); // Face touching width is always 3 in each dimension
   for (size_t i = 0; i < m_permutationsFaceTouching.size(); ++i) {
     if (m_permutationsFaceTouching[i] == 0) {
       continue;
@@ -456,7 +421,7 @@ MDHistoWorkspaceIterator::findNeighbourIndexesFaceTouching() const {
     size_t neighbour_index = m_pos + m_permutationsFaceTouching[i];
     if (neighbour_index < m_ws->getNPoints() &&
         Utils::isNeighbourOfSubject(m_nd, neighbour_index, m_index,
-                                    m_indexMaker, m_indexMax)) {
+                                    m_indexMaker, m_indexMax, widths)) {
       neighbourIndexes.push_back(neighbour_index);
     }
   }
@@ -472,5 +437,133 @@ bool MDHistoWorkspaceIterator::isWithinBounds(size_t index) const {
   return index >= m_begin && index < m_max;
 }
 
+/**
+ * This is to create the permutations needed to operate find neighbours in the
+ *vertex-touching schenarios
+ * Rather than having to fabricate what the possible permutations are each time
+ *the iterator is moved and the method is called,
+ * we can cache the results, and re-use them as the only factors are the and the
+ *dimensionality, the width (n-neighbours).
+ * @param widths : vector of integer widths.
+ * @return index permutations
+ */
+std::vector<int64_t>
+MDHistoWorkspaceIterator::createPermutations(const std::vector<int>& widths) const {
+  // look-up
+  PermutationsMap::iterator it = m_permutationsVertexTouchingMap.find(widths);
+  if (it == m_permutationsVertexTouchingMap.end()) {
+
+    if (widths[0] % 2 == 0) {
+      throw std::invalid_argument("MDHistoWorkspaceIterator::"
+                                  "findNeighbourIndexesByWidth, width must "
+                                  "always be an even number");
+    }
+    if (widths.size() != m_nd) {
+        throw std::invalid_argument("MDHistoWorkspaceIterator::"
+                                    "findNeighbourIndexesByWidth, size of widths must be the same as the number of dimensions.");
+    }
+
+    int64_t offset = 1;
+
+    // Size of block will be width ^ nd
+    std::vector<int64_t> permutationsVertexTouching;
+    // Calculate maximum permutations size.
+    int product = std::accumulate(widths.begin(), widths.end(), 1, std::multiplies<int>());
+    permutationsVertexTouching.reserve(product);
+
+    int centreIndex = widths[0] / 2; // Deliberately truncate to get centre index
+
+    for (int i = 0; i < widths[0]; ++i) {
+      // for width = 3 : -1, 0, 1
+      // for width = 5 : -2, -1, 0, 1, 2
+      permutationsVertexTouching.push_back(centreIndex - i);
+    }
+
+    // Figure out what possible indexes deltas to generate indexes that are next
+    // to the current one.
+    for (size_t j = 1; j < m_nd; ++j) {
+      offset =
+          offset * static_cast<int64_t>(m_ws->getDimension(j - 1)->getNBins());
+
+      size_t nEntries = permutationsVertexTouching.size();
+      for (int k = 1; k <= widths[j] / 2; ++k) {
+        for (size_t m = 0; m < nEntries; m++) {
+          permutationsVertexTouching.push_back((offset * k) +
+                                               permutationsVertexTouching[m]);
+          permutationsVertexTouching.push_back((offset * k * (-1)) +
+                                               permutationsVertexTouching[m]);
+        }
+      }
+    }
+
+    m_permutationsVertexTouchingMap.insert(std::make_pair(widths, permutationsVertexTouching));
+  }
+
+  // In either case, get the result.
+  return m_permutationsVertexTouchingMap[widths];
+}
+
+/**
+ * Find vertex-touching neighbours.
+ *
+ * Expands out the width to make a n-dimensional vector filled with the requested width.
+ *
+ * @param width : Odd number of pixels for all dimensions.
+ * @return collection of indexes.
+ */
+std::vector<size_t>
+MDHistoWorkspaceIterator::findNeighbourIndexesByWidth(const int & width) const {
+
+    return this->findNeighbourIndexesByWidth(std::vector<int>(m_nd, width));
+}
+
+
+/**
+ * Find vertex-touching neighbours.
+ * @param widths : Vector containing odd number of pixels per dimension. Entries match dimensions of iterator.
+ * @return collection of indexes.
+ */
+std::vector<size_t>
+MDHistoWorkspaceIterator::findNeighbourIndexesByWidth(const std::vector<int>& widths) const {
+
+  // Find existing or create required index permutations.
+  std::vector<int64_t> permutationsVertexTouching = createPermutations(widths);
+
+  Utils::NestedForLoop::GetIndicesFromLinearIndex(m_nd, m_pos, m_indexMaker,
+                                                  m_indexMax, m_index);
+
+  // Filter out indexes that are are not actually neighbours.
+  // Accumulate neighbour indexes.
+  std::vector<size_t> neighbourIndexes;
+  for (size_t i = 0; i < permutationsVertexTouching.size(); ++i) {
+    if (permutationsVertexTouching[i] == 0) {
+      continue;
+    }
+
+    size_t neighbour_index = m_pos + permutationsVertexTouching[i];
+    if (neighbour_index < m_ws->getNPoints() &&
+        Utils::isNeighbourOfSubject(m_nd, neighbour_index, m_index,
+                                    m_indexMaker, m_indexMax, widths) ) {
+      neighbourIndexes.push_back(neighbour_index);
+    }
+  }
+
+  // Remove duplicates
+  std::sort(neighbourIndexes.begin(), neighbourIndexes.end());
+  neighbourIndexes.erase(
+      std::unique(neighbourIndexes.begin(), neighbourIndexes.end()),
+      neighbourIndexes.end());
+  return neighbourIndexes;
+}
+
+/**
+ *
+ * @return The size of the permutation cache.
+ */
+size_t MDHistoWorkspaceIterator::permutationCacheSize() const
+{
+    return m_permutationsVertexTouchingMap.size();
+}
+
 } // namespace Mantid
 } // namespace DataObjects
diff --git a/Code/Mantid/Framework/DataObjects/test/MDHistoWorkspaceIteratorTest.h b/Code/Mantid/Framework/DataObjects/test/MDHistoWorkspaceIteratorTest.h
index d8949c9a26ad8f969bbb9a509add03679c30c678..e39b618a81543c6159385c992ad147b8fcfead09 100644
--- a/Code/Mantid/Framework/DataObjects/test/MDHistoWorkspaceIteratorTest.h
+++ b/Code/Mantid/Framework/DataObjects/test/MDHistoWorkspaceIteratorTest.h
@@ -15,6 +15,7 @@
 #include "MantidGeometry/MDGeometry/MDPlane.h"
 #include <boost/assign/list_of.hpp>
 #include <boost/function.hpp>
+#include <boost/bind.hpp>
 
 using namespace Mantid;
 using namespace Mantid::DataObjects;
@@ -279,7 +280,9 @@ public:
         histoIt->getLinearIndex());
   }
 
-  bool doesContainIndex(const std::vector<size_t>& container, const size_t element)
+  //template<typename ContainerType, typename ElementType>
+  template<class ContainerType>
+  bool doesContainIndex(const ContainerType& container, const typename ContainerType::value_type element)
   {
     return std::find(container.begin(), container.end(), element) != container.end();
   }
@@ -298,6 +301,7 @@ public:
     TS_ASSERT(!iterator.isWithinBounds(end));
   }
 
+
   void do_test_neighbours_1d(
       boost::function<std::vector<size_t>(MDHistoWorkspaceIterator*)> findNeighbourMemberFunction)
   {
@@ -321,14 +325,14 @@ public:
     std::vector<size_t> neighbourIndexes = findNeighbourMemberFunction(it);
     TS_ASSERT_EQUALS(1, neighbourIndexes.size());
     // should be on edge
-    TSM_ASSERT( "Neighbour at index 0 is 1", doesContainIndex(neighbourIndexes, 1));
+    TSM_ASSERT( "Neighbour at index 0 is 1", doesContainIndex(neighbourIndexes, size_t(1)));
 
     // Go to intermediate position
     /*
      0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9
-     ^
-     |
-     */
+         ^
+         |
+         */
     it->next();
     neighbourIndexes = findNeighbourMemberFunction(it);
     TS_ASSERT_EQUALS(2, neighbourIndexes.size());
@@ -339,9 +343,9 @@ public:
     // Go to last position
     /*
      0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9
-     ^
-     |
-     */
+                                         ^
+                                         |
+                                         */
     it->jumpTo(9);
     neighbourIndexes = findNeighbourMemberFunction(it);
     TSM_ASSERT( "Neighbour at index 9 is 8", doesContainIndex(neighbourIndexes, 8));
@@ -671,6 +675,294 @@ public:
 
   }
 
+  void test_neighbours_1d_with_width()
+  {
+
+      // This is the width to use
+      const int width = 5;
+
+      const size_t nd = 1;
+      MDHistoWorkspace_sptr ws = MDEventsTestHelper::makeFakeMDHistoWorkspace(1.0, nd, 10);
+      /*
+       1D MDHistoWorkspace
+
+       0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9
+
+       */
+
+      MDHistoWorkspaceIterator * it = new MDHistoWorkspaceIterator(ws);
+
+      // At first position
+      /*
+       0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9
+       ^
+       |
+       */
+
+      std::vector<size_t> neighbourIndexes = it->findNeighbourIndexesByWidth(width);
+      TS_ASSERT_EQUALS(2, neighbourIndexes.size());
+      // should be on edge
+      TSM_ASSERT( "Neighbours at index 0 includes 1", doesContainIndex(neighbourIndexes, 1));
+      TSM_ASSERT( "Neighbours at index 0 includes 2", doesContainIndex(neighbourIndexes, 1));
+
+      // Go to intermediate position
+      /*
+       0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9
+           ^
+           |
+           */
+      it->next();
+      neighbourIndexes = it->findNeighbourIndexesByWidth(width);
+      TS_ASSERT_EQUALS(3, neighbourIndexes.size());
+      // should be on edge
+      TSM_ASSERT( "Neighbours at index 1 includes 0", doesContainIndex(neighbourIndexes, 0));
+      TSM_ASSERT( "Neighbours at index 1 includes 2", doesContainIndex(neighbourIndexes, 2));
+      TSM_ASSERT( "Neighbours at index 1 includes 3", doesContainIndex(neighbourIndexes, 3));
+
+      // Go to last position
+      /*
+       0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9
+                                           ^
+                                           |
+                                           */
+      it->jumpTo(9);
+      neighbourIndexes = it->findNeighbourIndexesByWidth(width);
+      TS_ASSERT_EQUALS(2, neighbourIndexes.size());
+      TSM_ASSERT( "Neighbours at index 9 includes 8", doesContainIndex(neighbourIndexes, 8));
+      TSM_ASSERT( "Neighbours at index 9 includes 7", doesContainIndex(neighbourIndexes, 7));
+  }
+
+  void test_neighbours_2d_vertex_touching_by_width()
+  {
+    const size_t nd = 2;
+    const int width = 5;
+    MDHistoWorkspace_sptr ws = MDEventsTestHelper::makeFakeMDHistoWorkspace(1.0, nd, 4);
+    /*
+     2D MDHistoWorkspace
+
+     0 - 1 - 2 - 3
+     4 - 5 - 6 - 7
+     8 - 9 -10 -11
+     12-13 -14 -15
+     */
+    MDHistoWorkspaceIterator * it = new MDHistoWorkspaceIterator(ws);
+
+    // At initial position
+    /*
+     |0| - 1 - 2 - 3
+     4 - 5 - 6 - 7
+     8 - 9 -10 -11
+     12-13 -14 -15
+     */
+    std::vector<size_t> neighbourIndexes = it->findNeighbourIndexesByWidth(width);
+    TS_ASSERT_EQUALS(8, neighbourIndexes.size());
+    // Is on an edge
+    TSM_ASSERT( "Neighbour at index 0 is 1", doesContainIndex(neighbourIndexes, 1));
+    TSM_ASSERT( "Neighbour at index 0 is 2", doesContainIndex(neighbourIndexes, 2));
+    TSM_ASSERT( "Neighbour at index 0 is 4", doesContainIndex(neighbourIndexes, 4));
+    TSM_ASSERT( "Neighbour at index 0 is 5", doesContainIndex(neighbourIndexes, 5));
+    TSM_ASSERT( "Neighbour at index 0 is 6", doesContainIndex(neighbourIndexes, 6));
+    TSM_ASSERT( "Neighbour at index 0 is 8", doesContainIndex(neighbourIndexes, 8));
+    TSM_ASSERT( "Neighbour at index 0 is 9", doesContainIndex(neighbourIndexes, 9));
+    TSM_ASSERT( "Neighbour at index 0 is 10", doesContainIndex(neighbourIndexes, 10));
+
+
+    // At centreish position
+    /*
+     0 - 1 - 2 - 3
+     4 - |5| - 6 - 7
+     8 - 9 -10 -11
+     12-13 -14 -15
+     */
+    it->jumpTo(5);
+    neighbourIndexes = it->findNeighbourIndexesByWidth(width);
+    TS_ASSERT_EQUALS(15, neighbourIndexes.size());
+    // Is on an edge
+    for(int i = 0; i < 16; ++i)
+    {
+        if(i == 5)
+        {
+            continue; // skip over the current index of the iterator.
+        }
+        std::stringstream buffer;
+        buffer << "Neighbour at index 5 should include " << i;
+        TSM_ASSERT( buffer.str(), doesContainIndex(neighbourIndexes, i));
+    }
+
+    // At end position
+    /*
+     0 - 1 - 2 - 3
+     4 - 5 - 6 - 7
+     8 - 9 -10 -11
+     12-13 -14 -|15|
+     */
+    it->jumpTo(15);
+    neighbourIndexes = it->findNeighbourIndexesByWidth(width);
+    TS_ASSERT_EQUALS(8, neighbourIndexes.size());
+    // Is on an edge
+    TSM_ASSERT( "Neighbour at index is 5", doesContainIndex(neighbourIndexes, 5));
+    TSM_ASSERT( "Neighbour at index is 6", doesContainIndex(neighbourIndexes, 6));
+    TSM_ASSERT( "Neighbour at index is 7", doesContainIndex(neighbourIndexes, 7));
+    TSM_ASSERT( "Neighbour at index is 9", doesContainIndex(neighbourIndexes, 9));
+    TSM_ASSERT( "Neighbour at index is 10", doesContainIndex(neighbourIndexes, 10));
+    TSM_ASSERT( "Neighbour at index is 11", doesContainIndex(neighbourIndexes, 11));
+    TSM_ASSERT( "Neighbour at index is 13", doesContainIndex(neighbourIndexes, 13));
+    TSM_ASSERT( "Neighbour at index is 14", doesContainIndex(neighbourIndexes, 14));
+  }
+
+  void test_neighbours_2d_vertex_touching_by_width_vector()
+  {
+    const size_t nd = 2;
+    std::vector<int> widthVector;
+    widthVector.push_back(5);
+    widthVector.push_back(3);
+
+    MDHistoWorkspace_sptr ws = MDEventsTestHelper::makeFakeMDHistoWorkspace(1.0, nd, 4);
+    /*
+     2D MDHistoWorkspace
+
+     0 - 1 - 2 - 3
+     4 - 5 - 6 - 7
+     8 - 9 -10 -11
+     12-13 -14 -15
+     */
+    MDHistoWorkspaceIterator * it = new MDHistoWorkspaceIterator(ws);
+
+    // At initial position
+    /*
+     |0| - 1 - 2 - 3
+     4 - 5 - 6 - 7
+     8 - 9 -10 -11
+     12-13 -14 -15
+     */
+    std::vector<size_t> neighbourIndexes = it->findNeighbourIndexesByWidth(widthVector);
+    TS_ASSERT_EQUALS(5, neighbourIndexes.size());
+    // Is on an edge
+    TSM_ASSERT( "Neighbour at index is 1", doesContainIndex(neighbourIndexes, 1));
+    TSM_ASSERT( "Neighbour at index is 2", doesContainIndex(neighbourIndexes, 2));
+    TSM_ASSERT( "Neighbour at index is 4", doesContainIndex(neighbourIndexes, 4));
+    TSM_ASSERT( "Neighbour at index is 5", doesContainIndex(neighbourIndexes, 5));
+    TSM_ASSERT( "Neighbour at index is 6", doesContainIndex(neighbourIndexes, 6));
+
+
+
+    // At centreish position
+    /*
+     0 - 1 - 2 - 3
+     4 - |5| - 6 - 7
+     8 - 9 -10 -11
+     12-13 -14 -15
+     */
+    it->jumpTo(5);
+    neighbourIndexes = it->findNeighbourIndexesByWidth(widthVector);
+    TS_ASSERT_EQUALS(11, neighbourIndexes.size());
+    // Is on an edge
+    for(int i = 0; i < 12; ++i)
+    {
+        if(i == 5)
+        {
+            continue; // skip over the current index of the iterator.
+        }
+        std::stringstream buffer;
+        buffer << "Neighbour at index 5 should include " << i;
+        TSM_ASSERT( buffer.str(), doesContainIndex(neighbourIndexes, i));
+    }
+
+    // At end position
+    /*
+     0 - 1 - 2 - 3
+     4 - 5 - 6 - 7
+     8 - 9 -10 -11
+     12-13 -14 -|15|
+     */
+    it->jumpTo(15);
+    neighbourIndexes = it->findNeighbourIndexesByWidth(widthVector);
+    TS_ASSERT_EQUALS(5, neighbourIndexes.size());
+    // Is on an edge
+    TSM_ASSERT( "Neighbour at index is 9", doesContainIndex(neighbourIndexes, 9));
+    TSM_ASSERT( "Neighbour at index is 10", doesContainIndex(neighbourIndexes, 10));
+    TSM_ASSERT( "Neighbour at index is 11", doesContainIndex(neighbourIndexes, 11));
+    TSM_ASSERT( "Neighbour at index is 13", doesContainIndex(neighbourIndexes, 13));
+    TSM_ASSERT( "Neighbour at index is 14", doesContainIndex(neighbourIndexes, 14));
+  }
+
+
+  void test_neighbours_3d_vertex_touching_width()
+  {
+    const size_t nd = 3;
+    const int width = 5;
+    MDHistoWorkspace_sptr ws = MDEventsTestHelper::makeFakeMDHistoWorkspace(1.0, nd, 4);
+    /*
+     3D MDHistoWorkspace
+
+     [[[ 0  1  2  3]
+     [ 4  5  6  7]
+     [ 8  9 10 11]
+     [12 13 14 15]]
+
+     [[16 17 18 19]
+     [20 21 22 23]
+     [24 25 26 27]
+     [28 29 30 31]]
+
+     [[32 33 34 35]
+     [36 37 38 39]
+     [40 41 42 43]
+     [44 45 46 47]]
+
+     [[48 49 50 51]
+     [52 53 54 55]
+     [56 57 58 59]
+     [60 61 62 63]]]
+     */
+
+    MDHistoWorkspaceIterator * it = new MDHistoWorkspaceIterator(ws);
+
+    // Start at Index = 0
+    std::vector<size_t> neighbourIndexes = it->findNeighbourIndexesByWidth(width);
+    TS_ASSERT_EQUALS(26, neighbourIndexes.size());
+    // Is on an edge
+    TS_ASSERT(doesContainIndex(neighbourIndexes, 1));
+    TS_ASSERT(doesContainIndex(neighbourIndexes, 2));
+    TS_ASSERT(doesContainIndex(neighbourIndexes, 4));
+    TS_ASSERT(doesContainIndex(neighbourIndexes, 5));
+    TS_ASSERT(doesContainIndex(neighbourIndexes, 6));
+    TS_ASSERT(doesContainIndex(neighbourIndexes, 8));
+    TS_ASSERT(doesContainIndex(neighbourIndexes, 9));
+    TS_ASSERT(doesContainIndex(neighbourIndexes, 10));
+    TS_ASSERT(doesContainIndex(neighbourIndexes, 16));
+    TS_ASSERT(doesContainIndex(neighbourIndexes, 17));
+    TS_ASSERT(doesContainIndex(neighbourIndexes, 18));
+    TS_ASSERT(doesContainIndex(neighbourIndexes, 20));
+    TS_ASSERT(doesContainIndex(neighbourIndexes, 21));
+    TS_ASSERT(doesContainIndex(neighbourIndexes, 22));
+    TS_ASSERT(doesContainIndex(neighbourIndexes, 24));
+    TS_ASSERT(doesContainIndex(neighbourIndexes, 25));
+    TS_ASSERT(doesContainIndex(neighbourIndexes, 26));
+  }
+
+  void test_cache()
+  {
+      const size_t nd = 1;
+      MDHistoWorkspace_sptr ws = MDEventsTestHelper::makeFakeMDHistoWorkspace(1.0, nd, 10);
+      /*
+       1D MDHistoWorkspace
+
+       0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9
+
+       */
+
+      MDHistoWorkspaceIterator * it = new MDHistoWorkspaceIterator(ws);
+      TSM_ASSERT_EQUALS("Empty cache expected", 0, it->permutationCacheSize());
+      it->findNeighbourIndexesByWidth(3);
+      TSM_ASSERT_EQUALS("One cache item expected", 1, it->permutationCacheSize());
+      it->findNeighbourIndexesByWidth(3);
+      TSM_ASSERT_EQUALS("One cache item expected", 1, it->permutationCacheSize()); // Same item, no change to cache
+      it->findNeighbourIndexesByWidth(5);
+      TSM_ASSERT_EQUALS("Two cache entries expected", 2, it->permutationCacheSize());
+  }
+
+
 };
 
 class MDHistoWorkspaceIteratorTestPerformance: public CxxTest::TestSuite
@@ -799,6 +1091,15 @@ public:
     } while (iterator.next());
   }
 
+  void test_findNeighboursByWidth()
+  {
+      MDHistoWorkspaceIterator iterator(small_ws, new SkipNothing());
+      do
+      {
+        iterator.findNeighbourIndexesByWidth(5);
+      } while (iterator.next());
+  }
+
 };
 
 #endif /* MANTID_DATAOBJECTS_MDHISTOWORKSPACEITERATORTEST_H_ */
diff --git a/Code/Mantid/Framework/Kernel/inc/MantidKernel/Utils.h b/Code/Mantid/Framework/Kernel/inc/MantidKernel/Utils.h
index 449f16ed831385307d7204cae96ad528b3b1ac6e..e53ef353320531563f25afcfef32b993c83153e8 100644
--- a/Code/Mantid/Framework/Kernel/inc/MantidKernel/Utils.h
+++ b/Code/Mantid/Framework/Kernel/inc/MantidKernel/Utils.h
@@ -286,13 +286,14 @@ inline void getIndicesFromLinearIndex(const size_t linear_index,
  * @param subject_indices
  * @param num_bins
  * @param index_max
+ * @param widths : width in pixels per dimension
  * @return True if the are neighbours, otherwise false.
  */
 inline bool isNeighbourOfSubject(const size_t ndims,
                                  const size_t neighbour_linear_index,
                                  const size_t *subject_indices,
                                  const size_t *num_bins,
-                                 const size_t *index_max) {
+                                 const size_t *index_max, const std::vector<int>& widths) {
   std::vector<size_t> neighbour_indices(ndims);
   Utils::NestedForLoop::GetIndicesFromLinearIndex(ndims, neighbour_linear_index,
                                                   num_bins, index_max,
@@ -302,7 +303,7 @@ inline bool isNeighbourOfSubject(const size_t ndims,
     long double diff =
         std::abs(static_cast<long double>(subject_indices[ind]) -
                  static_cast<long double>(neighbour_indices[ind]));
-    if (diff > 1) {
+    if (diff > widths[ind]/2) {
       return false;
     }
   }
diff --git a/Code/Mantid/Framework/MDAlgorithms/CMakeLists.txt b/Code/Mantid/Framework/MDAlgorithms/CMakeLists.txt
index 3e92b53e508838bb7f92d2ea1a2b9f2f41c90df6..1adef4fb0bb0a5e237dcf7fd76138dff4742de8a 100644
--- a/Code/Mantid/Framework/MDAlgorithms/CMakeLists.txt
+++ b/Code/Mantid/Framework/MDAlgorithms/CMakeLists.txt
@@ -104,6 +104,7 @@ set ( SRC_FILES
 	src/SetMDUsingMask.cpp
 	src/SliceMD.cpp
 	src/SlicingAlgorithm.cpp
+	src/SmoothMD.cpp
 	src/ThresholdMD.cpp
 	src/TransformMD.cpp
 	src/UnaryOperationMD.cpp
@@ -219,6 +220,7 @@ set ( INC_FILES
 	inc/MantidMDAlgorithms/SetMDUsingMask.h
 	inc/MantidMDAlgorithms/SliceMD.h
 	inc/MantidMDAlgorithms/SlicingAlgorithm.h
+	inc/MantidMDAlgorithms/SmoothMD.h
 	inc/MantidMDAlgorithms/ThresholdMD.h
 	inc/MantidMDAlgorithms/TransformMD.h
 	inc/MantidMDAlgorithms/UnaryOperationMD.h
@@ -317,6 +319,7 @@ set ( TEST_FILES
 	SimulateResolutionConvolvedModelTest.h
 	SliceMDTest.h
 	SlicingAlgorithmTest.h
+	SmoothMDTest.h
 	Strontium122Test.h
 	ThresholdMDTest.h
 	TobyFitBMatrixTest.h
diff --git a/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/SmoothMD.h b/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/SmoothMD.h
new file mode 100644
index 0000000000000000000000000000000000000000..61c447729a39e417ab6799c08af66a17f952a1cf
--- /dev/null
+++ b/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/SmoothMD.h
@@ -0,0 +1,66 @@
+#ifndef MANTID_MDALGORITHMS_SMOOTHMD_H_
+#define MANTID_MDALGORITHMS_SMOOTHMD_H_
+
+#include "MantidKernel/System.h"
+#include "MantidAPI/Algorithm.h"
+#include <boost/shared_ptr.hpp>
+#include <boost/optional.hpp>
+
+namespace Mantid
+{
+ namespace API
+ {
+ class IMDHistoWorkspace;
+ }
+namespace MDAlgorithms
+{
+
+  /** SmoothMD : Algorithm for smoothing MDHistoWorkspaces
+
+    Copyright &copy; 2015 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge National Laboratory & European Spallation Source
+
+    This file is part of Mantid.
+
+    Mantid is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    Mantid is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+    File change history is stored at: <https://github.com/mantidproject/mantid>
+    Code Documentation is available at: <http://doxygen.mantidproject.org>
+  */
+  class DLLExport SmoothMD  : public API::Algorithm
+  {
+  public:
+    SmoothMD();
+    virtual ~SmoothMD();
+    
+    virtual const std::string name() const;
+    virtual int version() const;
+    virtual const std::string category() const;
+    virtual const std::string summary() const;
+    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, boost::optional<boost::shared_ptr<const Mantid::API::IMDHistoWorkspace> > weighting);
+
+  private:
+
+    void init();
+    void exec();
+
+  };
+
+
+} // namespace MDAlgorithms
+} // namespace Mantid
+
+#endif  /* MANTID_MDALGORITHMS_SMOOTHMD_H_ */
diff --git a/Code/Mantid/Framework/MDAlgorithms/src/SmoothMD.cpp b/Code/Mantid/Framework/MDAlgorithms/src/SmoothMD.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8e46f38e9691b290259c7e4b658b438b5117d0b1
--- /dev/null
+++ b/Code/Mantid/Framework/MDAlgorithms/src/SmoothMD.cpp
@@ -0,0 +1,341 @@
+#include "MantidMDAlgorithms/SmoothMD.h"
+#include "MantidAPI/FrameworkManager.h"
+#include "MantidAPI/IMDHistoWorkspace.h"
+#include "MantidAPI/IMDIterator.h"
+#include "MantidAPI/Progress.h"
+#include "MantidKernel/ArrayProperty.h"
+#include "MantidKernel/ArrayBoundedValidator.h"
+#include "MantidKernel/CompositeValidator.h"
+#include "MantidKernel/ListValidator.h"
+#include "MantidKernel/MandatoryValidator.h"
+#include "MantidKernel/PropertyWithValue.h"
+#include "MantidKernel/MultiThreaded.h"
+#include "MantidDataObjects/MDHistoWorkspaceIterator.h"
+#include <boost/make_shared.hpp>
+#include <vector>
+#include <map>
+#include <string>
+#include <sstream>
+#include <utility>
+#include <limits>
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <boost/tuple/tuple.hpp>
+
+using namespace Mantid::Kernel;
+using namespace Mantid::API;
+using namespace Mantid::DataObjects;
+
+// 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 &,
+    OptionalIMDHistoWorkspace_const_sptr)> SmoothFunction;
+
+// Typedef for a smoothing function map keyed by name.
+typedef std::map<std::string, SmoothFunction> SmoothFunctionMap;
+
+namespace {
+
+/**
+ * @brief functions
+ * @return Allowed smoothing functions
+ */
+std::vector<std::string> functions() {
+  std::vector<std::string> propOptions;
+  propOptions.push_back("Hat");
+  // propOptions.push_back("Gaussian");
+  return propOptions;
+}
+
+/**
+ * Maps a function name to a smoothing function
+ * @return function map
+ */
+SmoothFunctionMap makeFunctionMap(Mantid::MDAlgorithms::SmoothMD *instance) {
+  SmoothFunctionMap map;
+  map.insert(std::make_pair(
+      "Hat", boost::bind(&Mantid::MDAlgorithms::SmoothMD::hatSmooth, instance,
+                         _1, _2, _3)));
+  return map;
+}
+}
+
+namespace Mantid {
+namespace MDAlgorithms {
+
+// Register the algorithm into the AlgorithmFactory
+DECLARE_ALGORITHM(SmoothMD)
+
+//----------------------------------------------------------------------------------------------
+/** Constructor
+ */
+SmoothMD::SmoothMD() {}
+
+//----------------------------------------------------------------------------------------------
+/** Destructor
+ */
+SmoothMD::~SmoothMD() {}
+
+//----------------------------------------------------------------------------------------------
+
+/// Algorithms name for identification. @see Algorithm::name
+const std::string SmoothMD::name() const { return "SmoothMD"; }
+
+/// Algorithm's version for identification. @see Algorithm::version
+int SmoothMD::version() const { return 1; }
+
+/// Algorithm's category for identification. @see Algorithm::category
+const std::string SmoothMD::category() const { return "MDAlgorithms"; }
+
+/// Algorithm's summary for use in the GUI and help. @see Algorithm::summary
+const std::string SmoothMD::summary() const {
+  return "Smooth an MDHistoWorkspace according to a weight function";
+}
+
+/**
+ * Hat function smoothing. All weights even. Hat function boundaries beyond
+ * 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,
+                    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.
+  IMDHistoWorkspace_sptr outWS = toSmooth->clone();
+  progress.reportIncrement(
+      size_t(double(nPoints) * 0.1)); // Report ~10% progress
+
+  const int nThreads = Mantid::API::FrameworkManager::Instance()
+                           .getNumOMPThreads(); // NThreads to Request
+
+  auto iterators = toSmooth->createIterators(nThreads, NULL);
+
+  PARALLEL_FOR_NO_WSP_CHECK()
+  for (int it = 0; it < int(iterators.size()); ++it) {
+
+    PARALLEL_START_INTERUPT_REGION
+    boost::scoped_ptr<MDHistoWorkspaceIterator> iterator(
+        dynamic_cast<MDHistoWorkspaceIterator *>(iterators[it]));
+
+    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);
+
+      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(iteratorIndex, sumSignal / double(nNeighbours + 1));
+      // Calculate the sample variance
+      outWS->setErrorSquaredAt(iteratorIndex,
+                               sumSqError / double(nNeighbours + 1));
+
+      progress.report();
+
+    } while (iterator->next());
+    PARALLEL_END_INTERUPT_REGION
+  }
+  PARALLEL_CHECK_INTERUPT_REGION
+
+  return outWS;
+}
+
+//----------------------------------------------------------------------------------------------
+/** Initialize the algorithm's properties.
+ */
+void SmoothMD::init() {
+  declareProperty(new WorkspaceProperty<API::IMDHistoWorkspace>(
+                      "InputWorkspace", "", Direction::Input),
+                  "An input MDHistoWorkspace to smooth.");
+
+  auto widthVectorValidator = boost::make_shared<CompositeValidator>();
+  auto boundedValidator =
+      boost::make_shared<ArrayBoundedValidator<int>>(1, 100);
+  widthVectorValidator->add(boundedValidator);
+  widthVectorValidator->add(
+      boost::make_shared<MandatoryValidator<std::vector<int>>>());
+
+  declareProperty(new ArrayProperty<int>("WidthVector", widthVectorValidator,
+                                         Direction::Input),
+                  "Width vector. Either specify the width in n-pixels for each "
+                  "dimension, or provide a single entry (n-pixels) for all "
+                  "dimensions.");
+
+  const auto allFunctionTypes = functions();
+  const std::string first = allFunctionTypes.front();
+
+  std::stringstream docBuffer;
+  docBuffer << "Smoothing function. Defaults to " << first;
+  declareProperty(
+      new PropertyWithValue<std::string>(
+          "Function", first,
+          boost::make_shared<ListValidator<std::string>>(allFunctionTypes),
+          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.");
+}
+
+//----------------------------------------------------------------------------------------------
+/** Execute the algorithm.
+ */
+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
+  std::vector<int> widthVector = this->getProperty("WidthVector");
+  if (widthVector.size() == 1) {
+    // Pad the width vector out to the right size if only one entry has been
+    // provided.
+    widthVector =
+        std::vector<int>(toSmooth->getNumDims(), widthVector.front());
+  }
+
+  // Find the choosen smooth operation
+  const std::string smoothFunctionName = this->getProperty("Function");
+  SmoothFunctionMap functionMap = makeFunctionMap(this);
+  SmoothFunction smoothFunction = functionMap[smoothFunctionName];
+  // invoke the smoothing operation
+  auto smoothed = smoothFunction(toSmooth, widthVector, optionalWeightingWS);
+
+  setProperty("OutputWorkspace", smoothed);
+}
+
+/**
+ * validateInputs
+ * @return map of property names to errors.
+ */
+std::map<std::string, std::string> SmoothMD::validateInputs() {
+
+  std::map<std::string, std::string> product;
+
+  IMDHistoWorkspace_sptr toSmoothWs = this->getProperty("InputWorkspace");
+
+  // Check the width vector
+  const std::string widthVectorPropertyName = "WidthVector";
+  std::vector<int> widthVector = this->getProperty(widthVectorPropertyName);
+
+  if (widthVector.size() != 1 && widthVector.size() != toSmoothWs->getNumDims()) {
+    product.insert(std::make_pair(widthVectorPropertyName,
+                                  widthVectorPropertyName +
+                                      " can either have one entry or needs to "
+                                      "have entries for each dimension of the "
+                                      "InputWorkspace."));
+  } else {
+    for (auto it = widthVector.begin(); it != widthVector.end(); ++it) {
+      const int widthEntry = *it;
+      if (widthEntry % 2 == 0) {
+        std::stringstream message;
+        message << widthVectorPropertyName
+                << " entries must be odd numbers. Bad entry is " << widthEntry;
+        product.insert(std::make_pair(widthVectorPropertyName, message.str()));
+      }
+    }
+  }
+
+  // Check the dimensionality of the normalization workspace
+  const std::string normalisationWorkspacePropertyName =
+      "InputNormalizationWorkspace";
+
+  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;
+}
+
+} // namespace MDAlgorithms
+} // namespace Mantid
diff --git a/Code/Mantid/Framework/MDAlgorithms/test/SmoothMDTest.h b/Code/Mantid/Framework/MDAlgorithms/test/SmoothMDTest.h
new file mode 100644
index 0000000000000000000000000000000000000000..db55e4e826616d2197f001639a12268b5d1e36f2
--- /dev/null
+++ b/Code/Mantid/Framework/MDAlgorithms/test/SmoothMDTest.h
@@ -0,0 +1,423 @@
+#ifndef MANTID_MDALGORITHMS_SMOOTHMDTEST_H_
+#define MANTID_MDALGORITHMS_SMOOTHMDTEST_H_
+
+#include <cxxtest/TestSuite.h>
+#include "MantidMDAlgorithms/SmoothMD.h"
+#include "MantidDataObjects/MDHistoWorkspace.h"
+#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;
+using namespace Mantid::DataObjects;
+
+class SmoothMDTest : public CxxTest::TestSuite {
+public:
+  // This pair of boilerplate methods prevent the suite being created statically
+  // This means the constructor isn't called when running other tests
+  static SmoothMDTest *createSuite() { return new SmoothMDTest(); }
+  static void destroySuite(SmoothMDTest *suite) { delete suite; }
+
+  void test_Init() {
+    SmoothMD alg;
+    TS_ASSERT_THROWS_NOTHING(alg.initialize())
+  }
+
+  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 &);
+  }
+
+  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_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 &);
+
+    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_width_vector_must_not_be_arbitrary_size() {
+      auto toSmooth = MDEventsTestHelper::makeFakeMDHistoWorkspace(
+          1 /*signal*/, 2 /*numDims*/, 3 /*numBins in each dimension*/);
+
+      std::vector<int> badWidths(11, 3); // odd number value = 3, but size of 11 has no meaning
+
+      SmoothMD alg;
+      alg.setChild(true);
+      alg.initialize();
+      alg.setPropertyValue("OutputWorkspace", "dummy");
+      alg.setProperty("InputWorkspace", toSmooth);
+      alg.setProperty(
+          "WidthVector",
+          badWidths); // Width vector is the wrong size
+      TSM_ASSERT_THROWS("Size of with vector is wrong should 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
+    */
+
+    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));
+    }
+  }
+
+  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
+
+     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
+
+     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_mixed_widths() {
+
+    auto toSmooth = MDEventsTestHelper::makeFakeMDHistoWorkspace(
+        2 /*signal*/, 2 /*numDims*/, 5 /*numBins in each dimension*/);
+    toSmooth->setSignalAt(2, 1.0);
+    toSmooth->setSignalAt(22, 1.0);
+
+    /*
+     2D MDHistoWorkspace Input
+
+     2 - 2 - 1 - 2 - 2
+     2 - 2 - 2 - 2 - 2
+     2 - 2 - 2 - 2 - 2
+     2 - 2 - 2 - 2 - 2
+     2 - 2 - 1 - 2 - 2
+
+    */
+
+    SmoothMD alg;
+    alg.setChild(true);
+    alg.initialize();
+    std::vector<int> widthVector;
+    widthVector.push_back(3); // 3 = width in zeroth dimension
+    widthVector.push_back(5); // 5 = width in first dimension
+    alg.setProperty("WidthVector", widthVector);
+    alg.setProperty("InputWorkspace", toSmooth);
+    alg.setPropertyValue("OutputWorkspace", "dummy");
+    alg.execute();
+    IMDHistoWorkspace_sptr out = alg.getProperty("OutputWorkspace");
+
+    /*
+     Smoothing region for centre point
+
+     2 - |2 - 1 - 2| - 2
+     2 - |2 - 2 - 2| - 2
+     2 - |2 -|2|- 2| - 2
+     2 - |2 - 2 - 2| - 2
+     2 - |2 - 1 - 2| - 2
+
+     3 by 5 with.
+
+     average for centre point should be [ (3 * 5) - 2 ] * 2 / (3 * 5) = 28 / 15
+
+    */
+
+    // Check vertexes
+    double expectedSmoothedValue  = 28.0 / 15;
+
+    TS_ASSERT_EQUALS(expectedSmoothedValue, out->getSignalAt(12));
+  }
+
+
+  void test_dimensional_check_of_weight_ws() {
+
+      MDHistoWorkspace_sptr a =
+          MDEventsTestHelper::makeFakeMDHistoWorkspace(1.0 /*signal value*/, 1 /*dimensionality*/,
+                                                       9);
+
+      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); // Smooth with width == 3
+      alg.setProperty("WidthVector", widthVector);
+      alg.setProperty("InputWorkspace", a);
+      alg.setProperty("InputNormalizationWorkspace", b);
+      alg.setPropertyValue("OutputWorkspace", "dummy");
+
+      TSM_ASSERT_THROWS("Input unsmoothed and input Normalisation workspaces must have the same dimensionality",
+                        alg.execute(), std::runtime_error &);
+
+  }
+
+  void test_shape_check_of_weight_ws() {
+
+      const size_t nd = 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, 3); // Smooth with width == 3
+      alg.setProperty("WidthVector", widthVector);
+      alg.setProperty("InputWorkspace", a);
+      alg.setProperty("InputNormalizationWorkspace", b);
+      alg.setPropertyValue("OutputWorkspace", "dummy");
+
+      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;
+
+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 void destroySuite(SmoothMDTestPerformance *suite) { delete suite; }
+
+  SmoothMDTestPerformance() {
+    m_toSmooth = MDEventsTestHelper::makeFakeMDHistoWorkspace(
+        1 /*signal*/, 2 /*numDims*/, 500 /*numBins in each dimension*/);
+  }
+
+  void test_execute_hat_function() {
+    SmoothMD alg;
+    alg.setChild(true);
+    alg.initialize();
+    std::vector<int> widthVector(1, 5); // Smooth with width == 5
+    alg.setProperty("WidthVector", widthVector);
+    alg.setProperty("InputWorkspace", m_toSmooth);
+    alg.setPropertyValue("OutputWorkspace", "dummy");
+    alg.execute();
+    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
new file mode 100644
index 0000000000000000000000000000000000000000..57ac9b1775402e29a0e426e15184f763389176f3
--- /dev/null
+++ b/Code/Mantid/docs/source/algorithms/SmoothMD-v1.rst
@@ -0,0 +1,57 @@
+
+.. algorithm::
+
+.. summary::
+
+.. alias::
+
+.. properties::
+
+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.
+Note that the NormalizationWorkspace is not changed, and needs to be smoothed as well, using the same parameters and *InputNormalizationWorkspace* as the original data.
+
+.. figure:: /images/PreSmooth.png
+   :alt: PreSmooth.png
+   :width: 400px
+   :align: center
+   
+   No smoothing
+   
+.. figure:: /images/Smoothed.png
+   :alt: PreSmooth.png
+   :width: 400px
+   :align: center
+   
+   Smooth with WidthVector=5
+
+
+Usage
+-----
+
+**Example - SmoothMD**
+
+.. testcode:: SmoothMDExample
+
+   ws = CreateMDWorkspace(Dimensions=2, Extents=[-10,10,-10,10], Names='A,B', Units='U,U')
+   FakeMDEventData(InputWorkspace=ws, PeakParams='100000,-5,0,1')
+   FakeMDEventData(InputWorkspace=ws, PeakParams='100000,5,0,1')
+   histogram = BinMD(InputWorkspace=ws, AlignedDim0='A,-10,10,50', AlignedDim1='B,-10,10,50', OutputExtents='-10,10,-10,10,-10,10', OutputBins='10,10,10')
+   # plotSlice(histogram)
+   smoothed = SmoothMD(InputWorkspace=histogram, WidthVector=5, Function='Hat')
+   # plotSlice(smoothed)
+
+   print 'Smoothed has %i points' % smoothed.getNPoints()
+
+Output:
+
+.. testoutput:: SmoothMDExample
+
+   Smoothed has 2500 points
+
+.. categories::
+
diff --git a/Code/Mantid/docs/source/images/PreSmooth.png b/Code/Mantid/docs/source/images/PreSmooth.png
new file mode 100644
index 0000000000000000000000000000000000000000..fe335f515ed1bea97acd4138906631466c6bfed5
Binary files /dev/null and b/Code/Mantid/docs/source/images/PreSmooth.png differ
diff --git a/Code/Mantid/docs/source/images/Smoothed.png b/Code/Mantid/docs/source/images/Smoothed.png
new file mode 100644
index 0000000000000000000000000000000000000000..c819c6891cdb7b50ef78c64de5e05219712fed0b
Binary files /dev/null and b/Code/Mantid/docs/source/images/Smoothed.png differ