diff --git a/Framework/Algorithms/inc/MantidAlgorithms/RunCombinationHelpers/RunCombinationHelper.h b/Framework/Algorithms/inc/MantidAlgorithms/RunCombinationHelpers/RunCombinationHelper.h
index 7093a030439be1cc9dd394771052b7b2cbbb0d51..3b2e9a7eebcc2d1e217af5a965b036efef6f9b08 100644
--- a/Framework/Algorithms/inc/MantidAlgorithms/RunCombinationHelpers/RunCombinationHelper.h
+++ b/Framework/Algorithms/inc/MantidAlgorithms/RunCombinationHelpers/RunCombinationHelper.h
@@ -64,6 +64,7 @@ private:
   std::string m_instrumentName;
   bool m_isHistogramData;
   bool m_isScanning;
+  std::vector<bool> m_hasDx;
 };
 
 } // namespace Algorithms
diff --git a/Framework/Algorithms/inc/MantidAlgorithms/SumOverlappingTubes.h b/Framework/Algorithms/inc/MantidAlgorithms/SumOverlappingTubes.h
index c322de8585871a9423f45cbbe84288c0048728ce..08b79627cb4f144b3be749c49b02d0564898876a 100644
--- a/Framework/Algorithms/inc/MantidAlgorithms/SumOverlappingTubes.h
+++ b/Framework/Algorithms/inc/MantidAlgorithms/SumOverlappingTubes.h
@@ -4,7 +4,10 @@
 #include "MantidAlgorithms/DllConfig.h"
 
 #include "MantidAPI/Algorithm.h"
-#include "MantidAPI/MatrixWorkspace.h"
+#include "MantidAPI/MatrixWorkspace_fwd.h"
+#include "MantidAPI/Progress.h"
+
+#include <list>
 
 namespace Mantid {
 namespace Algorithms {
@@ -75,7 +78,9 @@ private:
 
   double distanceFromAngle(const int angleIndex, const double angle) const;
 
-  int m_mirrorDetectors;
+  int m_mirrorDetectors; /// holds the sign flipper for 2theta
+
+  std::unique_ptr<API::Progress> m_progress;
 };
 
 } // namespace Algorithms
diff --git a/Framework/Algorithms/src/ConjoinXRuns.cpp b/Framework/Algorithms/src/ConjoinXRuns.cpp
index fa9504834ad4f8b10e3a1595a7a14465916e881d..b9fc326800d0c28120bc9f78085fd9a4785ba6d1 100644
--- a/Framework/Algorithms/src/ConjoinXRuns.cpp
+++ b/Framework/Algorithms/src/ConjoinXRuns.cpp
@@ -10,6 +10,10 @@
 #include "MantidAlgorithms/RunCombinationHelpers/RunCombinationHelper.h"
 #include "MantidAlgorithms/RunCombinationHelpers/SampleLogsBehaviour.h"
 #include "MantidGeometry/Instrument.h"
+#include "MantidHistogramData/HistogramDx.h"
+#include "MantidHistogramData/HistogramE.h"
+#include "MantidHistogramData/HistogramX.h"
+#include "MantidHistogramData/HistogramY.h"
 #include "MantidKernel/ArrayProperty.h"
 #include "MantidKernel/Exception.h"
 #include "MantidKernel/ListValidator.h"
@@ -274,25 +278,31 @@ void ConjoinXRuns::joinSpectrum(int64_t wsIndex) {
   std::vector<double> spectrum;
   std::vector<double> errors;
   std::vector<double> axis;
+  std::vector<double> x;
+  std::vector<double> xerrors;
   spectrum.reserve(m_outWS->blocksize());
   errors.reserve(m_outWS->blocksize());
   axis.reserve(m_outWS->blocksize());
   size_t index = static_cast<size_t>(wsIndex);
-
   for (const auto &input : m_inputWS) {
-    auto y = input->y(index).rawData();
-    auto e = input->e(index).rawData();
-    std::vector<double> x;
+    const auto &y = input->y(index);
+    spectrum.insert(spectrum.end(), y.begin(), y.end());
+    const auto &e = input->e(index);
+    errors.insert(errors.end(), e.begin(), e.end());
     if (m_logEntry.empty()) {
-      x = input->x(index).rawData();
+      const auto &x = input->x(index);
+      axis.insert(axis.end(), x.begin(), x.end());
     } else {
       x = m_axisCache[input->getName()];
+      axis.insert(axis.end(), x.begin(), x.end());
+    }
+    if (input->hasDx(index)) {
+      const auto &dx = input->dx(index);
+      xerrors.insert(xerrors.end(), dx.begin(), dx.end());
     }
-    spectrum.insert(spectrum.end(), y.begin(), y.end());
-    errors.insert(errors.end(), e.begin(), e.end());
-    axis.insert(axis.end(), x.begin(), x.end());
   }
-
+  if (!xerrors.empty())
+    m_outWS->setPointStandardDeviations(index, xerrors);
   m_outWS->mutableY(index) = spectrum;
   m_outWS->mutableE(index) = errors;
   m_outWS->mutableX(index) = axis;
diff --git a/Framework/Algorithms/src/ExtractMask.cpp b/Framework/Algorithms/src/ExtractMask.cpp
index c595ccdad76938f19932bd873cdbfb7bc8bc72ac..2d474bf92b4c81227dcf97f01509a07d7ce648c5 100644
--- a/Framework/Algorithms/src/ExtractMask.cpp
+++ b/Framework/Algorithms/src/ExtractMask.cpp
@@ -54,9 +54,12 @@ void ExtractMask::exec() {
   std::vector<detid_t> detectorList;
   const auto &detInfo = inputWS->detectorInfo();
   const auto &detIds = detInfo.detectorIDs();
-  for (size_t i = 0; i < detInfo.size(); ++i)
-    if (detInfo.isMasked(i))
+  for (size_t i = 0; i < detInfo.size(); ++i) {
+    if ((inputWSIsSpecial && inputMaskWS->isMasked(detIds[i])) ||
+        detInfo.isMasked(i)) {
       detectorList.push_back(detIds[i]);
+    }
+  }
 
   // Create a new workspace for the results, copy from the input to ensure
   // that we copy over the instrument and current masking
diff --git a/Framework/Algorithms/src/RunCombinationHelpers/RunCombinationHelper.cpp b/Framework/Algorithms/src/RunCombinationHelpers/RunCombinationHelper.cpp
index adde7248cd6b49cd7d33761001365862c8b6e500..4f8ae81b93dc3130e2744df0dec37505e93362a3 100644
--- a/Framework/Algorithms/src/RunCombinationHelpers/RunCombinationHelper.cpp
+++ b/Framework/Algorithms/src/RunCombinationHelpers/RunCombinationHelper.cpp
@@ -54,6 +54,11 @@ void RunCombinationHelper::setReferenceProperties(MatrixWorkspace_sptr ref) {
   m_isHistogramData = ref->isHistogramData();
   m_isScanning = ref->detectorInfo().isScanning();
   m_instrumentName = ref->getInstrument()->getName();
+  if (m_numberSpectra) {
+    m_hasDx.reserve(m_numberSpectra);
+    for (unsigned int i = 0; i < m_numberSpectra; ++i)
+      m_hasDx.push_back(ref->hasDx(i));
+  }
 }
 
 //----------------------------------------------------------------------------------------------
@@ -65,7 +70,7 @@ void RunCombinationHelper::setReferenceProperties(MatrixWorkspace_sptr ref) {
 std::string
 RunCombinationHelper::checkCompatibility(MatrixWorkspace_sptr ws,
                                          bool checkNumberHistograms) {
-  std::string errors = "";
+  std::string errors;
   if (ws->getNumberHistograms() != m_numberSpectra && checkNumberHistograms)
     errors += "different number of histograms; ";
   if (ws->getAxis(0)->unit()->unitID() != m_xUnit)
@@ -83,6 +88,16 @@ RunCombinationHelper::checkCompatibility(MatrixWorkspace_sptr ws,
               "detectors; ";
   if (ws->getInstrument()->getName() != m_instrumentName)
     errors += "different instrument names; ";
+  if (ws->getNumberHistograms() == m_numberSpectra) {
+    if (!m_hasDx.empty()) {
+      for (unsigned int i = 0; i < m_numberSpectra; ++i) {
+        if (m_hasDx[i] != ws->hasDx(i)) {
+          errors += "spectra must have either Dx values or not; ";
+          break;
+        }
+      }
+    }
+  }
   return errors;
 }
 
diff --git a/Framework/Algorithms/src/SumOverlappingTubes.cpp b/Framework/Algorithms/src/SumOverlappingTubes.cpp
index 9f6da909ce7eb7d31a40b20ff3e4affb90eb2b61..fac9121644d4c47cbccd746003c11caaf722fd88 100644
--- a/Framework/Algorithms/src/SumOverlappingTubes.cpp
+++ b/Framework/Algorithms/src/SumOverlappingTubes.cpp
@@ -2,6 +2,7 @@
 
 #include "MantidAlgorithms/RunCombinationHelpers/RunCombinationHelper.h"
 #include "MantidAPI/ADSValidator.h"
+#include "MantidAPI/MatrixWorkspace.h"
 #include "MantidAPI/NumericAxis.h"
 #include "MantidAPI/SpectrumInfo.h"
 #include "MantidAPI/WorkspaceProperty.h"
@@ -21,6 +22,8 @@
 #include "MantidKernel/Unit.h"
 #include "MantidKernel/UnitFactory.h"
 
+#include <boost/math/special_functions/round.hpp>
+
 namespace Mantid {
 namespace Algorithms {
 
@@ -70,20 +73,23 @@ void SumOverlappingTubes::init() {
   declareProperty(
       make_unique<PropertyWithValue<bool>>("Normalise", true, Direction::Input),
       "If true normalise to the number of entries added for a particular "
-      "scattering angle. If the maximum entries accross all the scattering "
-      "angles is N_MAX, and the number of entries for a scattering angle is N, "
-      "the normalisation is performed as N_MAX / N.");
+      "scattering angle. ");
   auto toleranceValidator =
       boost::make_shared<BoundedValidator<double>>(0.0, 0.0);
   toleranceValidator->clearUpper();
   declareProperty("ScatteringAngleTolerance", 0.0, toleranceValidator,
                   "The relative tolerance for the scattering angles before the "
                   "counts are split.");
+  declareProperty(make_unique<PropertyWithValue<bool>>("MirrorScatteringAngles",
+                                                       false, Direction::Input),
+                  "A flag to mirror the signed 2thetas. ");
 }
 
 void SumOverlappingTubes::exec() {
   getInputParameters();
 
+  m_progress = make_unique<Progress>(this, 0.0, 1.0, m_workspaceList.size());
+
   HistogramData::Points x(m_numPoints, LinearGenerator(m_startScatteringAngle,
                                                        m_stepScatteringAngle));
 
@@ -102,52 +108,40 @@ void SumOverlappingTubes::exec() {
   Unit_sptr xUnit = outputWS->getAxis(0)->unit();
   boost::shared_ptr<Units::Label> xLabel =
       boost::dynamic_pointer_cast<Units::Label>(xUnit);
-  xLabel->setLabel("Tube Angle", "degrees");
+  xLabel->setLabel("Scattering Angle", "degrees");
 
   const auto normalisation = performBinning(outputWS);
 
-  auto maxEntry = 0.0;
-  for (const auto &vector : normalisation)
-    maxEntry =
-        std::max(*std::max_element(vector.begin(), vector.end()), maxEntry);
   if (getProperty("Normalise"))
     for (size_t i = 0; i < m_numPoints; ++i)
       for (size_t j = 0; j < m_numHistograms; ++j) {
         // Avoid spurious normalisation for low counting cells
         if (normalisation[j][i] < 1e-15)
           continue;
-        outputWS->mutableY(j)[i] *= maxEntry / normalisation[j][i];
-        outputWS->mutableE(j)[i] *= maxEntry / normalisation[j][i];
+        outputWS->mutableY(j)[i] /= normalisation[j][i];
+        outputWS->mutableE(j)[i] /= normalisation[j][i];
       }
 
   setProperty("OutputWorkspace", outputWS);
 }
 
 void SumOverlappingTubes::getInputParameters() {
+
+  // This is flag for flipping the sign of 2theta
+  m_mirrorDetectors = getProperty("MirrorScatteringAngles") ? -1 : 1;
+
   const std::vector<std::string> inputWorkspaces =
       getProperty("InputWorkspaces");
   auto workspaces = RunCombinationHelper::unWrapGroups(inputWorkspaces);
   RunCombinationHelper combHelper;
   m_workspaceList = combHelper.validateInputWorkspaces(workspaces, g_log);
-
   m_outputType = getPropertyValue("OutputType");
   const auto &instrument = m_workspaceList.front()->getInstrument();
-
-  // For D2B at the ILL the detectors are flipped when comparing with other
-  // powder diffraction instruments such as D20. It is still desired to show
-  // angles as positive however, so here we check if we need to multiple angle
-  // calculations by -1.
-  m_mirrorDetectors = 1;
-  auto mirrorDetectors = instrument->getBoolParameter("mirror_detector_angles");
-  if (!mirrorDetectors.empty() && mirrorDetectors[0])
-    m_mirrorDetectors = -1;
-
   std::string componentName = "";
   auto componentNameParam =
       instrument->getStringParameter("detector_for_height_axis");
   if (!componentNameParam.empty())
     componentName = componentNameParam[0];
-
   getScatteringAngleBinning();
   getHeightAxis(componentName);
 }
@@ -205,6 +199,7 @@ void SumOverlappingTubes::getScatteringAngleBinning() {
 
 void SumOverlappingTubes::getHeightAxis(const std::string &componentName) {
   std::vector<double> heightBinning = getProperty("HeightAxis");
+  m_heightAxis.clear();
   if (componentName.length() == 0 && heightBinning.empty())
     throw std::runtime_error("No detector_for_height_axis parameter for this "
                              "instrument. Please enter a value for the "
@@ -269,6 +264,7 @@ SumOverlappingTubes::performBinning(MatrixWorkspace_sptr &outputWS) {
 
   // loop over all workspaces
   for (auto &ws : m_workspaceList) {
+    m_progress->report("Processing workspace " + std::string(ws->getName()));
     // loop over spectra
     const auto &specInfo = ws->spectrumInfo();
     for (size_t i = 0; i < specInfo.size(); ++i) {
@@ -298,8 +294,8 @@ SumOverlappingTubes::performBinning(MatrixWorkspace_sptr &outputWS) {
         angle = specInfo.signedTwoTheta(i);
       angle *= m_mirrorDetectors * 180.0 / M_PI;
 
-      int angleIndex =
-          int((angle - m_startScatteringAngle) / m_stepScatteringAngle + 0.5);
+      int angleIndex = boost::math::iround((angle - m_startScatteringAngle) /
+                                           m_stepScatteringAngle);
 
       // point is out of range, a warning should have been generated already for
       // the theta index
@@ -347,7 +343,9 @@ SumOverlappingTubes::performBinning(MatrixWorkspace_sptr &outputWS) {
         yData[angleIndex] += counts;
         eData[angleIndex] =
             sqrt(eData[angleIndex] * eData[angleIndex] + error * error);
-        normalisation[heightIndex][angleIndex]++;
+        if (counts != 0.) {
+          normalisation[heightIndex][angleIndex]++;
+        }
       }
     }
   }
diff --git a/Framework/Algorithms/test/ConjoinXRunsTest.h b/Framework/Algorithms/test/ConjoinXRunsTest.h
index 8133133d9614ad865bb76317ee54976b307c5e5a..fd1e397fdc023c13b3409f3dc3fe9a8e0d96696f 100644
--- a/Framework/Algorithms/test/ConjoinXRunsTest.h
+++ b/Framework/Algorithms/test/ConjoinXRunsTest.h
@@ -8,15 +8,26 @@
 #include "MantidAlgorithms/AddSampleLog.h"
 #include "MantidAlgorithms/AddTimeSeriesLog.h"
 #include "MantidAlgorithms/ConjoinXRuns.h"
+#include "MantidHistogramData/Counts.h"
+#include "MantidHistogramData/HistogramDx.h"
+#include "MantidHistogramData/HistogramE.h"
+#include "MantidHistogramData/HistogramX.h"
+#include "MantidHistogramData/HistogramY.h"
+#include "MantidHistogramData/Points.h"
 #include "MantidKernel/Unit.h"
 #include "MantidTestHelpers/WorkspaceCreationHelper.h"
 
 using Mantid::Algorithms::ConjoinXRuns;
 using Mantid::Algorithms::AddSampleLog;
 using Mantid::Algorithms::AddTimeSeriesLog;
+using Mantid::HistogramData::Counts;
+using Mantid::HistogramData::HistogramDx;
+using Mantid::HistogramData::HistogramE;
+using Mantid::HistogramData::HistogramX;
+using Mantid::HistogramData::HistogramY;
+using Mantid::HistogramData::Points;
 using namespace Mantid::API;
 using namespace WorkspaceCreationHelper;
-using namespace Mantid::HistogramData;
 
 class ConjoinXRunsTest : public CxxTest::TestSuite {
 public:
@@ -26,37 +37,33 @@ public:
   static void destroySuite(ConjoinXRunsTest *suite) { delete suite; }
 
   void setUp() override {
-    MatrixWorkspace_sptr ws1 = create2DWorkspace123(5, 3); // 3 points
-    MatrixWorkspace_sptr ws2 = create2DWorkspace154(5, 2); // 2 points
-    MatrixWorkspace_sptr ws3 = create2DWorkspace123(5, 1); // 1 point
-    MatrixWorkspace_sptr ws4 = create2DWorkspace154(5, 1); // 1 point
-    MatrixWorkspace_sptr ws5 = create2DWorkspace123(5, 3); // 3 points
-    MatrixWorkspace_sptr ws6 = create2DWorkspace123(5, 3); // 3 points
-
-    ws1->getAxis(0)->setUnit("TOF");
-    ws2->getAxis(0)->setUnit("TOF");
-    ws3->getAxis(0)->setUnit("TOF");
-    ws4->getAxis(0)->setUnit("TOF");
-    ws5->getAxis(0)->setUnit("TOF");
-    ws6->getAxis(0)->setUnit("TOF");
-
-    storeWS("ws1", ws1);
-    storeWS("ws2", ws2);
-    storeWS("ws3", ws3);
-    storeWS("ws4", ws4);
-    storeWS("ws5", ws5);
-    storeWS("ws6", ws6);
-
+    std::vector<MatrixWorkspace_sptr> ws(6);
+    // Workspaces have 5 spectra must be point data, don't have masks and have
+    // dx
+    ws[0] = create2DWorkspace123(5, 3, false, std::set<int64_t>(),
+                                 true); // 3 points
+    ws[1] = create2DWorkspace154(5, 2, false, std::set<int64_t>(),
+                                 true); // 2 points
+    ws[2] =
+        create2DWorkspace123(5, 1, false, std::set<int64_t>(), true); // 1 point
+    ws[3] =
+        create2DWorkspace154(5, 1, false, std::set<int64_t>(), true); // 1 point
+    ws[4] = create2DWorkspace123(5, 3, false, std::set<int64_t>(),
+                                 true); // 3 points
+    ws[5] = create2DWorkspace123(5, 3, false, std::set<int64_t>(),
+                                 true); // 3 points
     m_testWS = {"ws1", "ws2", "ws3", "ws4", "ws5", "ws6"};
+
+    for (unsigned int i = 0; i < ws.size(); ++i) {
+      ws[i]->getAxis(0)->setUnit("TOF");
+      storeWS(m_testWS[i], ws[i]);
+    }
   }
 
   void tearDown() override {
-    removeWS("ws1");
-    removeWS("ws2");
-    removeWS("ws3");
-    removeWS("ws4");
-    removeWS("ws5");
-    removeWS("ws6");
+    for (unsigned int i = 0; i < m_testWS.size(); ++i) {
+      removeWS(m_testWS[i]);
+    }
     m_testWS.clear();
   }
 
@@ -80,34 +87,43 @@ public:
     TS_ASSERT_EQUALS(out->blocksize(), 7);
     TS_ASSERT(!out->isHistogramData());
     TS_ASSERT_EQUALS(out->getAxis(0)->unit()->unitID(), "TOF");
+    std::vector<double> x{1., 2., 3., 1., 2., 1., 1.};
+    std::vector<double> y{2., 2., 2., 5., 5., 2., 5.};
+    std::vector<double> e{3., 3., 3., 4., 4., 3., 4.};
+    TS_ASSERT_EQUALS(out->x(0).rawData(), x);
+    TS_ASSERT_EQUALS(out->y(0).rawData(), y);
+    TS_ASSERT_EQUALS(out->e(0).rawData(), e);
+    TSM_ASSERT_EQUALS("Dx and y values are the same", out->dx(0).rawData(), y);
+  }
+
+  void testWSWithoutDxValues() {
+    // Workspaces have 5 spectra must be point data
+    MatrixWorkspace_sptr ws0 = create2DWorkspace123(5, 3); // 3 points
+    MatrixWorkspace_sptr ws1 = create2DWorkspace154(5, 2); // 2 points
+    ws0->getAxis(0)->setUnit("TOF");
+    ws1->getAxis(0)->setUnit("TOF");
+    storeWS("ws_0", ws0);
+    storeWS("ws_1", ws1);
+    m_testee.setProperty("InputWorkspaces",
+                         std::vector<std::string>{"ws_0", "ws_1"});
+    m_testee.setProperty("OutputWorkspace", "out");
+    TS_ASSERT_THROWS_NOTHING(m_testee.execute());
+    TS_ASSERT(m_testee.isExecuted());
+
+    MatrixWorkspace_sptr out =
+        AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>("out");
 
-    std::vector<double> spectrum = out->y(0).rawData();
-    std::vector<double> error = out->e(0).rawData();
-    std::vector<double> xaxis = out->x(0).rawData();
-
-    TS_ASSERT_EQUALS(spectrum[0], 2.);
-    TS_ASSERT_EQUALS(spectrum[1], 2.);
-    TS_ASSERT_EQUALS(spectrum[2], 2.);
-    TS_ASSERT_EQUALS(spectrum[3], 5.);
-    TS_ASSERT_EQUALS(spectrum[4], 5.);
-    TS_ASSERT_EQUALS(spectrum[5], 2.);
-    TS_ASSERT_EQUALS(spectrum[6], 5.);
-
-    TS_ASSERT_EQUALS(error[0], 3.);
-    TS_ASSERT_EQUALS(error[1], 3.);
-    TS_ASSERT_EQUALS(error[2], 3.);
-    TS_ASSERT_EQUALS(error[3], 4.);
-    TS_ASSERT_EQUALS(error[4], 4.);
-    TS_ASSERT_EQUALS(error[5], 3.);
-    TS_ASSERT_EQUALS(error[6], 4.);
-
-    TS_ASSERT_EQUALS(xaxis[0], 1.);
-    TS_ASSERT_EQUALS(xaxis[1], 2.);
-    TS_ASSERT_EQUALS(xaxis[2], 3.);
-    TS_ASSERT_EQUALS(xaxis[3], 1.);
-    TS_ASSERT_EQUALS(xaxis[4], 2.);
-    TS_ASSERT_EQUALS(xaxis[5], 1.);
-    TS_ASSERT_EQUALS(xaxis[6], 1.);
+    TS_ASSERT(out);
+    TS_ASSERT_EQUALS(out->getNumberHistograms(), 5);
+    TS_ASSERT_EQUALS(out->blocksize(), 5);
+    TS_ASSERT(!out->isHistogramData());
+    TS_ASSERT_EQUALS(out->getAxis(0)->unit()->unitID(), "TOF");
+    std::vector<double> x{1., 2., 3., 1., 2.};
+    std::vector<double> y{2., 2., 2., 5., 5.};
+    std::vector<double> e{3., 3., 3., 4., 4.};
+    TS_ASSERT_EQUALS(out->x(0).rawData(), x);
+    TS_ASSERT_EQUALS(out->y(0).rawData(), y);
+    TS_ASSERT_EQUALS(out->e(0).rawData(), e);
   }
 
   void testFailDifferentNumberBins() {
@@ -127,9 +143,11 @@ public:
     MatrixWorkspace_sptr ws6 =
         AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>("ws6");
 
-    Counts counts{{4, 9, 16}};
-    Points points{{0.4, 0.9, 1.1}};
-    ws6->setHistogram(3, points, counts);
+    for (auto i = 0; i < 5;
+         i++) { // modify all 5 spectra of ws6 in terms of y and x
+      ws6->mutableY(i) = {4., 9., 16.};
+      ws6->mutableX(i) = {0.4, 0.9, 1.1};
+    }
 
     m_testee.setProperty("InputWorkspaces",
                          std::vector<std::string>{"ws1", "ws6"});
@@ -146,55 +164,17 @@ public:
     TS_ASSERT(!out->isHistogramData());
     TS_ASSERT_EQUALS(out->getAxis(0)->unit()->unitID(), "TOF");
 
-    auto spectrum0 = out->y(0).rawData();
-    auto error0 = out->e(0).rawData();
-    auto xaxis0 = out->x(0).rawData();
-
-    TS_ASSERT_EQUALS(spectrum0[0], 2.);
-    TS_ASSERT_EQUALS(spectrum0[1], 2.);
-    TS_ASSERT_EQUALS(spectrum0[2], 2.);
-    TS_ASSERT_EQUALS(spectrum0[3], 2.);
-    TS_ASSERT_EQUALS(spectrum0[4], 2.);
-    TS_ASSERT_EQUALS(spectrum0[5], 2.);
-
-    TS_ASSERT_EQUALS(error0[0], 3.);
-    TS_ASSERT_EQUALS(error0[1], 3.);
-    TS_ASSERT_EQUALS(error0[2], 3.);
-    TS_ASSERT_EQUALS(error0[3], 3.);
-    TS_ASSERT_EQUALS(error0[4], 3.);
-    TS_ASSERT_EQUALS(error0[5], 3.);
-
-    TS_ASSERT_EQUALS(xaxis0[0], 1.);
-    TS_ASSERT_EQUALS(xaxis0[1], 2.);
-    TS_ASSERT_EQUALS(xaxis0[2], 3.);
-    TS_ASSERT_EQUALS(xaxis0[3], 1.);
-    TS_ASSERT_EQUALS(xaxis0[4], 2.);
-    TS_ASSERT_EQUALS(xaxis0[5], 3.);
-
-    auto spectrum3 = out->y(3).rawData();
-    auto error3 = out->e(3).rawData();
-    auto xaxis3 = out->x(3).rawData();
-
-    TS_ASSERT_EQUALS(spectrum3[0], 2.);
-    TS_ASSERT_EQUALS(spectrum3[1], 2.);
-    TS_ASSERT_EQUALS(spectrum3[2], 2.);
-    TS_ASSERT_EQUALS(spectrum3[3], 4.);
-    TS_ASSERT_EQUALS(spectrum3[4], 9.);
-    TS_ASSERT_EQUALS(spectrum3[5], 16.);
-
-    TS_ASSERT_EQUALS(error3[0], 3.);
-    TS_ASSERT_EQUALS(error3[1], 3.);
-    TS_ASSERT_EQUALS(error3[2], 3.);
-    TS_ASSERT_EQUALS(error3[3], 2.);
-    TS_ASSERT_EQUALS(error3[4], 3.);
-    TS_ASSERT_EQUALS(error3[5], 4.);
-
-    TS_ASSERT_EQUALS(xaxis3[0], 1.);
-    TS_ASSERT_EQUALS(xaxis3[1], 2.);
-    TS_ASSERT_EQUALS(xaxis3[2], 3.);
-    TS_ASSERT_EQUALS(xaxis3[3], 0.4);
-    TS_ASSERT_EQUALS(xaxis3[4], 0.9);
-    TS_ASSERT_EQUALS(xaxis3[5], 1.1);
+    std::vector<double> x_vec{1., 2., 3., .4, .9, 1.1};
+    std::vector<double> y_vec{2., 2., 2., 4., 9., 16.};
+    std::vector<double> e_vec{3., 3., 3., 3., 3., 3.};
+    std::vector<double> dx_vec{2., 2., 2., 2., 2., 2.};
+    // Check all 5 spectra
+    for (auto i = 0; i < 5; i++) {
+      TS_ASSERT_EQUALS(out->y(i).rawData(), y_vec);
+      TS_ASSERT_EQUALS(out->e(i).rawData(), e_vec);
+      TS_ASSERT_EQUALS(out->x(i).rawData(), x_vec);
+      TS_ASSERT_EQUALS(out->dx(i).rawData(), dx_vec);
+    }
   }
 
   void testFailWithNumLog() {
@@ -245,16 +225,12 @@ public:
     TS_ASSERT_EQUALS(out->getNumberHistograms(), 5);
     TS_ASSERT_EQUALS(out->getAxis(0)->unit()->unitID(), "Energy");
 
-    std::vector<double> xaxis = out->x(0).rawData();
-    std::vector<double> spectrum = out->y(0).rawData();
-    std::vector<double> error = out->e(0).rawData();
-
-    TS_ASSERT_EQUALS(xaxis[0], 0.7);
-    TS_ASSERT_EQUALS(xaxis[1], 1.1);
-    TS_ASSERT_EQUALS(spectrum[0], 2.);
-    TS_ASSERT_EQUALS(spectrum[1], 5.);
-    TS_ASSERT_EQUALS(error[0], 3.);
-    TS_ASSERT_EQUALS(error[1], 4.);
+    TS_ASSERT_EQUALS(out->x(0)[0], 0.7);
+    TS_ASSERT_EQUALS(out->x(0)[1], 1.1);
+    TS_ASSERT_EQUALS(out->y(0)[0], 2.);
+    TS_ASSERT_EQUALS(out->y(0)[1], 5.);
+    TS_ASSERT_EQUALS(out->e(0)[0], 3.);
+    TS_ASSERT_EQUALS(out->e(0)[1], 4.);
   }
 
   void testFailWithStringLog() {
@@ -315,27 +291,13 @@ public:
     TS_ASSERT_EQUALS(out->blocksize(), 5);
     TS_ASSERT_EQUALS(out->getNumberHistograms(), 5);
 
-    std::vector<double> spectrum = out->y(0).rawData();
-    std::vector<double> xaxis = out->x(0).rawData();
-    std::vector<double> error = out->e(0).rawData();
-
-    TS_ASSERT_EQUALS(spectrum[0], 2.);
-    TS_ASSERT_EQUALS(spectrum[1], 2.);
-    TS_ASSERT_EQUALS(spectrum[2], 2.);
-    TS_ASSERT_EQUALS(spectrum[3], 5.);
-    TS_ASSERT_EQUALS(spectrum[4], 5.);
-
-    TS_ASSERT_EQUALS(error[0], 3.);
-    TS_ASSERT_EQUALS(error[1], 3.);
-    TS_ASSERT_EQUALS(error[2], 3.);
-    TS_ASSERT_EQUALS(error[3], 4.);
-    TS_ASSERT_EQUALS(error[4], 4.);
-
-    TS_ASSERT_EQUALS(xaxis[0], 5.7);
-    TS_ASSERT_EQUALS(xaxis[1], 6.1);
-    TS_ASSERT_EQUALS(xaxis[2], 6.7);
-    TS_ASSERT_EQUALS(xaxis[3], 8.3);
-    TS_ASSERT_EQUALS(xaxis[4], 9.5);
+    std::vector<double> y_vec{2., 2., 2., 5., 5.};
+    std::vector<double> x_vec{5.7, 6.1, 6.7, 8.3, 9.5};
+    std::vector<double> e_vec{3., 3., 3., 4., 4.};
+    TS_ASSERT_EQUALS(out->y(0).rawData(), y_vec);
+    TS_ASSERT_EQUALS(out->x(0).rawData(), x_vec);
+    TS_ASSERT_EQUALS(out->e(0).rawData(), e_vec);
+    TS_ASSERT_EQUALS(out->dx(0).rawData(), y_vec)
   }
 
   void testFailWithNumSeriesLog() {
@@ -402,7 +364,8 @@ public:
   void setUp() override {
     m_ws.reserve(100);
     for (size_t i = 0; i < 100; ++i) {
-      MatrixWorkspace_sptr ws = create2DWorkspace123(10000, 100);
+      MatrixWorkspace_sptr ws =
+          create2DWorkspace123(2000, 100, false, std::set<int64_t>(), true);
       std::string name = "ws" + std::to_string(i);
       storeWS(name, ws);
       m_ws.push_back(name);
diff --git a/Framework/Algorithms/test/ConvertToPointDataTest.h b/Framework/Algorithms/test/ConvertToPointDataTest.h
index d9f2963fa683b27883395ee001fb817b272101e1..33a2a42f94137f5a76665ef3f902fb2bde5ab1cf 100644
--- a/Framework/Algorithms/test/ConvertToPointDataTest.h
+++ b/Framework/Algorithms/test/ConvertToPointDataTest.h
@@ -13,8 +13,6 @@ using Mantid::API::IAlgorithm_sptr;
 using Mantid::API::MatrixWorkspace;
 using Mantid::API::MatrixWorkspace_sptr;
 using Mantid::DataObjects::Workspace2D_sptr;
-using Mantid::HistogramData::HistogramDx;
-using Mantid::Kernel::make_cow;
 
 class ConvertToPointDataTest : public CxxTest::TestSuite {
 
@@ -106,8 +104,9 @@ public:
     double xBoundaries[11] = {0.0,  1.0,  3.0,  5.0,  6.0, 7.0,
                               10.0, 13.0, 16.0, 17.0, 17.5};
     const int numSpectra(2);
-    Workspace2D_sptr testWS = WorkspaceCreationHelper::create2DWorkspaceBinned(
-        numSpectra, 11, xBoundaries);
+    Workspace2D_sptr testWS =
+        WorkspaceCreationHelper::create2DWorkspaceNonUniformlyBinned(
+            numSpectra, 11, xBoundaries);
     const size_t numBins = testWS->blocksize();
     TS_ASSERT_EQUALS(testWS->isHistogramData(), true);
 
@@ -147,14 +146,12 @@ public:
     double xBoundaries[numBins] = {0.0,  1.0,  3.0,  5.0,  6.0, 7.0,
                                    10.0, 13.0, 16.0, 17.0, 17.5};
     constexpr int numSpectra{2};
-    Workspace2D_sptr testWS = WorkspaceCreationHelper::create2DWorkspaceBinned(
-        numSpectra, numBins, xBoundaries);
+    Workspace2D_sptr testWS =
+        WorkspaceCreationHelper::create2DWorkspaceNonUniformlyBinned(
+            numSpectra, numBins, xBoundaries, true);
     TS_ASSERT(testWS->isHistogramData())
     double xErrors[numBins - 1] = {0.1, 0.2, 0.3, 0.4, 0.5,
                                    0.6, 0.7, 0.8, 0.9, 1.0};
-    auto dxs = make_cow<HistogramDx>(xErrors, xErrors + numBins - 1);
-    testWS->setSharedDx(0, dxs);
-    testWS->setSharedDx(1, dxs);
     MatrixWorkspace_sptr outputWS = runAlgorithm(testWS);
     TS_ASSERT(outputWS)
     TS_ASSERT(!outputWS->isHistogramData())
@@ -163,7 +160,7 @@ public:
       const auto &dx = outputWS->dx(i);
       TS_ASSERT_EQUALS(dx.size(), numBins - 1)
       for (size_t j = 0; j < dx.size(); ++j) {
-        TS_ASSERT_EQUALS(dx[j], xErrors[j])
+        TS_ASSERT_DELTA(dx[j], xErrors[j], 1E-16);
       }
     }
   }
diff --git a/Framework/Algorithms/test/CopyInstrumentParametersTest.h b/Framework/Algorithms/test/CopyInstrumentParametersTest.h
index 6109c023b2a07f06eedd00f32a98864ad31746be..4a5b449f498d632b0680169d54022ead2d385197 100644
--- a/Framework/Algorithms/test/CopyInstrumentParametersTest.h
+++ b/Framework/Algorithms/test/CopyInstrumentParametersTest.h
@@ -3,32 +3,25 @@
 
 #include <cxxtest/TestSuite.h>
 
-#include "MantidDataHandling/LoadInstrument.h"
-#include "MantidAPI/IAlgorithm.h"
 #include "MantidAlgorithms/CopyInstrumentParameters.h"
-#include "MantidAPI/Workspace.h"
-#include "MantidDataObjects/Workspace2D.h"
-#include "MantidGeometry/Instrument/DetectorInfo.h"
+#include "MantidAPI/AnalysisDataService.h"
+#include "MantidAPI/IAlgorithm.h"
 #include "MantidAPI/MatrixWorkspace.h"
 #include "MantidAPI/SpectrumInfo.h"
 #include "MantidAPI/WorkspaceFactory.h"
-#include "WorkspaceCreationHelperTest.h"
-#include "MantidAPI/AnalysisDataService.h"
-#include "MantidAPI/ITableWorkspace.h"
-#include "MantidAPI/TableRow.h"
 #include "MantidKernel/V3D.h"
 #include "MantidGeometry/Instrument.h"
 #include "MantidGeometry/Instrument/Component.h"
-#include "MantidDataHandling/LoadEmptyInstrument.h"
+#include "MantidGeometry/Instrument/DetectorInfo.h"
+#include "MantidTestHelpers/WorkspaceCreationHelper.h"
 #include <cmath>
 #include <stdexcept>
 
 using namespace Mantid::Algorithms;
 using namespace Mantid::API;
-using namespace Mantid::Kernel;
-using namespace Mantid::DataObjects;
-using Mantid::Geometry::IDetector_const_sptr;
-using Mantid::Geometry::IComponent_const_sptr;
+using Mantid::Geometry::Instrument_const_sptr;
+using Mantid::Geometry::ParameterMap;
+using Mantid::Kernel::V3D;
 
 class CopyInstrumentParametersTest : public CxxTest::TestSuite {
 public:
@@ -62,10 +55,10 @@ public:
     TS_ASSERT_THROWS_NOTHING(
         copyInstParam.setPropertyValue("OutputWorkspace", wsName2));
     // Get instrument of input workspace and move some detectors
-    Geometry::ParameterMap *pmap;
+    ParameterMap *pmap;
     pmap = &(ws1->instrumentParameters());
     auto &detectorInfoWs1 = ws1->mutableDetectorInfo();
-    Geometry::Instrument_const_sptr instrument = ws1->getInstrument();
+    Instrument_const_sptr instrument = ws1->getInstrument();
     detectorInfoWs1.setPosition(0, V3D(6.0, 0.0, 0.7));
     detectorInfoWs1.setPosition(1, V3D(6.0, 0.1, 0.7));
 
@@ -119,8 +112,8 @@ public:
     AnalysisDataServiceImpl &dataStore = AnalysisDataService::Instance();
     dataStore.add(wsName1, ws1);
 
-    Geometry::Instrument_const_sptr instrument = ws1->getInstrument();
-    Geometry::ParameterMap *pmap;
+    Instrument_const_sptr instrument = ws1->getInstrument();
+    ParameterMap *pmap;
     pmap = &(ws1->instrumentParameters());
     // add auxiliary instrument parameters
     pmap->addDouble(instrument.get(), "Ei", 100);
@@ -218,8 +211,8 @@ public:
     AnalysisDataServiceImpl &dataStore = AnalysisDataService::Instance();
     dataStore.add(m_SourceWSName, ws1);
 
-    Geometry::Instrument_const_sptr instrument = ws1->getInstrument();
-    Geometry::ParameterMap *pmap;
+    Instrument_const_sptr instrument = ws1->getInstrument();
+    ParameterMap *pmap;
     pmap = &(ws1->instrumentParameters());
     for (size_t i = 0; i < n_Parameters; i++) {
       // add auxiliary instrument parameters
@@ -230,7 +223,8 @@ public:
     // calibrate detectors;
     auto &detectorInfo = ws1->mutableDetectorInfo();
     for (size_t i = 0; i < n_detectors; i++) {
-      auto detIndex = detectorInfo.indexOf(static_cast<Mantid::detid_t>(i + 1));
+      size_t detIndex =
+          detectorInfo.indexOf(static_cast<Mantid::detid_t>(i + 1));
       detectorInfo.setPosition(
           detIndex, V3D(sin(M_PI * double(i)), cos(M_PI * double(i / 500)), 7));
     }
@@ -270,7 +264,7 @@ public:
 
     AnalysisDataServiceImpl &dataStore = AnalysisDataService::Instance();
     MatrixWorkspace_sptr ws2 =
-        dataStore.retrieveWS<API::MatrixWorkspace>(m_TargetWSName);
+        dataStore.retrieveWS<MatrixWorkspace>(m_TargetWSName);
     auto instr2 = ws2->getInstrument();
 
     auto param_names = instr2->getParameterNames();
diff --git a/Framework/Algorithms/test/ExtractMaskTest.h b/Framework/Algorithms/test/ExtractMaskTest.h
index e9803cf60e6c508e7227aaed47f558044a704f42..94d2c8503d51653c5e4398497cafaaeae39d1004 100644
--- a/Framework/Algorithms/test/ExtractMaskTest.h
+++ b/Framework/Algorithms/test/ExtractMaskTest.h
@@ -6,6 +6,7 @@
 #include "MantidAlgorithms/ExtractMask.h"
 #include "MantidDataObjects/MaskWorkspace.h"
 #include "MantidTestHelpers/WorkspaceCreationHelper.h"
+#include "MantidAPI/SpectrumInfo.h"
 
 using namespace Mantid::API;
 using namespace Mantid::DataObjects;
@@ -59,6 +60,67 @@ public:
     AnalysisDataService::Instance().remove(outputWS->getName());
   }
 
+  void test_That_Masked_Detector_List_Populated_When_Passed_A_Mask_Workspace() {
+    // Create a simple test workspace
+    const int nvectors(50), nbins(10);
+    Workspace2D_sptr inputWS =
+        WorkspaceCreationHelper::create2DWorkspace(nvectors, nbins);
+    // Mask every 10th spectra
+    std::set<int64_t> maskedIndices;
+    for (int i = 0; i < 50; i += 10) {
+      maskedIndices.insert(i);
+    }
+    // A few randoms
+    maskedIndices.insert(5);
+    maskedIndices.insert(23);
+    maskedIndices.insert(37);
+    inputWS = WorkspaceCreationHelper::maskSpectra(inputWS, maskedIndices);
+    const std::string inputName("inputWSMask");
+    AnalysisDataService::Instance().add(inputName, inputWS);
+    MaskWorkspace_sptr inputWSMask = runExtractMask(inputName);
+    std::vector<Mantid::detid_t> expectedDetectorList = {1,  6,  11, 21,
+                                                         24, 31, 38, 41};
+
+    std::vector<Mantid::detid_t> detectorList;
+
+    TS_ASSERT_THROWS_NOTHING(
+        detectorList = runExtractMaskReturnList(inputWSMask->getName()));
+    TS_ASSERT_EQUALS(detectorList, expectedDetectorList)
+
+    AnalysisDataService::Instance().remove(inputName);
+    AnalysisDataService::Instance().remove(inputWSMask->getName());
+  }
+
+  void test_That_Masked_Detector_List_Populated_When_Passed_A_Workspace() {
+    // Create a simple test workspace
+    const int nvectors(50), nbins(10);
+    Workspace2D_sptr inputWS =
+        WorkspaceCreationHelper::create2DWorkspace(nvectors, nbins);
+    // Mask every 10th spectra
+    std::set<int64_t> maskedIndices;
+    for (int i = 0; i < 50; i += 10) {
+      maskedIndices.insert(i);
+    }
+    // A few randoms
+    maskedIndices.insert(5);
+    maskedIndices.insert(23);
+    maskedIndices.insert(37);
+    inputWS = WorkspaceCreationHelper::maskSpectra(inputWS, maskedIndices);
+    const std::string inputName("inputWS");
+    AnalysisDataService::Instance().add(inputName, inputWS);
+
+    std::vector<Mantid::detid_t> expectedDetectorList = {1,  6,  11, 21,
+                                                         24, 31, 38, 41};
+
+    std::vector<Mantid::detid_t> detectorList;
+
+    TS_ASSERT_THROWS_NOTHING(detectorList =
+                                 runExtractMaskReturnList(inputName));
+    TS_ASSERT_EQUALS(detectorList, expectedDetectorList)
+
+    AnalysisDataService::Instance().remove(inputName);
+  }
+
 private:
   // The input workspace should be in the analysis data service
   MaskWorkspace_sptr runExtractMask(const std::string &inputName) {
@@ -82,6 +144,21 @@ private:
     }
   }
 
+  std::vector<Mantid::detid_t>
+  runExtractMaskReturnList(const std::string &inputName) {
+    ExtractMask maskExtractor;
+    maskExtractor.initialize();
+    maskExtractor.setPropertyValue("InputWorkspace", inputName);
+    const std::string outputName("masking");
+    maskExtractor.setPropertyValue("OutputWorkspace", outputName);
+    maskExtractor.setRethrows(true);
+    maskExtractor.execute();
+    std::vector<Mantid::detid_t> detectorList =
+        maskExtractor.getProperty("DetectorList");
+    AnalysisDataService::Instance().remove(outputName);
+    return detectorList;
+  }
+
   void doTest(MatrixWorkspace_const_sptr inputWS,
               MaskWorkspace_const_sptr outputWS) {
     TS_ASSERT_EQUALS(outputWS->blocksize(), 1);
diff --git a/Framework/Algorithms/test/MergeRunsTest.h b/Framework/Algorithms/test/MergeRunsTest.h
index bbdde628d2bbd91664c7e438943547a8b0530bce..a895e35e8d63a90189ea519d0595e90f03181e12 100644
--- a/Framework/Algorithms/test/MergeRunsTest.h
+++ b/Framework/Algorithms/test/MergeRunsTest.h
@@ -3,25 +3,21 @@
 
 #include "MantidTestHelpers/WorkspaceCreationHelper.h"
 #include <cxxtest/TestSuite.h>
-#include "MantidTestHelpers/WorkspaceCreationHelper.h"
 #include <stdarg.h>
 
 #include "MantidAPI/AnalysisDataService.h"
 #include "MantidGeometry/Instrument/DetectorInfo.h"
 #include "MantidAPI/SpectrumInfo.h"
-#include "MantidAPI/WorkspaceGroup.h"
 #include "MantidAPI/MatrixWorkspace.h"
 #include "MantidAPI/WorkspaceGroup.h"
 #include "MantidAlgorithms/GroupWorkspaces.h"
 #include "MantidAlgorithms/MergeRuns.h"
-#include "MantidAlgorithms/GroupWorkspaces.h"
 #include "MantidAlgorithms/Rebin.h"
 #include "MantidGeometry/Instrument.h"
 #include "MantidAPI/AnalysisDataService.h"
 #include "MantidKernel/TimeSeriesProperty.h"
 #include <boost/make_shared.hpp>
 #include <boost/shared_ptr.hpp>
-#include <boost/make_shared.hpp>
 #include <MantidAlgorithms/RunCombinationHelpers/RunCombinationHelper.h>
 #include <MantidAlgorithms/RunCombinationHelpers/SampleLogsBehaviour.h>
 #include "MantidTypes/SpectrumDefinition.h"
@@ -305,17 +301,17 @@ public:
 
   MergeRunsTest() {
     AnalysisDataService::Instance().add(
-        "in1", WorkspaceCreationHelper::create2DWorkspaceBinned(3, 10, 1));
+        "in1", WorkspaceCreationHelper::create2DWorkspaceBinned(3, 10, 1.));
     AnalysisDataService::Instance().add(
-        "in2", WorkspaceCreationHelper::create2DWorkspaceBinned(3, 10, 1));
+        "in2", WorkspaceCreationHelper::create2DWorkspaceBinned(3, 10, 1.));
     AnalysisDataService::Instance().add(
-        "in3", WorkspaceCreationHelper::create2DWorkspaceBinned(3, 10, 1));
+        "in3", WorkspaceCreationHelper::create2DWorkspaceBinned(3, 10, 1.));
     AnalysisDataService::Instance().add(
-        "in4", WorkspaceCreationHelper::create2DWorkspaceBinned(3, 5, 20));
+        "in4", WorkspaceCreationHelper::create2DWorkspaceBinned(3, 5, 20.));
     AnalysisDataService::Instance().add(
-        "in5", WorkspaceCreationHelper::create2DWorkspaceBinned(3, 5, 3.5, 2));
+        "in5", WorkspaceCreationHelper::create2DWorkspaceBinned(3, 5, 3.5, 2.));
     AnalysisDataService::Instance().add(
-        "in6", WorkspaceCreationHelper::create2DWorkspaceBinned(3, 3, 2, 2));
+        "in6", WorkspaceCreationHelper::create2DWorkspaceBinned(3, 3, 2., 2.));
   }
 
   void checkOutput(std::string wsname) {
diff --git a/Framework/Algorithms/test/RunCombinationHelperTest.h b/Framework/Algorithms/test/RunCombinationHelperTest.h
index 3545278c02b8566f13f1ef4bde72be731ae3138d..154f72f6e45db91f3c45b0b4f9cf854e5b245218 100644
--- a/Framework/Algorithms/test/RunCombinationHelperTest.h
+++ b/Framework/Algorithms/test/RunCombinationHelperTest.h
@@ -9,15 +9,18 @@
 #include "MantidAPI/Axis.h"
 #include "MantidAPI/MatrixWorkspace.h"
 #include "MantidGeometry/Instrument/DetectorInfo.h"
+#include "MantidHistogramData/HistogramDx.h"
 #include "MantidAlgorithms/CreateSampleWorkspace.h"
 #include "MantidAlgorithms/GroupWorkspaces.h"
 #include "MantidKernel/UnitFactory.h"
+#include "MantidKernel/make_cow.h"
 #include "MantidTestHelpers/WorkspaceCreationHelper.h"
 
 using Mantid::Algorithms::RunCombinationHelper;
 using Mantid::Algorithms::GroupWorkspaces;
 using Mantid::Algorithms::CreateSampleWorkspace;
 using namespace Mantid::API;
+using namespace Mantid::HistogramData;
 using namespace Mantid::Kernel;
 using namespace WorkspaceCreationHelper;
 
@@ -122,6 +125,16 @@ public:
                      "different X units; different spectrum axis units; ");
   }
 
+  void testIncompatibleDx() {
+    // create a workspace where spectrum 1 has Dx
+    MatrixWorkspace_sptr ws2 = m_reference->clone();
+    auto dx =
+        Mantid::Kernel::make_cow<Mantid::HistogramData::HistogramDx>(3, 0.2);
+    ws2->setSharedDx(1, dx);
+    TS_ASSERT_EQUALS(m_testee.checkCompatibility(ws2),
+                     "spectra must have either Dx values or not; ");
+  }
+
   void test_scanning_workspaces_throw_no_error() {
     const auto scanWS1 = createSampleScanningWorkspace(2);
     const auto scanWS2 = createSampleScanningWorkspace(2);
diff --git a/Framework/Algorithms/test/SumOverlappingTubesTest.h b/Framework/Algorithms/test/SumOverlappingTubesTest.h
index c1b6ff578798d6e851ef6fc587e671c81df28f25..cee05ac312192a436b356c94376469b6d3a004d3 100644
--- a/Framework/Algorithms/test/SumOverlappingTubesTest.h
+++ b/Framework/Algorithms/test/SumOverlappingTubesTest.h
@@ -5,15 +5,15 @@
 
 #include "MantidAlgorithms/SumOverlappingTubes.h"
 
-#include "MantidAPI/Axis.h"
 #include "MantidAPI/AnalysisDataService.h"
+#include "MantidAPI/Axis.h"
 #include "MantidAPI/WorkspaceGroup.h"
 #include "MantidDataObjects/ScanningWorkspaceBuilder.h"
 #include "MantidDataObjects/Workspace2D.h"
 #include "MantidDataObjects/WorkspaceCreation.h"
+#include "MantidGeometry/Instrument/DetectorInfo.h"
 #include "MantidHistogramData/LinearGenerator.h"
 #include "MantidIndexing/IndexInfo.h"
-#include "MantidGeometry/Instrument/DetectorInfo.h"
 #include "MantidTestHelpers/ComponentCreationHelper.h"
 #include "MantidTypes/Core/DateAndTime.h"
 
@@ -98,14 +98,15 @@ public:
     return testWS;
   }
 
-  void verifySuccessCase(double expectedCounts = 2.0) {
+  void verifySuccessCase(double expectedCounts = 2.0,
+                         double expectedErrors = sqrt(2.0)) {
     MatrixWorkspace_sptr outWS =
         boost::dynamic_pointer_cast<Mantid::API::MatrixWorkspace>(
             AnalysisDataService::Instance().retrieve("outWS"));
 
     verifyScatteringAngleAxis(outWS, N_TUBES);
     verifyHeightAxis(outWS);
-    verifySpectraHaveSameCounts(outWS, expectedCounts);
+    verifySpectraHaveSameCounts(outWS, expectedCounts, expectedErrors);
   }
 
   void verifyScatteringAngleAxis(const MatrixWorkspace_sptr &outWS,
@@ -127,13 +128,13 @@ public:
 
   void verifySpectraHaveSameCounts(MatrixWorkspace_sptr outWS,
                                    double expectedCounts = 2.0,
+                                   double expectedErrors = sqrt(2.0),
                                    bool checkErrors = true) {
     for (size_t i = 0; i < N_TUBES; ++i)
       for (size_t j = 0; j < N_PIXELS_PER_TUBE; ++j) {
         TS_ASSERT_DELTA(outWS->getSpectrum(j).y()[i], expectedCounts, 1e-6)
         if (checkErrors)
-          TS_ASSERT_DELTA(outWS->getSpectrum(j).e()[i], sqrt(expectedCounts),
-                          1e-6)
+          TS_ASSERT_DELTA(outWS->getSpectrum(j).e()[i], expectedErrors, 1e-6)
       }
   }
 
@@ -196,6 +197,7 @@ public:
     alg.setProperty("OutputWorkspace", "outWS");
     alg.setProperty("OutputType", "2DTubes");
     alg.setProperty("ScatteringAngleBinning", "22.5");
+    alg.setProperty("MirrorScatteringAngles", true);
     TS_ASSERT_THROWS_NOTHING(alg.execute());
 
     MatrixWorkspace_sptr outWS =
@@ -209,7 +211,7 @@ public:
       TS_ASSERT_DELTA(xAxis->getValue(i), 22.5 * double(i), 1e-6)
 
     verifyHeightAxis(outWS);
-    verifySpectraHaveSameCounts(outWS, 2.0);
+    verifySpectraHaveSameCounts(outWS, 2.0, sqrt(2.0));
 
     AnalysisDataService::Instance().remove("testWS");
     AnalysisDataService::Instance().remove("outWS");
@@ -318,7 +320,7 @@ public:
     alg.setProperty("ScatteringAngleBinning", "22.5");
     TS_ASSERT_THROWS_NOTHING(alg.execute());
 
-    verifySuccessCase(6.0);
+    verifySuccessCase(2.0, sqrt(6) / 3.);
 
     AnalysisDataService::Instance().remove("testWS");
     AnalysisDataService::Instance().remove("outWS");
@@ -490,7 +492,7 @@ public:
     verifyScatteringAngleAxis(outWS, N_TUBES + 2);
     verifyHeightAxis(outWS);
 
-    verifySpectraHaveSameCounts(outWS, 6.0, false);
+    verifySpectraHaveSameCounts(outWS, 2.0, sqrt(2.0), false);
 
     AnalysisDataService::Instance().remove("testWS");
     AnalysisDataService::Instance().remove("outWS");
@@ -518,35 +520,33 @@ public:
 
     size_t bin = 0;
     for (size_t j = 0; j < N_PIXELS_PER_TUBE; ++j) {
-      TS_ASSERT_DELTA(outWS->getSpectrum(j).y()[bin], 6.0, 1e-6)
-      TS_ASSERT_DELTA(outWS->getSpectrum(j).e()[bin], sqrt(2.0) * 3.0, 1e-6)
+      TS_ASSERT_DELTA(outWS->getSpectrum(j).y()[bin], 2.0, 1e-6)
+      TS_ASSERT_DELTA(outWS->getSpectrum(j).e()[bin], sqrt(2.0), 1e-6)
     }
 
     bin = 1;
     for (size_t j = 0; j < N_PIXELS_PER_TUBE; ++j) {
-      TS_ASSERT_DELTA(outWS->getSpectrum(j).y()[bin], 6.0, 1e-6)
-      TS_ASSERT_DELTA(outWS->getSpectrum(j).e()[bin], sqrt(4.0) * 3.0 / 2.0,
-                      1e-6)
+      TS_ASSERT_DELTA(outWS->getSpectrum(j).y()[bin], 2.0, 1e-6)
+      TS_ASSERT_DELTA(outWS->getSpectrum(j).e()[bin], sqrt(4.0) / 2.0, 1e-6)
     }
 
     for (size_t i = 2; i < 5; ++i) {
       for (size_t j = 0; j < N_PIXELS_PER_TUBE; ++j) {
-        TS_ASSERT_DELTA(outWS->getSpectrum(j).y()[i], 6.0, 1e-6)
-        TS_ASSERT_DELTA(outWS->getSpectrum(j).e()[i], sqrt(6.0), 1e-6)
+        TS_ASSERT_DELTA(outWS->getSpectrum(j).y()[i], 2.0, 1e-6)
+        TS_ASSERT_DELTA(outWS->getSpectrum(j).e()[i], sqrt(6.0) / 3., 1e-6)
       }
     }
 
     bin = 5;
     for (size_t j = 0; j < N_PIXELS_PER_TUBE; ++j) {
-      TS_ASSERT_DELTA(outWS->getSpectrum(j).y()[bin], 6.0, 1e-6)
-      TS_ASSERT_DELTA(outWS->getSpectrum(j).e()[bin], sqrt(4.0) * 3.0 / 2.0,
-                      1e-6)
+      TS_ASSERT_DELTA(outWS->getSpectrum(j).y()[bin], 2.0, 1e-6)
+      TS_ASSERT_DELTA(outWS->getSpectrum(j).e()[bin], sqrt(4.0) / 2.0, 1e-6)
     }
 
     bin = 6;
     for (size_t j = 0; j < N_PIXELS_PER_TUBE; ++j) {
-      TS_ASSERT_DELTA(outWS->getSpectrum(j).y()[bin], 6.0, 1e-6)
-      TS_ASSERT_DELTA(outWS->getSpectrum(j).e()[bin], sqrt(2.0) * 3.0, 1e-6)
+      TS_ASSERT_DELTA(outWS->getSpectrum(j).y()[bin], 2.0, 1e-6)
+      TS_ASSERT_DELTA(outWS->getSpectrum(j).e()[bin], sqrt(2.0), 1e-6)
     }
 
     AnalysisDataService::Instance().remove("testWS");
@@ -567,6 +567,7 @@ public:
     alg.setProperty("Normalise", false);
     if (oneDimensional)
       alg.setProperty("OutputType", "1D");
+    alg.setProperty("MirrorScatteringAngles", false);
     TS_ASSERT_THROWS_NOTHING(alg.execute());
 
     auto outWS = boost::dynamic_pointer_cast<Mantid::API::MatrixWorkspace>(
diff --git a/Framework/Algorithms/test/WorkspaceCreationHelperTest.h b/Framework/Algorithms/test/WorkspaceCreationHelperTest.h
index f56173e6a3f265fb1237bd961ed00aa21cbe32b5..e788871524e4bfade6d5977e735470f3d60f3c72 100644
--- a/Framework/Algorithms/test/WorkspaceCreationHelperTest.h
+++ b/Framework/Algorithms/test/WorkspaceCreationHelperTest.h
@@ -1,14 +1,9 @@
 #ifndef MANTID_ALGORITHMS_WORKSPACECREATIONHELPERTEST_H_
 #define MANTID_ALGORITHMS_WORKSPACECREATIONHELPERTEST_H_
 
-#include "MantidKernel/System.h"
-#include "MantidKernel/Timer.h"
 #include "MantidTestHelpers/WorkspaceCreationHelper.h"
 #include <cxxtest/TestSuite.h>
-#include "MantidAPI/SpectraDetectorTypes.h"
 
-using namespace Mantid;
-using namespace Mantid::API;
 using namespace Mantid::DataObjects;
 
 /** Test class for the helpers in MantidTestHelpers/WorkspaceCreationHelper.h */
@@ -36,6 +31,19 @@ public:
     TS_ASSERT(ws->getSpectrum(0).hasDetectorID(100));
     TS_ASSERT(ws->getSpectrum(1).hasDetectorID(101));
   }
+
+  void test_create2DWorkspaceWithValues() {
+    Workspace2D_sptr ws = WorkspaceCreationHelper::create2DWorkspace123(
+        1, 2, false, std::set<int64_t>(), true);
+    TS_ASSERT(ws);
+    TS_ASSERT_EQUALS(ws->getNumberHistograms(), 1);
+    TS_ASSERT_EQUALS(ws->size(), 2);
+    TS_ASSERT(ws->hasDx(0));
+    TS_ASSERT_EQUALS(ws->dx(0).rawData()[0], 2.);
+    Workspace2D_sptr ws2 = WorkspaceCreationHelper::create2DWorkspace123(
+        1, 2, false, std::set<int64_t>(), false);
+    TS_ASSERT(!ws2->hasDx(0));
+  }
 };
 
 #endif /* MANTID_ALGORITHMS_WORKSPACECREATIONHELPERTEST_H_ */
diff --git a/Framework/Crystal/test/SCDCalibratePanelsTest.h b/Framework/Crystal/test/SCDCalibratePanelsTest.h
index 68b8026d4f6d0a59a632744f00e868dd3b0e4478..3113192c978c7ba6de20ae2428dbcb9e1a8acd58 100644
--- a/Framework/Crystal/test/SCDCalibratePanelsTest.h
+++ b/Framework/Crystal/test/SCDCalibratePanelsTest.h
@@ -17,7 +17,6 @@ using namespace Mantid::DataObjects;
 using namespace std;
 using namespace Mantid::Geometry;
 using namespace Mantid::Kernel;
-using namespace Mantid::Geometry;
 using namespace Mantid::Crystal;
 
 class SCDCalibratePanelsTest : public CxxTest::TestSuite {
diff --git a/Framework/CurveFitting/src/Algorithms/SplineInterpolation.cpp b/Framework/CurveFitting/src/Algorithms/SplineInterpolation.cpp
index 032f4748d1a0764e4f556317605b300211e91748..a07084ca021906b3802759fb64cf7f4de504a41d 100644
--- a/Framework/CurveFitting/src/Algorithms/SplineInterpolation.cpp
+++ b/Framework/CurveFitting/src/Algorithms/SplineInterpolation.cpp
@@ -93,22 +93,32 @@ std::map<std::string, std::string> SplineInterpolation::validateInputs() {
   const int derivOrder = getProperty("DerivOrder");
 
   MatrixWorkspace_const_sptr iwsValid = getProperty("WorkspaceToInterpolate");
-  const size_t binsNo = iwsValid->blocksize();
-
-  // The minimum number of points for cubic splines is 3
-  if (binsNo < 2) {
-    result["WorkspaceToInterpolate"] = "Workspace must have minimum 2 points.";
-  } else if (binsNo == 2) {
-    if (!linear) {
-      result["WorkspaceToInterpolate"] =
-          "Workspace has only 2 points, "
-          "you can enable linear interpolation by "
-          "setting the property Linear2Points. Otherwise "
-          "provide a minimum of 3 points.";
-    } else if (derivOrder == 2) {
-      result["DerivOrder"] = "Linear interpolation is requested, hence "
-                             "derivative order can be maximum 1.";
+  if (iwsValid) {
+    try {
+      const size_t binsNo = iwsValid->blocksize();
+
+      // The minimum number of points for cubic splines is 3
+      if (binsNo < 2) {
+        result["WorkspaceToInterpolate"] =
+            "Workspace must have minimum 2 points.";
+      } else if (binsNo == 2) {
+        if (!linear) {
+          result["WorkspaceToInterpolate"] =
+              "Workspace has only 2 points, "
+              "you can enable linear interpolation by "
+              "setting the property Linear2Points. Otherwise "
+              "provide a minimum of 3 points.";
+        } else if (derivOrder == 2) {
+          result["DerivOrder"] = "Linear interpolation is requested, hence "
+                                 "derivative order can be maximum 1.";
+        }
+      }
+    } catch (std::length_error &) {
+      result["WorkspaceToInterpolate"] = "The input workspace does not have "
+                                         "the same number of bins per spectrum";
     }
+  } else {
+    result["WorkspaceToInterpolate"] = "The input is not a MatrixWorkspace";
   }
 
   return result;
diff --git a/Framework/DataHandling/src/LoadILLDiffraction.cpp b/Framework/DataHandling/src/LoadILLDiffraction.cpp
index 0921cf4c7f71484e52fc822a4bdba99eee163e34..181ef561a500f05cfd95882da5f6b995df8b901d 100644
--- a/Framework/DataHandling/src/LoadILLDiffraction.cpp
+++ b/Framework/DataHandling/src/LoadILLDiffraction.cpp
@@ -752,6 +752,8 @@ void LoadILLDiffraction::setSampleLogs() {
   double lambda = run.getLogAsSingleValue("wavelength");
   double eFixed = WAVE_TO_E / (lambda * lambda);
   run.addLogData(new PropertyWithValue<double>("Ei", eFixed));
+  run.addLogData(new PropertyWithValue<size_t>("NumberOfDetectors",
+                                               m_numberDetectorsActual));
 }
 
 /**
diff --git a/Framework/DataHandling/test/MaskDetectorsTest.h b/Framework/DataHandling/test/MaskDetectorsTest.h
index a23e6800cf403ec0dc631646f78a6790c550042e..caee36084d2b7066b68d67be6dd327de4fbb36d0 100644
--- a/Framework/DataHandling/test/MaskDetectorsTest.h
+++ b/Framework/DataHandling/test/MaskDetectorsTest.h
@@ -668,7 +668,7 @@ public:
     // Make workspace to act as mask
     const auto numMaskWSSpec = inputWS->getInstrument()->getNumberDetectors();
     auto maskWs = WorkspaceCreationHelper::create2DWorkspaceBinned(
-        static_cast<int>(numMaskWSSpec), 1, 0, 0);
+        static_cast<int>(numMaskWSSpec), 1, 0., 0.);
     maskWs->setInstrument(inputWS->getInstrument());
     for (size_t i = 0; i < maskWs->getNumberHistograms(); ++i) {
       maskWs->mutableY(i)[0] = 1.0;
@@ -736,7 +736,7 @@ public:
     // Make workspace to act as mask
     const auto numMaskWSSpec = inputWS->getInstrument()->getNumberDetectors();
     auto maskWs = WorkspaceCreationHelper::create2DWorkspaceBinned(
-        static_cast<int>(numMaskWSSpec), 1, 0, 0);
+        static_cast<int>(numMaskWSSpec), 1, 0., 0.);
     maskWs->setInstrument(inputWS->getInstrument());
     for (size_t i = 0; i < maskWs->getNumberHistograms(); ++i) {
       maskWs->mutableY(i)[0] = 1.0;
diff --git a/Framework/LiveData/inc/MantidLiveData/ISIS/FakeISISEventDAE.h b/Framework/LiveData/inc/MantidLiveData/ISIS/FakeISISEventDAE.h
index 2f648a9df8f0af3c299aa202f5ffbaae08b195ce..c9cb0496b4b23823da4079fc323fcac2d0b5def6 100644
--- a/Framework/LiveData/inc/MantidLiveData/ISIS/FakeISISEventDAE.h
+++ b/Framework/LiveData/inc/MantidLiveData/ISIS/FakeISISEventDAE.h
@@ -6,23 +6,14 @@
 //----------------------------------------------------------------------
 #include "MantidAPI/Algorithm.h"
 
-#include <mutex>
-
-namespace Poco {
-namespace Net {
-class TCPServer;
-}
-}
-
 namespace Mantid {
 namespace LiveData {
 /**
     Simulates ISIS histogram DAE. It runs continuously until canceled and
-   listens to port 6789 for
-    ISIS DAE commands.
+    listens to port 6789 for ISIS DAE commands.
 
     Copyright &copy; 2008-9 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge
-   National Laboratory & European Spallation Source
+    National Laboratory & European Spallation Source
 
     This file is part of Mantid.
 
@@ -44,9 +35,6 @@ namespace LiveData {
 */
 class FakeISISEventDAE : public API::Algorithm {
 public:
-  FakeISISEventDAE();
-  ~FakeISISEventDAE() override;
-
   /// Algorithm's name for identification overriding a virtual method
   const std::string name() const override { return "FakeISISEventDAE"; }
   /// Algorithm's version for identification overriding a virtual method
@@ -67,10 +55,6 @@ public:
 private:
   void init() override;
   void exec() override;
-  /// Poco TCP server
-  Poco::Net::TCPServer *m_server;
-  /// Mutex
-  std::mutex m_mutex;
 };
 
 } // namespace LiveData
diff --git a/Framework/LiveData/inc/MantidLiveData/ISIS/FakeISISHistoDAE.h b/Framework/LiveData/inc/MantidLiveData/ISIS/FakeISISHistoDAE.h
index 87a7dab447a2c2c066a911ee8d8f3f127b0e6117..84c187739b3ef62cf0f3e7736b2cb1d3ef26d2d4 100644
--- a/Framework/LiveData/inc/MantidLiveData/ISIS/FakeISISHistoDAE.h
+++ b/Framework/LiveData/inc/MantidLiveData/ISIS/FakeISISHistoDAE.h
@@ -5,30 +5,22 @@
 // Includes
 //----------------------------------------------------------------------
 #include "MantidAPI/Algorithm.h"
-#include <mutex>
-
-namespace Poco {
-namespace Net {
-class TCPServer;
-}
-}
 
 namespace Mantid {
 namespace LiveData {
 /**
     Simulates ISIS histogram DAE. It runs continuously until canceled and
-   listens to port 6789 for
-    ISIS DAE commands.
+    listens to port 6789 for ISIS DAE commands.
 
     Data is generated starting at 10000 microseconds Time of flight, and each
-   bin requested covers 100 microseconds.
+    bin requested covers 100 microseconds.
     The algorithm silently defines three additional spectra with numbers
-   NSpectra+1, NSpectra+2 and NSpectra+3 in a
+    NSpectra+1, NSpectra+2 and NSpectra+3 in a
     different time regime (they have different binning to the rest of the
-   spectra).
+    spectra).
 
     Copyright &copy; 2008-9 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge
-   National Laboratory & European Spallation Source
+    National Laboratory & European Spallation Source
 
     This file is part of Mantid.
 
@@ -50,13 +42,10 @@ namespace LiveData {
 */
 class DLLExport FakeISISHistoDAE : public API::Algorithm {
 public:
-  FakeISISHistoDAE();
-  ~FakeISISHistoDAE() override;
-
   /// Algorithm's name for identification overriding a virtual method
-  const std::string name() const override { return "FakeISISHistoDAE"; };
+  const std::string name() const override { return "FakeISISHistoDAE"; }
   /// Algorithm's version for identification overriding a virtual method
-  int version() const override { return 1; };
+  int version() const override { return 1; }
   /// Algorithm's category for identification overriding a virtual method
   const std::string category() const override {
     return "DataHandling\\DataAcquisition";
@@ -73,10 +62,6 @@ private:
   // Implement abstract Algorithm methods
   void init() override;
   void exec() override;
-  /// Poco TCP server
-  Poco::Net::TCPServer *m_server;
-  /// Mutex
-  std::mutex m_mutex;
 };
 
 } // namespace LiveData
diff --git a/Framework/LiveData/inc/MantidLiveData/ISIS/ISISLiveEventDataListener.h b/Framework/LiveData/inc/MantidLiveData/ISIS/ISISLiveEventDataListener.h
index 1609026442d0f8d06652f84f7bdeaebc058afcfd..d773d374a82a23d3e0bb528461c6ef68fe77e0d9 100644
--- a/Framework/LiveData/inc/MantidLiveData/ISIS/ISISLiveEventDataListener.h
+++ b/Framework/LiveData/inc/MantidLiveData/ISIS/ISISLiveEventDataListener.h
@@ -19,7 +19,7 @@
 const long RECV_TIMEOUT = 30;
 // Sleep time in case we need to wait for the data to become available (in
 // milliseconds)
-const long RECV_WAIT = 1;
+const long RECV_WAIT = 10;
 
 //----------------------------------------------------------------------
 // Forward declarations
@@ -164,6 +164,8 @@ protected:
   void Receive(T &buffer, const std::string &head, const std::string &msg) {
     long timeout = 0;
     while (m_socket.available() < static_cast<int>(sizeof(buffer))) {
+      if (m_stopThread)
+        return;
       Poco::Thread::sleep(RECV_WAIT);
       timeout += RECV_WAIT;
       if (timeout > RECV_TIMEOUT * 1000)
@@ -190,7 +192,7 @@ protected:
   /// Thread that reads events from the DAE in the background
   Poco::Thread m_thread;
   /// background thread checks this periodically.  If true, the thread exits
-  bool m_stopThread;
+  std::atomic<bool> m_stopThread;
   /// Holds on to any exceptions that were thrown in the background thread so
   /// that we
   /// can re-throw them in the forground thread
diff --git a/Framework/LiveData/src/ISIS/FakeISISEventDAE.cpp b/Framework/LiveData/src/ISIS/FakeISISEventDAE.cpp
index 4e22d55816a660cb0447323e3be79c1c4abd5420..b16acedc7e39170baae9503a9164573812576e00 100644
--- a/Framework/LiveData/src/ISIS/FakeISISEventDAE.cpp
+++ b/Framework/LiveData/src/ISIS/FakeISISEventDAE.cpp
@@ -7,15 +7,12 @@
 #include "MantidKernel/MersenneTwister.h"
 #include "MantidKernel/Timer.h"
 
+#include <Poco/ActiveResult.h>
 #include <Poco/Net/TCPServer.h>
 #include <Poco/Net/StreamSocket.h>
-#include <Poco/ActiveResult.h>
-#include <Poco/Thread.h>
 
 #include <boost/random/uniform_int.hpp>
 
-#include <numeric>
-
 namespace Mantid {
 namespace LiveData {
 // Register the algorithm into the algorithm factory
@@ -147,17 +144,6 @@ public:
 };
 } // end anonymous
 
-/// (Empty) Constructor
-FakeISISEventDAE::FakeISISEventDAE() : m_server(nullptr) {}
-
-/// Destructor
-FakeISISEventDAE::~FakeISISEventDAE() {
-  if (m_server) {
-    m_server->stop();
-    delete m_server;
-  }
-}
-
 /**
 * Declare the algorithm properties
 */
@@ -198,19 +184,18 @@ void FakeISISEventDAE::exec() {
   histoDAE->setProperty("NPeriods", nper);
   histoDAE->setProperty("NSpectra", nspec);
   histoDAE->setProperty("Port", port + 1);
-  Poco::ActiveResult<bool> histoDAEHandle = histoDAE->executeAsync();
+  auto histoDAEHandle = histoDAE->executeAsync();
 
   auto prog = boost::make_shared<Progress>(this, 0.0, 1.0, 100);
   prog->setNotifyStep(0);
   prog->report(0, "Waiting for client");
-  std::lock_guard<std::mutex> lock(m_mutex);
   Poco::Net::ServerSocket socket(static_cast<Poco::UInt16>(port));
   socket.listen();
-  m_server = new Poco::Net::TCPServer(
+  Poco::Net::TCPServer server(
       TestServerConnectionFactory::Ptr(
           new TestServerConnectionFactory(nper, nspec, rate, nevents, prog)),
       socket);
-  m_server->start();
+  server.start();
   // Keep going until you get cancelled
   while (true) {
     try {
@@ -223,19 +208,12 @@ void FakeISISEventDAE::exec() {
     // Sleep for 50 msec
     Poco::Thread::sleep(50);
   }
+  // It's most likely that we got here from a cancel request
+  // so calling prog->report after this point
+  // will generate another CancelException
   histoDAE->cancel();
   histoDAEHandle.wait();
-  if (m_server) {
-    m_server->stop();
-    m_server = nullptr;
-  }
   socket.close();
-
-  prog->report(90, "Closing ISIS event DAE");
-  histoDAE->setLogging(false); // hide the final closedown message to the log it
-                               // is confusing as it is a child alg.
-  histoDAE->cancel();
-  histoDAEHandle.wait();
 }
 
 } // namespace LiveData
diff --git a/Framework/LiveData/src/ISIS/FakeISISHistoDAE.cpp b/Framework/LiveData/src/ISIS/FakeISISHistoDAE.cpp
index bb9b11d2eeaa89eb54ca98966863c3bf395d31c9..bea46be21c0ad16c5abbae6e96493b28a7a01bb6 100644
--- a/Framework/LiveData/src/ISIS/FakeISISHistoDAE.cpp
+++ b/Framework/LiveData/src/ISIS/FakeISISHistoDAE.cpp
@@ -2,11 +2,9 @@
 // Includes
 //----------------------------------------------------------------------
 #include "MantidLiveData/ISIS/FakeISISHistoDAE.h"
-#include <numeric>
 
 #include <Poco/Net/TCPServer.h>
 #include <Poco/Net/StreamSocket.h>
-#include <Poco/Thread.h>
 
 namespace Mantid {
 namespace LiveData {
@@ -314,17 +312,6 @@ public:
 using namespace Kernel;
 using namespace API;
 
-/// (Empty) Constructor
-FakeISISHistoDAE::FakeISISHistoDAE() : m_server(nullptr) {}
-
-/// Destructor
-FakeISISHistoDAE::~FakeISISHistoDAE() {
-  if (m_server) {
-    m_server->stop();
-    delete m_server;
-  }
-}
-
 /**
  * Declare the algorithm properties
  */
@@ -352,16 +339,15 @@ void FakeISISHistoDAE::exec() {
   int nbins = getProperty("NBins");
   int port = getProperty("Port");
 
-  std::lock_guard<std::mutex> lock(m_mutex);
   Poco::Net::ServerSocket socket(static_cast<Poco::UInt16>(port));
   socket.listen();
 
-  m_server = new Poco::Net::TCPServer(
+  Poco::Net::TCPServer server(
       TestServerConnectionFactory::Ptr(
           new TestServerConnectionFactory(nper, nspec, nbins)),
       socket);
-  m_server->start();
-  // Keep going until you get cancelled
+  server.start();
+  // Keep going until you get cancelled or an error occurs
   while (true) {
     try {
       // Exit if the user presses cancel
@@ -374,10 +360,10 @@ void FakeISISHistoDAE::exec() {
     // Sleep for 50 msec
     Poco::Thread::sleep(50);
   }
-  if (m_server) {
-    m_server->stop();
-    m_server = nullptr;
-  }
+  // It's most likely that we got here from a cancel request
+  // so calling prog->report after this point
+  // will generate another CancelException
+  server.stop();
   socket.close();
 }
 
diff --git a/Framework/LiveData/src/ISIS/ISISLiveEventDataListener.cpp b/Framework/LiveData/src/ISIS/ISISLiveEventDataListener.cpp
index ef62b701c44ff04e8463b0294632f56dea03f25f..53c620436f3292a33d09d648cac9145a94bcde34 100644
--- a/Framework/LiveData/src/ISIS/ISISLiveEventDataListener.cpp
+++ b/Framework/LiveData/src/ISIS/ISISLiveEventDataListener.cpp
@@ -235,6 +235,8 @@ void ISISLiveEventDataListener::run() {
       // get the header with the type of the packet
       Receive(events.head, "Events header",
               "Corrupt stream - you should reconnect.");
+      if (m_stopThread)
+        break;
       if (!(events.head.type == TCPStreamEventHeader::Neutron)) {
         // don't know what to do with it - stop
         throw std::runtime_error("Unknown packet type.");
@@ -244,6 +246,8 @@ void ISISLiveEventDataListener::run() {
       // get the header with the sream size
       Receive(events.head_n, "Neutrons header",
               "Corrupt stream - you should reconnect.");
+      if (m_stopThread)
+        break;
       CollectJunk(events.head_n);
 
       // absolute pulse (frame) time
@@ -282,8 +286,7 @@ void ISISLiveEventDataListener::run() {
       saveEvents(events.data, pulseTime, events.head_n.period);
     }
 
-  } catch (std::runtime_error &
-               e) { // exception handler for generic runtime exceptions
+  } catch (std::runtime_error &e) {
 
     g_log.error() << "Caught a runtime exception.\nException message: "
                   << e.what() << '\n';
@@ -291,8 +294,8 @@ void ISISLiveEventDataListener::run() {
 
     m_backgroundException = boost::make_shared<std::runtime_error>(e);
 
-  } catch (std::invalid_argument &
-               e) { // TimeSeriesProperty (and possibly some other things) can
+  } catch (std::invalid_argument &e) {
+    // TimeSeriesProperty (and possibly some other things) can
     // can throw these errors
     g_log.error()
         << "Caught an invalid argument exception.\nException message: "
@@ -303,7 +306,7 @@ void ISISLiveEventDataListener::run() {
     newMsg += e.what();
     m_backgroundException = boost::make_shared<std::runtime_error>(newMsg);
 
-  } catch (...) { // Default exception handler
+  } catch (...) {
     g_log.error() << "Uncaught exception in ISISLiveEventDataListener network "
                      "read thread.\n";
     m_isConnected = false;
diff --git a/Framework/MDAlgorithms/CMakeLists.txt b/Framework/MDAlgorithms/CMakeLists.txt
index 20b3cfbe911f6b4164ca141a535f12f669ca97f6..f35029deb15a40a29f217bfb3a328d1b448de380 100644
--- a/Framework/MDAlgorithms/CMakeLists.txt
+++ b/Framework/MDAlgorithms/CMakeLists.txt
@@ -1,383 +1,386 @@
 # GLOBs should be replaced with explicit listings
 set ( SRC_FILES
-    # Old TMP convertToMD code prepared for deprecation:
-    # src/CreateMDFitWorkspace.cpp
-    #end TMP
-    src/AccumulateMD.cpp
-    src/AndMD.cpp
-    src/BaseConvertToDiffractionMDWorkspace.cpp
-    src/BinMD.cpp
-    src/BinaryOperationMD.cpp
-    src/BooleanBinaryOperationMD.cpp
-    src/BoxControllerSettingsAlgorithm.cpp
-    src/CalculateCoverageDGS.cpp
-    src/CentroidPeaksMD.cpp
-    src/CentroidPeaksMD2.cpp
-    src/ChangeQConvention.cpp
-    src/CloneMDWorkspace.cpp
-    src/CompactMD.cpp
-    src/CompareMDWorkspaces.cpp
-    src/ConvToMDBase.cpp
-    src/ConvToMDEventsWS.cpp
-    src/ConvToMDHistoWS.cpp
-    src/ConvToMDSelector.cpp
-    src/ConvertCWPDMDToSpectra.cpp
-    src/ConvertCWSDExpToMomentum.cpp
-    src/ConvertCWSDMDtoHKL.cpp
-    src/ConvertMDHistoToMatrixWorkspace.cpp
-    src/ConvertSpiceDataToRealSpace.cpp
-    src/ConvertToDetectorFaceMD.cpp
-    src/ConvertToDiffractionMDWorkspace.cpp
-    src/ConvertToDiffractionMDWorkspace2.cpp
-    src/ConvertToDiffractionMDWorkspace3.cpp
-    src/ConvertToMD.cpp
-    src/ConvertToMDMinMaxGlobal.cpp
-    src/ConvertToMDMinMaxLocal.cpp
-    src/ConvertToMDParent.cpp
-    src/ConvertToReflectometryQ.cpp
-    src/CreateMD.cpp
-    src/CreateMDHistoWorkspace.cpp
-    src/CreateMDWorkspace.cpp
-    src/CutMD.cpp
-    src/DisplayNormalizationSetter.cpp
-    src/DivideMD.cpp
-    src/EqualToMD.cpp
-    src/EvaluateMDFunction.cpp
-    src/ExponentialMD.cpp
-    src/FakeMDEventData.cpp
-    src/FindPeaksMD.cpp
-    src/FitMD.cpp
-    src/GetSpiceDataRawCountsFromMD.cpp
-    src/GreaterThanMD.cpp
-    src/IDynamicRebinning.cpp
-    src/ImportMDEventWorkspace.cpp
-    src/ImportMDHistoWorkspace.cpp
-    src/ImportMDHistoWorkspaceBase.cpp
-    src/Integrate3DEvents.cpp
-    src/IntegrateEllipsoids.cpp
-    src/IntegrateEllipsoidsTwoStep.cpp
-    src/IntegrateFlux.cpp
-    src/IntegrateMDHistoWorkspace.cpp
-    src/IntegratePeaksMD.cpp
-    src/IntegratePeaksMD2.cpp
-    src/IntegratePeaksMDHKL.cpp
-    src/IntegratePeaksCWSD.cpp
-    src/InvalidParameter.cpp
-    src/InvalidParameterParser.cpp
-    src/LessThanMD.cpp
-    src/LoadMD.cpp
-    src/LoadSQW.cpp
-    src/LoadSQW2.cpp
-    src/LogarithmMD.cpp
-    src/MDEventWSWrapper.cpp
-    src/MDNormDirectSC.cpp
-    src/MDNormSCD.cpp
-    src/MDTransfAxisNames.cpp
-    src/MDTransfFactory.cpp
-    src/MDTransfModQ.cpp
-    src/MDTransfNoQ.cpp
-    src/MDTransfQ3D.cpp
-    src/MDWSDescription.cpp
-    src/MDWSTransform.cpp
-    src/MaskMD.cpp
-    src/MergeMD.cpp
-    src/MergeMDFiles.cpp
-    src/MinusMD.cpp
-    src/MultiplyMD.cpp
-    src/NotMD.cpp
-    src/OneStepMDEW.cpp
-    src/OrMD.cpp
-    src/PlusMD.cpp
-    src/PowerMD.cpp
-    src/PreprocessDetectorsToMD.cpp
-    src/Quantification/CachedExperimentInfo.cpp
-    src/Quantification/FitResolutionConvolvedModel.cpp
-    src/Quantification/ForegroundModel.cpp
-    src/Quantification/ForegroundModelFactory.cpp
-    src/Quantification/MDResolutionConvolution.cpp
-    src/Quantification/MDResolutionConvolutionFactory.cpp
-    src/Quantification/Models/MullerAnsatz.cpp
-    src/Quantification/Models/QCoordinate.cpp
-    src/Quantification/Models/Strontium122.cpp
-    src/Quantification/Resolution/ModeratorChopperResolution.cpp
-    src/Quantification/Resolution/TobyFitBMatrix.cpp
-    src/Quantification/Resolution/TobyFitResolutionModel.cpp
-    src/Quantification/Resolution/TobyFitYVector.cpp
-    src/Quantification/ResolutionConvolvedCrossSection.cpp
-    src/Quantification/SimulateResolutionConvolvedModel.cpp
-    src/QueryMDWorkspace.cpp
-    src/ReflectometryTransformKiKf.cpp
-    src/ReflectometryTransformP.cpp
-    src/ReflectometryTransformQxQz.cpp
-    src/ReplicateMD.cpp
-    src/SaveIsawQvector.cpp
-    src/SaveMD.cpp
-    src/SaveMD2.cpp
-    src/SaveZODS.cpp
-    src/SetMDFrame.cpp
-    src/SetMDUsingMask.cpp
-    src/SliceMD.cpp
-    src/SlicingAlgorithm.cpp
-    src/SmoothMD.cpp
-    src/ThresholdMD.cpp
-    src/TransformMD.cpp
-    src/TransposeMD.cpp
-    src/UnaryOperationMD.cpp
-    src/UnitsConversionHelper.cpp
-    src/UserFunctionMD.cpp
-    src/WeightedMeanMD.cpp
-    src/XorMD.cpp
-    )
+	# Old TMP convertToMD code prepared for deprecation:
+	# src/CreateMDFitWorkspace.cpp
+	#end TMP
+	src/AccumulateMD.cpp
+	src/AndMD.cpp
+	src/BaseConvertToDiffractionMDWorkspace.cpp
+	src/BinMD.cpp
+	src/BinaryOperationMD.cpp
+	src/BooleanBinaryOperationMD.cpp
+	src/BoxControllerSettingsAlgorithm.cpp
+	src/CalculateCoverageDGS.cpp
+	src/CentroidPeaksMD.cpp
+	src/CentroidPeaksMD2.cpp
+	src/ChangeQConvention.cpp
+	src/CloneMDWorkspace.cpp
+	src/CompactMD.cpp
+	src/CompareMDWorkspaces.cpp
+	src/ConvToMDBase.cpp
+	src/ConvToMDEventsWS.cpp
+	src/ConvToMDHistoWS.cpp
+	src/ConvToMDSelector.cpp
+	src/ConvertCWPDMDToSpectra.cpp
+	src/ConvertCWSDExpToMomentum.cpp
+	src/ConvertCWSDMDtoHKL.cpp
+	src/ConvertMDHistoToMatrixWorkspace.cpp
+	src/ConvertSpiceDataToRealSpace.cpp
+	src/ConvertToDetectorFaceMD.cpp
+	src/ConvertToDiffractionMDWorkspace.cpp
+	src/ConvertToDiffractionMDWorkspace2.cpp
+	src/ConvertToDiffractionMDWorkspace3.cpp
+	src/ConvertToMD.cpp
+	src/ConvertToMDMinMaxGlobal.cpp
+	src/ConvertToMDMinMaxLocal.cpp
+	src/ConvertToMDParent.cpp
+	src/ConvertToReflectometryQ.cpp
+	src/CreateMD.cpp
+	src/CreateMDHistoWorkspace.cpp
+	src/CreateMDWorkspace.cpp
+	src/CutMD.cpp
+	src/DisplayNormalizationSetter.cpp
+	src/DivideMD.cpp
+	src/EqualToMD.cpp
+	src/EvaluateMDFunction.cpp
+	src/ExponentialMD.cpp
+	src/FakeMDEventData.cpp
+	src/FindPeaksMD.cpp
+	src/FitMD.cpp
+	src/GetSpiceDataRawCountsFromMD.cpp
+	src/GreaterThanMD.cpp
+	src/IDynamicRebinning.cpp
+	src/ImportMDEventWorkspace.cpp
+	src/ImportMDHistoWorkspace.cpp
+	src/ImportMDHistoWorkspaceBase.cpp
+	src/Integrate3DEvents.cpp
+	src/IntegrateEllipsoids.cpp
+	src/IntegrateEllipsoidsTwoStep.cpp
+	src/IntegrateFlux.cpp
+	src/IntegrateMDHistoWorkspace.cpp
+	src/IntegratePeaksCWSD.cpp
+	src/IntegratePeaksMD.cpp
+	src/IntegratePeaksMD2.cpp
+	src/IntegratePeaksMDHKL.cpp
+	src/InvalidParameter.cpp
+	src/InvalidParameterParser.cpp
+	src/LessThanMD.cpp
+	src/LoadDNSSCD.cpp
+	src/LoadMD.cpp
+	src/LoadSQW.cpp
+	src/LoadSQW2.cpp
+	src/LogarithmMD.cpp
+	src/MDEventWSWrapper.cpp
+	src/MDNormDirectSC.cpp
+	src/MDNormSCD.cpp
+	src/MDTransfAxisNames.cpp
+	src/MDTransfFactory.cpp
+	src/MDTransfModQ.cpp
+	src/MDTransfNoQ.cpp
+	src/MDTransfQ3D.cpp
+	src/MDWSDescription.cpp
+	src/MDWSTransform.cpp
+	src/MaskMD.cpp
+	src/MergeMD.cpp
+	src/MergeMDFiles.cpp
+	src/MinusMD.cpp
+	src/MultiplyMD.cpp
+	src/NotMD.cpp
+	src/OneStepMDEW.cpp
+	src/OrMD.cpp
+	src/PlusMD.cpp
+	src/PowerMD.cpp
+	src/PreprocessDetectorsToMD.cpp
+	src/Quantification/CachedExperimentInfo.cpp
+	src/Quantification/FitResolutionConvolvedModel.cpp
+	src/Quantification/ForegroundModel.cpp
+	src/Quantification/ForegroundModelFactory.cpp
+	src/Quantification/MDResolutionConvolution.cpp
+	src/Quantification/MDResolutionConvolutionFactory.cpp
+	src/Quantification/Models/MullerAnsatz.cpp
+	src/Quantification/Models/QCoordinate.cpp
+	src/Quantification/Models/Strontium122.cpp
+	src/Quantification/Resolution/ModeratorChopperResolution.cpp
+	src/Quantification/Resolution/TobyFitBMatrix.cpp
+	src/Quantification/Resolution/TobyFitResolutionModel.cpp
+	src/Quantification/Resolution/TobyFitYVector.cpp
+	src/Quantification/ResolutionConvolvedCrossSection.cpp
+	src/Quantification/SimulateResolutionConvolvedModel.cpp
+	src/QueryMDWorkspace.cpp
+	src/ReflectometryTransformKiKf.cpp
+	src/ReflectometryTransformP.cpp
+	src/ReflectometryTransformQxQz.cpp
+	src/ReplicateMD.cpp
+	src/SaveIsawQvector.cpp
+	src/SaveMD.cpp
+	src/SaveMD2.cpp
+	src/SaveZODS.cpp
+	src/SetMDFrame.cpp
+	src/SetMDUsingMask.cpp
+	src/SliceMD.cpp
+	src/SlicingAlgorithm.cpp
+	src/SmoothMD.cpp
+	src/ThresholdMD.cpp
+	src/TransformMD.cpp
+	src/TransposeMD.cpp
+	src/UnaryOperationMD.cpp
+	src/UnitsConversionHelper.cpp
+	src/UserFunctionMD.cpp
+	src/WeightedMeanMD.cpp
+	src/XorMD.cpp
+)
 
 #set ( SRC_UNITY_IGNORE_FILES src/IDynamicRebinning.cpp
 #)
 
 set ( INC_FILES
-    inc/MantidMDAlgorithms/AccumulateMD.h
-    inc/MantidMDAlgorithms/AndMD.h
-    inc/MantidMDAlgorithms/BaseConvertToDiffractionMDWorkspace.h
-    inc/MantidMDAlgorithms/BinMD.h
-    inc/MantidMDAlgorithms/BinaryOperationMD.h
-    inc/MantidMDAlgorithms/BooleanBinaryOperationMD.h
-    inc/MantidMDAlgorithms/BoxControllerSettingsAlgorithm.h
-    inc/MantidMDAlgorithms/CalculateCoverageDGS.h
-    inc/MantidMDAlgorithms/CentroidPeaksMD.h
-    inc/MantidMDAlgorithms/CentroidPeaksMD2.h
-    inc/MantidMDAlgorithms/ChangeQConvention.h
-    inc/MantidMDAlgorithms/CloneMDWorkspace.h
-    inc/MantidMDAlgorithms/CompactMD.h
-    inc/MantidMDAlgorithms/CompareMDWorkspaces.h
-    inc/MantidMDAlgorithms/ConvToMDBase.h
-    inc/MantidMDAlgorithms/ConvertCWPDMDToSpectra.h
-    inc/MantidMDAlgorithms/ConvertCWSDExpToMomentum.h
-    inc/MantidMDAlgorithms/ConvertCWSDMDtoHKL.h
-    inc/MantidMDAlgorithms/ConvertMDHistoToMatrixWorkspace.h
-    inc/MantidMDAlgorithms/ConvertSpiceDataToRealSpace.h
-    inc/MantidMDAlgorithms/ConvertToDetectorFaceMD.h
-    inc/MantidMDAlgorithms/ConvertToDiffractionMDWorkspace.h
-    inc/MantidMDAlgorithms/ConvertToDiffractionMDWorkspace2.h
-    inc/MantidMDAlgorithms/ConvertToDiffractionMDWorkspace3.h
-    inc/MantidMDAlgorithms/ConvertToMD.h
-    inc/MantidMDAlgorithms/ConvertToMDMinMaxGlobal.h
-    inc/MantidMDAlgorithms/ConvertToMDMinMaxLocal.h
-    inc/MantidMDAlgorithms/ConvertToMDParent.h
-    inc/MantidMDAlgorithms/ConvertToReflectometryQ.h
-    inc/MantidMDAlgorithms/CreateMD.h
-    inc/MantidMDAlgorithms/CreateMDHistoWorkspace.h
-    inc/MantidMDAlgorithms/CreateMDWorkspace.h
-    inc/MantidMDAlgorithms/CutMD.h
-    inc/MantidMDAlgorithms/DisplayNormalizationSetter.h
-    inc/MantidMDAlgorithms/DivideMD.h
-    inc/MantidMDAlgorithms/DllConfig.h
-    inc/MantidMDAlgorithms/EqualToMD.h
-    inc/MantidMDAlgorithms/EvaluateMDFunction.h
-    inc/MantidMDAlgorithms/ExponentialMD.h
-    inc/MantidMDAlgorithms/FakeMDEventData.h
-    inc/MantidMDAlgorithms/FindPeaksMD.h
-    inc/MantidMDAlgorithms/FitMD.h
-    inc/MantidMDAlgorithms/GSLFunctions.h
-    inc/MantidMDAlgorithms/GetSpiceDataRawCountsFromMD.h
-    inc/MantidMDAlgorithms/GreaterThanMD.h
-    inc/MantidMDAlgorithms/IDynamicRebinning.h
-    inc/MantidMDAlgorithms/ImportMDEventWorkspace.h
-    inc/MantidMDAlgorithms/ImportMDHistoWorkspace.h
-    inc/MantidMDAlgorithms/ImportMDHistoWorkspaceBase.h
-    inc/MantidMDAlgorithms/Integrate3DEvents.h
-    inc/MantidMDAlgorithms/IntegrateEllipsoids.h
-    inc/MantidMDAlgorithms/IntegrateEllipsoidsTwoStep.h
-    inc/MantidMDAlgorithms/IntegrateFlux.h
-    inc/MantidMDAlgorithms/IntegrateMDHistoWorkspace.h
-    inc/MantidMDAlgorithms/IntegratePeaksMD.h
-    inc/MantidMDAlgorithms/IntegratePeaksMD2.h
-    inc/MantidMDAlgorithms/IntegratePeaksMDHKL.h
-    inc/MantidMDAlgorithms/IntegratePeaksCWSD.h
-    inc/MantidMDAlgorithms/InvalidParameter.h
-    inc/MantidMDAlgorithms/InvalidParameterParser.h
-    inc/MantidMDAlgorithms/LessThanMD.h
-    inc/MantidMDAlgorithms/LoadMD.h
-    inc/MantidMDAlgorithms/LoadSQW.h
-    inc/MantidMDAlgorithms/LoadSQW2.h
-    inc/MantidMDAlgorithms/LogarithmMD.h
-    inc/MantidMDAlgorithms/MDEventWSWrapper.h
-    inc/MantidMDAlgorithms/MDNormDirectSC.h
-    inc/MantidMDAlgorithms/MDNormSCD.h
-    inc/MantidMDAlgorithms/MDTransfAxisNames.h
-    inc/MantidMDAlgorithms/MDTransfFactory.h
-    inc/MantidMDAlgorithms/MDTransfInterface.h
-    inc/MantidMDAlgorithms/MDTransfModQ.h
-    inc/MantidMDAlgorithms/MDTransfNoQ.h
-    inc/MantidMDAlgorithms/MDTransfQ3D.h
-    inc/MantidMDAlgorithms/MDWSDescription.h
-    inc/MantidMDAlgorithms/MDWSTransform.h
-    inc/MantidMDAlgorithms/MaskMD.h
-    inc/MantidMDAlgorithms/MergeMD.h
-    inc/MantidMDAlgorithms/MergeMDFiles.h
-    inc/MantidMDAlgorithms/MinusMD.h
-    inc/MantidMDAlgorithms/MultiplyMD.h
-    inc/MantidMDAlgorithms/NotMD.h
-    inc/MantidMDAlgorithms/OneStepMDEW.h
-    inc/MantidMDAlgorithms/OrMD.h
-    inc/MantidMDAlgorithms/PlusMD.h
-    inc/MantidMDAlgorithms/PowerMD.h
-    inc/MantidMDAlgorithms/PreprocessDetectorsToMD.h
-    inc/MantidMDAlgorithms/Quantification/CachedExperimentInfo.h
-    inc/MantidMDAlgorithms/Quantification/FitResolutionConvolvedModel.h
-    inc/MantidMDAlgorithms/Quantification/ForegroundModel.h
-    inc/MantidMDAlgorithms/Quantification/ForegroundModelFactory.h
-    inc/MantidMDAlgorithms/Quantification/MDResolutionConvolution.h
-    inc/MantidMDAlgorithms/Quantification/MDResolutionConvolutionFactory.h
-    inc/MantidMDAlgorithms/Quantification/Models/MullerAnsatz.h
-    inc/MantidMDAlgorithms/Quantification/Models/QCoordinate.h
-    inc/MantidMDAlgorithms/Quantification/Models/Strontium122.h
-    inc/MantidMDAlgorithms/Quantification/Resolution/ModeratorChopperResolution.h
-    inc/MantidMDAlgorithms/Quantification/Resolution/TobyFitBMatrix.h
-    inc/MantidMDAlgorithms/Quantification/Resolution/TobyFitResolutionModel.h
-    inc/MantidMDAlgorithms/Quantification/Resolution/TobyFitYVector.h
-    inc/MantidMDAlgorithms/Quantification/ResolutionConvolvedCrossSection.h
-    inc/MantidMDAlgorithms/Quantification/SimulateResolutionConvolvedModel.h
-    inc/MantidMDAlgorithms/QueryMDWorkspace.h
-    inc/MantidMDAlgorithms/ReflectometryTransformKiKf.h
-    inc/MantidMDAlgorithms/ReflectometryTransformP.h
-    inc/MantidMDAlgorithms/ReflectometryTransformQxQz.h
-    inc/MantidMDAlgorithms/ReplicateMD.h
-    inc/MantidMDAlgorithms/SaveIsawQvector.h
-    inc/MantidMDAlgorithms/SaveMD.h
-    inc/MantidMDAlgorithms/SaveMD2.h
-    inc/MantidMDAlgorithms/SaveZODS.h
-    inc/MantidMDAlgorithms/SetMDFrame.h
-    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/TransposeMD.h
-    inc/MantidMDAlgorithms/UnaryOperationMD.h
-    inc/MantidMDAlgorithms/UnitsConversionHelper.h
-    inc/MantidMDAlgorithms/Vector3DParameter.h
-    inc/MantidMDAlgorithms/Vector3DParameterParser.h
-    inc/MantidMDAlgorithms/WeightedMeanMD.h
-    inc/MantidMDAlgorithms/XorMD.h
-    )
+	inc/MantidMDAlgorithms/AccumulateMD.h
+	inc/MantidMDAlgorithms/AndMD.h
+	inc/MantidMDAlgorithms/BaseConvertToDiffractionMDWorkspace.h
+	inc/MantidMDAlgorithms/BinMD.h
+	inc/MantidMDAlgorithms/BinaryOperationMD.h
+	inc/MantidMDAlgorithms/BooleanBinaryOperationMD.h
+	inc/MantidMDAlgorithms/BoxControllerSettingsAlgorithm.h
+	inc/MantidMDAlgorithms/CalculateCoverageDGS.h
+	inc/MantidMDAlgorithms/CentroidPeaksMD.h
+	inc/MantidMDAlgorithms/CentroidPeaksMD2.h
+	inc/MantidMDAlgorithms/ChangeQConvention.h
+	inc/MantidMDAlgorithms/CloneMDWorkspace.h
+	inc/MantidMDAlgorithms/CompactMD.h
+	inc/MantidMDAlgorithms/CompareMDWorkspaces.h
+	inc/MantidMDAlgorithms/ConvToMDBase.h
+	inc/MantidMDAlgorithms/ConvertCWPDMDToSpectra.h
+	inc/MantidMDAlgorithms/ConvertCWSDExpToMomentum.h
+	inc/MantidMDAlgorithms/ConvertCWSDMDtoHKL.h
+	inc/MantidMDAlgorithms/ConvertMDHistoToMatrixWorkspace.h
+	inc/MantidMDAlgorithms/ConvertSpiceDataToRealSpace.h
+	inc/MantidMDAlgorithms/ConvertToDetectorFaceMD.h
+	inc/MantidMDAlgorithms/ConvertToDiffractionMDWorkspace.h
+	inc/MantidMDAlgorithms/ConvertToDiffractionMDWorkspace2.h
+	inc/MantidMDAlgorithms/ConvertToDiffractionMDWorkspace3.h
+	inc/MantidMDAlgorithms/ConvertToMD.h
+	inc/MantidMDAlgorithms/ConvertToMDMinMaxGlobal.h
+	inc/MantidMDAlgorithms/ConvertToMDMinMaxLocal.h
+	inc/MantidMDAlgorithms/ConvertToMDParent.h
+	inc/MantidMDAlgorithms/ConvertToReflectometryQ.h
+	inc/MantidMDAlgorithms/CreateMD.h
+	inc/MantidMDAlgorithms/CreateMDHistoWorkspace.h
+	inc/MantidMDAlgorithms/CreateMDWorkspace.h
+	inc/MantidMDAlgorithms/CutMD.h
+	inc/MantidMDAlgorithms/DisplayNormalizationSetter.h
+	inc/MantidMDAlgorithms/DivideMD.h
+	inc/MantidMDAlgorithms/DllConfig.h
+	inc/MantidMDAlgorithms/EqualToMD.h
+	inc/MantidMDAlgorithms/EvaluateMDFunction.h
+	inc/MantidMDAlgorithms/ExponentialMD.h
+	inc/MantidMDAlgorithms/FakeMDEventData.h
+	inc/MantidMDAlgorithms/FindPeaksMD.h
+	inc/MantidMDAlgorithms/FitMD.h
+	inc/MantidMDAlgorithms/GSLFunctions.h
+	inc/MantidMDAlgorithms/GetSpiceDataRawCountsFromMD.h
+	inc/MantidMDAlgorithms/GreaterThanMD.h
+	inc/MantidMDAlgorithms/IDynamicRebinning.h
+	inc/MantidMDAlgorithms/ImportMDEventWorkspace.h
+	inc/MantidMDAlgorithms/ImportMDHistoWorkspace.h
+	inc/MantidMDAlgorithms/ImportMDHistoWorkspaceBase.h
+	inc/MantidMDAlgorithms/Integrate3DEvents.h
+	inc/MantidMDAlgorithms/IntegrateEllipsoids.h
+	inc/MantidMDAlgorithms/IntegrateEllipsoidsTwoStep.h
+	inc/MantidMDAlgorithms/IntegrateFlux.h
+	inc/MantidMDAlgorithms/IntegrateMDHistoWorkspace.h
+	inc/MantidMDAlgorithms/IntegratePeaksCWSD.h
+	inc/MantidMDAlgorithms/IntegratePeaksMD.h
+	inc/MantidMDAlgorithms/IntegratePeaksMD2.h
+	inc/MantidMDAlgorithms/IntegratePeaksMDHKL.h
+	inc/MantidMDAlgorithms/InvalidParameter.h
+	inc/MantidMDAlgorithms/InvalidParameterParser.h
+	inc/MantidMDAlgorithms/LessThanMD.h
+	inc/MantidMDAlgorithms/LoadDNSSCD.h
+	inc/MantidMDAlgorithms/LoadMD.h
+	inc/MantidMDAlgorithms/LoadSQW.h
+	inc/MantidMDAlgorithms/LoadSQW2.h
+	inc/MantidMDAlgorithms/LogarithmMD.h
+	inc/MantidMDAlgorithms/MDEventWSWrapper.h
+	inc/MantidMDAlgorithms/MDNormDirectSC.h
+	inc/MantidMDAlgorithms/MDNormSCD.h
+	inc/MantidMDAlgorithms/MDTransfAxisNames.h
+	inc/MantidMDAlgorithms/MDTransfFactory.h
+	inc/MantidMDAlgorithms/MDTransfInterface.h
+	inc/MantidMDAlgorithms/MDTransfModQ.h
+	inc/MantidMDAlgorithms/MDTransfNoQ.h
+	inc/MantidMDAlgorithms/MDTransfQ3D.h
+	inc/MantidMDAlgorithms/MDWSDescription.h
+	inc/MantidMDAlgorithms/MDWSTransform.h
+	inc/MantidMDAlgorithms/MaskMD.h
+	inc/MantidMDAlgorithms/MergeMD.h
+	inc/MantidMDAlgorithms/MergeMDFiles.h
+	inc/MantidMDAlgorithms/MinusMD.h
+	inc/MantidMDAlgorithms/MultiplyMD.h
+	inc/MantidMDAlgorithms/NotMD.h
+	inc/MantidMDAlgorithms/OneStepMDEW.h
+	inc/MantidMDAlgorithms/OrMD.h
+	inc/MantidMDAlgorithms/PlusMD.h
+	inc/MantidMDAlgorithms/PowerMD.h
+	inc/MantidMDAlgorithms/PreprocessDetectorsToMD.h
+	inc/MantidMDAlgorithms/Quantification/CachedExperimentInfo.h
+	inc/MantidMDAlgorithms/Quantification/FitResolutionConvolvedModel.h
+	inc/MantidMDAlgorithms/Quantification/ForegroundModel.h
+	inc/MantidMDAlgorithms/Quantification/ForegroundModelFactory.h
+	inc/MantidMDAlgorithms/Quantification/MDResolutionConvolution.h
+	inc/MantidMDAlgorithms/Quantification/MDResolutionConvolutionFactory.h
+	inc/MantidMDAlgorithms/Quantification/Models/MullerAnsatz.h
+	inc/MantidMDAlgorithms/Quantification/Models/QCoordinate.h
+	inc/MantidMDAlgorithms/Quantification/Models/Strontium122.h
+	inc/MantidMDAlgorithms/Quantification/Resolution/ModeratorChopperResolution.h
+	inc/MantidMDAlgorithms/Quantification/Resolution/TobyFitBMatrix.h
+	inc/MantidMDAlgorithms/Quantification/Resolution/TobyFitResolutionModel.h
+	inc/MantidMDAlgorithms/Quantification/Resolution/TobyFitYVector.h
+	inc/MantidMDAlgorithms/Quantification/ResolutionConvolvedCrossSection.h
+	inc/MantidMDAlgorithms/Quantification/SimulateResolutionConvolvedModel.h
+	inc/MantidMDAlgorithms/QueryMDWorkspace.h
+	inc/MantidMDAlgorithms/ReflectometryTransformKiKf.h
+	inc/MantidMDAlgorithms/ReflectometryTransformP.h
+	inc/MantidMDAlgorithms/ReflectometryTransformQxQz.h
+	inc/MantidMDAlgorithms/ReplicateMD.h
+	inc/MantidMDAlgorithms/SaveIsawQvector.h
+	inc/MantidMDAlgorithms/SaveMD.h
+	inc/MantidMDAlgorithms/SaveMD2.h
+	inc/MantidMDAlgorithms/SaveZODS.h
+	inc/MantidMDAlgorithms/SetMDFrame.h
+	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/TransposeMD.h
+	inc/MantidMDAlgorithms/UnaryOperationMD.h
+	inc/MantidMDAlgorithms/UnitsConversionHelper.h
+	inc/MantidMDAlgorithms/Vector3DParameter.h
+	inc/MantidMDAlgorithms/Vector3DParameterParser.h
+	inc/MantidMDAlgorithms/WeightedMeanMD.h
+	inc/MantidMDAlgorithms/XorMD.h
+)
 
 # Test files. Other source files required.
 set ( TEST_FILES
-    #
-    # these tests are as they test verify different parts of the CPR algorithms
-    #CreateMDFitWorkspaceTest.h
-    AccumulateMDTest.h
-    AndMDTest.h
-    BooleanBinaryOperationMDTest.h
-    BoxControllerSettingsAlgorithmTest.h
-    CachedExperimentInfoTest.h
-    CalculateCoverageDGSTest.h
-    CentroidPeaksMD2Test.h
-    CentroidPeaksMDTest.h
-    ChangeQConventionTest.h
-    CloneMDWorkspaceTest.h
-    CompactMDTest.h
-    CompareMDWorkspacesTest.h
-    ConvertCWPDMDToSpectraTest.h
-    ConvertCWSDExpToMomentumTest.h
-    ConvertCWSDMDtoHKLTest.h
-    ConvertEventsToMDTest.h
-    ConvertMDHistoToMatrixWorkspaceTest.h
-    ConvertSpiceDataToRealSpaceTest.h
-    ConvertToDetectorFaceMDTest.h
-    ConvertToDiffractionMDWorkspaceTest.h
-    ConvertToDiffractionMDWorkspace2Test.h
-    ConvertToDiffractionMDWorkspace3Test.h
-    ConvertToMDComponentsTest.h
-    ConvertToMDMinMaxGlobalTest.h
-    ConvertToMDMinMaxLocalTest.h
-    ConvertToMDTest.h
-    ConvertToQ3DdETest.h
-    ConvertToReflectometryQTest.h
-    CreateMDHistoWorkspaceTest.h
-    CreateMDTest.h
-    CreateMDWorkspaceTest.h
-    CutMDTest.h
-    DisplayNormalizationSetterTest.h
-    DivideMDTest.h
-    EqualToMDTest.h
-    EvaluateMDFunctionTest.h
-    ExponentialMDTest.h
-    FakeMDEventDataTest.h
-    FindPeaksMDTest.h
-    FitMDTest.h
-    FitResolutionConvolvedModelTest.h
-    ForegroundModelTest.h
-    GetSpiceDataRawCountsFromMDTest.h
-    GreaterThanMDTest.h
-    ImportMDEventWorkspaceTest.h
-    ImportMDHistoWorkspaceTest.h
-    Integrate3DEventsTest.h
-    IntegrateEllipsoidsTest.h
-    IntegrateEllipsoidsTwoStepTest.h
-    IntegrateFluxTest.h
-    IntegrateMDHistoWorkspaceTest.h
-    IntegratePeaksMD2Test.h
-    IntegratePeaksMDHKLTest.h
-    IntegratePeaksMDTest.h
-    IntegratePeaksCWSDTest.h
-    InvalidParameterParserTest.h
-    InvalidParameterTest.h
-    LessThanMDTest.h
-    LoadMDTest.h
-    LoadSQWTest.h
-    LoadSQW2Test.h
-    LogarithmMDTest.h
-    MDEventWSWrapperTest.h
-    MDNormDirectSCTest.h
-    MDNormSCDTest.h
-    MDResolutionConvolutionFactoryTest.h
-    MDTransfAxisNamesTest.h
-    MDTransfFactoryTest.h
-    MDTransfModQTest.h
-    MDTransfQ3DTest.h
-    MDWSDescriptionTest.h
-    MDWSTransfTest.h
-    MaskMDTest.h
-    MergeMDFilesTest.h
-    MergeMDTest.h
-    MinusMDTest.h
-    ModeratorChopperResolutionTest.h
-    MullerAnsatzTest.h
-    MultiplyMDTest.h
-    NotMDTest.h
-    OneStepMDEWTest.h
-    OrMDTest.h
-    PlusMDTest.h
-    PowerMDTest.h
-    PreprocessDetectorsToMDTest.h
-    QueryMDWorkspaceTest.h
-    ReflectometryTransformKiKfTest.h
-    ReflectometryTransformPTest.h
-    ReflectometryTransformQxQzTest.h
-    ReplicateMDTest.h
-    ResolutionConvolvedCrossSectionTest.h
-    SaveIsawQvectorTest.h
-    SaveMD2Test.h
-    SaveMDTest.h
-    SaveZODSTest.h
-    SetMDFrameTest.h
-    SetMDUsingMaskTest.h
-    SimulateResolutionConvolvedModelTest.h
-    SliceMDTest.h
-    SlicingAlgorithmTest.h
-    SmoothMDTest.h
-    Strontium122Test.h
-    ThresholdMDTest.h
-    TobyFitBMatrixTest.h
-    TobyFitResolutionModelTest.h
-    TobyFitYVectorTest.h
-    TransformMDTest.h
-    TransposeMDTest.h
-    UnaryOperationMDTest.h
-    UnitsConversionHelperTest.h
-    WeightedMeanMDTest.h
-    XorMDTest.h
-    )
+	#
+	# these tests are as they test verify different parts of the CPR algorithms
+	#CreateMDFitWorkspaceTest.h
+	AccumulateMDTest.h
+	AndMDTest.h
+	BooleanBinaryOperationMDTest.h
+	BoxControllerSettingsAlgorithmTest.h
+	CachedExperimentInfoTest.h
+	CalculateCoverageDGSTest.h
+	CentroidPeaksMD2Test.h
+	CentroidPeaksMDTest.h
+	ChangeQConventionTest.h
+	CloneMDWorkspaceTest.h
+	CompactMDTest.h
+	CompareMDWorkspacesTest.h
+	ConvertCWPDMDToSpectraTest.h
+	ConvertCWSDExpToMomentumTest.h
+	ConvertCWSDMDtoHKLTest.h
+	ConvertEventsToMDTest.h
+	ConvertMDHistoToMatrixWorkspaceTest.h
+	ConvertSpiceDataToRealSpaceTest.h
+	ConvertToDetectorFaceMDTest.h
+	ConvertToDiffractionMDWorkspace2Test.h
+	ConvertToDiffractionMDWorkspace3Test.h
+	ConvertToDiffractionMDWorkspaceTest.h
+	ConvertToMDComponentsTest.h
+	ConvertToMDMinMaxGlobalTest.h
+	ConvertToMDMinMaxLocalTest.h
+	ConvertToMDTest.h
+	ConvertToQ3DdETest.h
+	ConvertToReflectometryQTest.h
+	CreateMDHistoWorkspaceTest.h
+	CreateMDTest.h
+	CreateMDWorkspaceTest.h
+	CutMDTest.h
+	DisplayNormalizationSetterTest.h
+	DivideMDTest.h
+	EqualToMDTest.h
+	EvaluateMDFunctionTest.h
+	ExponentialMDTest.h
+	FakeMDEventDataTest.h
+	FindPeaksMDTest.h
+	FitMDTest.h
+	FitResolutionConvolvedModelTest.h
+	ForegroundModelTest.h
+	GetSpiceDataRawCountsFromMDTest.h
+	GreaterThanMDTest.h
+	ImportMDEventWorkspaceTest.h
+	ImportMDHistoWorkspaceTest.h
+	Integrate3DEventsTest.h
+	IntegrateEllipsoidsTest.h
+	IntegrateEllipsoidsTwoStepTest.h
+	IntegrateFluxTest.h
+	IntegrateMDHistoWorkspaceTest.h
+	IntegratePeaksCWSDTest.h
+	IntegratePeaksMD2Test.h
+	IntegratePeaksMDHKLTest.h
+	IntegratePeaksMDTest.h
+	InvalidParameterParserTest.h
+	InvalidParameterTest.h
+	LessThanMDTest.h
+	LoadDNSSCDTest.h
+	LoadMDTest.h
+	LoadSQW2Test.h
+	LoadSQWTest.h
+	LogarithmMDTest.h
+	MDEventWSWrapperTest.h
+	MDNormDirectSCTest.h
+	MDNormSCDTest.h
+	MDResolutionConvolutionFactoryTest.h
+	MDTransfAxisNamesTest.h
+	MDTransfFactoryTest.h
+	MDTransfModQTest.h
+	MDTransfQ3DTest.h
+	MDWSDescriptionTest.h
+	MDWSTransfTest.h
+	MaskMDTest.h
+	MergeMDFilesTest.h
+	MergeMDTest.h
+	MinusMDTest.h
+	ModeratorChopperResolutionTest.h
+	MullerAnsatzTest.h
+	MultiplyMDTest.h
+	NotMDTest.h
+	OneStepMDEWTest.h
+	OrMDTest.h
+	PlusMDTest.h
+	PowerMDTest.h
+	PreprocessDetectorsToMDTest.h
+	QueryMDWorkspaceTest.h
+	ReflectometryTransformKiKfTest.h
+	ReflectometryTransformPTest.h
+	ReflectometryTransformQxQzTest.h
+	ReplicateMDTest.h
+	ResolutionConvolvedCrossSectionTest.h
+	SaveIsawQvectorTest.h
+	SaveMD2Test.h
+	SaveMDTest.h
+	SaveZODSTest.h
+	SetMDFrameTest.h
+	SetMDUsingMaskTest.h
+	SimulateResolutionConvolvedModelTest.h
+	SliceMDTest.h
+	SlicingAlgorithmTest.h
+	SmoothMDTest.h
+	Strontium122Test.h
+	ThresholdMDTest.h
+	TobyFitBMatrixTest.h
+	TobyFitResolutionModelTest.h
+	TobyFitYVectorTest.h
+	TransformMDTest.h
+	TransposeMDTest.h
+	UnaryOperationMDTest.h
+	UnitsConversionHelperTest.h
+	WeightedMeanMDTest.h
+	XorMDTest.h
+)
 
 set ( GMOCK_TEST_FILES
     BinaryOperationMDTest.h
diff --git a/Framework/MDAlgorithms/inc/MantidMDAlgorithms/LoadDNSSCD.h b/Framework/MDAlgorithms/inc/MantidMDAlgorithms/LoadDNSSCD.h
new file mode 100644
index 0000000000000000000000000000000000000000..cf0b4e0e995c8a7fc571c37a44fcd8dd26886ffb
--- /dev/null
+++ b/Framework/MDAlgorithms/inc/MantidMDAlgorithms/LoadDNSSCD.h
@@ -0,0 +1,108 @@
+#ifndef MANTID_MDALGORITHMS_LOADDNSSCD_H_
+#define MANTID_MDALGORITHMS_LOADDNSSCD_H_
+
+#include <vector>
+#include "MantidAPI/DataProcessorAlgorithm.h"
+#include "MantidAPI/IFileLoader.h"
+#include "MantidAPI/IMDEventWorkspace_fwd.h"
+#include "MantidKernel/System.h"
+#include "MantidDataObjects/MDEventWorkspace.h"
+#include "MantidKernel/Matrix.h"
+#include "MantidKernel/V3D.h"
+
+namespace Mantid {
+namespace MDAlgorithms {
+
+/** LoadDNSSCD : Load a list of DNS .d_dat files into a MDEventWorkspace
+
+  @author Marina Ganeva
+  @date 2018-02-15
+
+  Copyright &copy; 2018 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 LoadDNSSCD : public API::IFileLoader<Kernel::FileDescriptor> {
+public:
+  LoadDNSSCD();
+
+  /// Algorithm's name for identification
+  const std::string name() const override { return "LoadDNSSCD"; }
+
+  /// Summary of algorithms purpose
+  const std::string summary() const override {
+    return "Load a list of DNS .d_dat files into a MDEventWorkspace.";
+  }
+
+  /// Algorithm's version for identification
+  int version() const override { return 1; }
+
+  /// Algorithm's category for identification
+  const std::string category() const override {
+    return "MDAlgorithms\\DataHandling";
+  }
+
+  /// Returns a confidence value that this algorithm can load a file
+  int confidence(Kernel::FileDescriptor &descriptor) const override;
+
+private:
+  /// Initialise the properties
+  void init() override;
+  /// Run the algorithm
+  void exec() override;
+
+  /// number of workspace dimensions
+  size_t m_nDims;
+
+  /// type of normalization;
+  std::string m_normtype;
+  /// factor to multiply the error^2 for normalization
+  double m_normfactor;
+
+  /// structure for experimental data
+  struct ExpData {
+    double deterota;
+    double huber;
+    double wavelength;
+    double norm;
+    std::vector<double> signal;
+    std::vector<int> detID;
+  };
+
+  std::vector<ExpData> m_data;
+
+  /// Output IMDEventWorkspace
+  Mantid::API::IMDEventWorkspace_sptr m_OutWS;
+
+  void read_data(const std::string fname,
+                 std::map<std::string, std::string> &str_metadata,
+                 std::map<std::string, double> &num_metadata);
+  void fillOutputWorkspace(double wavelength);
+  API::ITableWorkspace_sptr saveHuber();
+  void loadHuber(API::ITableWorkspace_sptr tws);
+  template <class T>
+  void updateProperties(API::Run &run, std::map<std::string, T> &metadata,
+                        std::string time);
+};
+
+} // namespace MDAlgorithms
+} // namespace Mantid
+
+#endif /* MANTID_MDALGORITHMS_LOADDNSSCD_H_ */
diff --git a/Framework/MDAlgorithms/src/LoadDNSSCD.cpp b/Framework/MDAlgorithms/src/LoadDNSSCD.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..43f4dc3c7e0b290264b526f552fcd62938e00d6b
--- /dev/null
+++ b/Framework/MDAlgorithms/src/LoadDNSSCD.cpp
@@ -0,0 +1,606 @@
+#include <map>
+#include <iterator>
+#include <iomanip>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/regex.hpp>
+#include <boost/exception/diagnostic_information.hpp>
+#include <boost/exception_ptr.hpp>
+#include <Poco/DateTime.h>
+#include <Poco/DateTimeFormat.h>
+#include <Poco/DateTimeFormatter.h>
+#include <Poco/DirectoryIterator.h>
+#include <Poco/DateTimeParser.h>
+#include <Poco/Path.h>
+#include <Poco/File.h>
+#include "MantidMDAlgorithms/LoadDNSSCD.h"
+#include "MantidAPI/RegisterFileLoader.h"
+#include "MantidAPI/FileProperty.h"
+#include "MantidAPI/MultipleFileProperty.h"
+#include "MantidKernel/ConfigService.h"
+#include "MantidKernel/ArrayProperty.h"
+#include "MantidKernel/ArrayLengthValidator.h"
+#include "MantidKernel/BoundedValidator.h"
+#include "MantidKernel/ListValidator.h"
+#include "MantidGeometry/Crystal/OrientedLattice.h"
+#include "MantidKernel/VectorHelper.h"
+#include "MantidDataObjects/MDEventFactory.h"
+#include "MantidAPI/ExperimentInfo.h"
+#include "MantidAPI/Run.h"
+#include "MantidKernel/TimeSeriesProperty.h"
+#include "MantidGeometry/Crystal/IndexingUtils.h"
+#include "MantidGeometry/Crystal/OrientedLattice.h"
+#include "MantidGeometry/MDGeometry/HKL.h"
+#include "MantidKernel/UnitLabelTypes.h"
+#include "MantidAPI/ITableWorkspace.h"
+#include "MantidAPI/WorkspaceFactory.h"
+
+#include "MantidMDAlgorithms/MDWSDescription.h"
+#include "MantidMDAlgorithms/MDWSTransform.h"
+#include "MantidDataObjects/MDBoxBase.h"
+#include "MantidDataObjects/MDEventInserter.h"
+
+//========================
+// helper functions
+namespace {
+void eraseSubStr(std::string &str, const std::string &toErase) {
+  // Search for the substring in string
+  size_t pos = str.find(toErase);
+  if (pos != std::string::npos) {
+    // If found then erase it from string
+    str.erase(pos, toErase.length());
+  }
+}
+
+std::string parseTime(std::string &str) {
+  // remove unnecessary symbols
+  eraseSubStr(str, "#");
+  eraseSubStr(str, "start");
+  eraseSubStr(str, "stopped");
+  eraseSubStr(str, "at");
+  auto it = std::find_if(str.begin(), str.end(), [](char ch) {
+    return !std::isspace<char>(ch, std::locale::classic());
+  });
+  str.erase(str.begin(), it);
+  using namespace boost::posix_time;
+  // try to parse as a posix time
+  try {
+    auto time = time_from_string(str);
+    return to_iso_extended_string(time);
+  } catch (std::exception &) {
+    int tzd;
+    Poco::DateTime dt;
+    bool ok = Poco::DateTimeParser::tryParse(str, dt, tzd);
+    if (ok) {
+      auto time = Poco::DateTimeFormatter::format(dt, "%Y-%m-%dT%H:%M:%S");
+      return time;
+    }
+    std::string result("");
+    return result;
+  }
+}
+
+} // anonymous namespace
+//============================
+
+using namespace Mantid::Kernel;
+using namespace Mantid::API;
+using namespace Mantid::DataObjects;
+using namespace Mantid::Geometry;
+
+namespace Mantid {
+namespace MDAlgorithms {
+
+DECLARE_FILELOADER_ALGORITHM(LoadDNSSCD)
+
+//----------------------------------------------------------------------------------------------
+/** Constructor
+*/
+LoadDNSSCD::LoadDNSSCD() : m_nDims(3) {}
+
+/**
+* Return the confidence with with this algorithm can load the file
+* @param descriptor A descriptor for the file
+* @returns An integer specifying the confidence level. 0 indicates it will not
+* be used
+*/
+int LoadDNSSCD::confidence(Kernel::FileDescriptor &descriptor) const {
+  // DNS data acquisition writes ascii files with .d_dat extension
+  int confidence(0);
+  if ((descriptor.extension() == ".d_dat") && descriptor.isAscii()) {
+    confidence = 80;
+  }
+  return confidence;
+}
+
+//----------------------------------------------------------------------------------------------
+/** Initialize the algorithm's properties.
+ */
+void LoadDNSSCD::init() {
+  std::vector<std::string> exts(1, ".d_dat");
+  declareProperty(Kernel::make_unique<MultipleFileProperty>("Filenames", exts),
+                  "Select one or more DNS SCD .d_dat files to load."
+                  "Files must be measured at the same conditions.");
+
+  declareProperty(make_unique<WorkspaceProperty<IMDEventWorkspace>>(
+                      "OutputWorkspace", "", Direction::Output),
+                  "An output MDEventWorkspace.");
+
+  declareProperty(make_unique<WorkspaceProperty<IMDEventWorkspace>>(
+                      "NormalizationWorkspace", "", Direction::Output),
+                  "An output normalization MDEventWorkspace.");
+
+  const std::vector<std::string> normOptions = {"monitor", "time"};
+  declareProperty("Normalization", "monitor",
+                  boost::make_shared<StringListValidator>(normOptions),
+                  "Algorithm will create a separate normalization workspace. "
+                  "Choose whether it should contain monitor counts or time.");
+
+  auto mustBePositive = boost::make_shared<BoundedValidator<double>>();
+  mustBePositive->setLower(0.0);
+  auto reasonableAngle = boost::make_shared<BoundedValidator<double>>();
+  reasonableAngle->setLower(5.0);
+  reasonableAngle->setUpper(175.0);
+  // clang-format off
+  auto mustBe3D = boost::make_shared<ArrayLengthValidator<double> >(3);
+  auto mustBe2D = boost::make_shared<ArrayLengthValidator<double> >(2);
+  // clang-format on
+  std::vector<double> u0(3, 0), v0(3, 0);
+  u0[0] = 1.;
+  u0[1] = 1.;
+  v0[2] = 1.;
+
+  declareProperty(make_unique<PropertyWithValue<double>>(
+                      "a", 1.0, mustBePositive, Direction::Input),
+                  "Lattice parameter a in Angstrom");
+  declareProperty(make_unique<PropertyWithValue<double>>(
+                      "b", 1.0, mustBePositive, Direction::Input),
+                  "Lattice parameter b in Angstrom");
+  declareProperty(make_unique<PropertyWithValue<double>>(
+                      "c", 1.0, mustBePositive, Direction::Input),
+                  "Lattice parameter c in Angstrom");
+  declareProperty(make_unique<PropertyWithValue<double>>(
+                      "alpha", 90.0, reasonableAngle, Direction::Input),
+                  "Angle between b and c in degrees");
+  declareProperty(make_unique<PropertyWithValue<double>>(
+                      "beta", 90.0, reasonableAngle, Direction::Input),
+                  "Angle between a and c in degrees");
+  declareProperty(make_unique<PropertyWithValue<double>>(
+                      "gamma", 90.0, reasonableAngle, Direction::Input),
+                  "Angle between a and b in degrees");
+
+  declareProperty(make_unique<PropertyWithValue<double>>(
+                      "OmegaOffset", 0.0,
+                      boost::make_shared<BoundedValidator<double>>(),
+                      Direction::Input),
+                  "Angle in degrees between (HKL1) and the beam axis"
+                  "if the goniometer is at zero.");
+  declareProperty(
+      Kernel::make_unique<ArrayProperty<double>>("HKL1", u0, mustBe3D),
+      "Indices of the vector in reciprocal space in the horizontal plane at "
+      "angle Omegaoffset, "
+      "if the goniometer is at zero.");
+
+  declareProperty(
+      Kernel::make_unique<ArrayProperty<double>>("HKL2", v0, mustBe3D),
+      "Indices of a second vector in reciprocal space in the horizontal plane "
+      "not parallel to HKL1");
+
+  std::vector<double> ttl(2, 0);
+  ttl[1] = 180.0;
+  declareProperty(
+      Kernel::make_unique<ArrayProperty<double>>("TwoThetaLimits", ttl,
+                                                 mustBe2D),
+      "Range (min, max) of scattering angles (2theta, in degrees) to consider. "
+      "Everything out of this range will be cut.");
+
+  declareProperty(
+      Kernel::make_unique<WorkspaceProperty<API::ITableWorkspace>>(
+          "LoadHuberFrom", "", Direction::Input, PropertyMode::Optional),
+      "A table workspace to load a list of raw sample rotation angles. "
+      "Huber angles given in the data files will be ignored.");
+
+  declareProperty(
+      Kernel::make_unique<WorkspaceProperty<API::ITableWorkspace>>(
+          "SaveHuberTo", "", Direction::Output, PropertyMode::Optional),
+      "A workspace name to save a list of raw sample rotation angles.");
+}
+
+//----------------------------------------------------------------------------------------------
+/** Read Huber angles from a given table workspace.
+ */
+
+void LoadDNSSCD::loadHuber(ITableWorkspace_sptr tws) {
+  ColumnVector<double> huber = tws->getVector("Huber(degrees)");
+  // set huber[0] for each run in m_data
+  for (auto &ds : m_data) {
+    ds.huber = huber[0];
+  }
+  // dublicate runs for each huber in the table
+  std::vector<ExpData> old(m_data);
+  for (size_t i = 1; i < huber.size(); ++i) {
+    for (auto &ds : old) {
+      ds.huber = huber[i];
+      m_data.push_back(ds);
+    }
+  }
+}
+
+//----------------------------------------------------------------------------------------------
+/** Save Huber angles to a given table workspace.
+ */
+Mantid::API::ITableWorkspace_sptr LoadDNSSCD::saveHuber() {
+  std::vector<double> huber;
+  for (auto ds : m_data)
+    huber.push_back(ds.huber);
+  // remove dublicates
+  std::sort(huber.begin(), huber.end());
+  huber.erase(unique(huber.begin(), huber.end()), huber.end());
+
+  Mantid::API::ITableWorkspace_sptr huberWS =
+      WorkspaceFactory::Instance().createTable("TableWorkspace");
+  huberWS->addColumn("double", "Huber(degrees)");
+  for (size_t i = 0; i < huber.size(); i++) {
+    huberWS->appendRow();
+    huberWS->cell<double>(i, 0) = huber[i];
+  }
+  return huberWS;
+}
+//----------------------------------------------------------------------------------------------
+/** Execute the algorithm.
+ */
+void LoadDNSSCD::exec() {
+  MultipleFileProperty *multiFileProp =
+      dynamic_cast<MultipleFileProperty *>(getPointerToProperty("Filenames"));
+  if (!multiFileProp) {
+    throw std::logic_error(
+        "Filenames property must have MultipleFileProperty type.");
+  }
+  std::vector<std::string> filenames =
+      VectorHelper::flattenVector(multiFileProp->operator()());
+  if (filenames.empty())
+    throw std::invalid_argument("Must specify at least one filename.");
+
+  // set type of normalization
+  std::string normtype = getProperty("Normalization");
+  if (normtype == "monitor") {
+    m_normtype = "Monitor";
+    m_normfactor = 1.0;
+  } else {
+    m_normtype = "Timer";
+    m_normfactor = 0.0; // error for time should be 0
+  }
+
+  g_log.notice() << "The normalization workspace will contain " << m_normtype
+                 << ".\n";
+
+  ExperimentInfo_sptr expinfo = boost::make_shared<ExperimentInfo>();
+  API::Run &run = expinfo->mutableRun();
+  for (auto fname : filenames) {
+    std::map<std::string, std::string> str_metadata;
+    std::map<std::string, double> num_metadata;
+    try {
+      read_data(fname, str_metadata, num_metadata);
+      // if no stop_time, take file_save_time
+      std::string time(str_metadata["stop_time"]);
+      if (time.empty()) {
+        g_log.warning()
+            << "stop_time is empty! File save time will be used instead."
+            << std::endl;
+        time = str_metadata["file_save_time"];
+      }
+      updateProperties<std::string>(run, str_metadata, time);
+      updateProperties<double>(run, num_metadata, time);
+    } catch (...) {
+      g_log.warning() << "Failed to read file " << fname;
+      g_log.warning() << ". This file will be ignored. " << std::endl;
+      g_log.debug() << boost::current_exception_diagnostic_information()
+                    << std::endl;
+    }
+  }
+
+  if (m_data.empty())
+    throw std::runtime_error(
+        "No valid DNS files have been provided. Nothing to load.");
+
+  m_OutWS = MDEventFactory::CreateMDWorkspace(m_nDims, "MDEvent");
+
+  m_OutWS->addExperimentInfo(expinfo);
+
+  // load huber angles from a table workspace if given
+  ITableWorkspace_sptr huberWS = getProperty("LoadHuberFrom");
+  if (huberWS) {
+    g_log.notice() << "Huber angles will be loaded from " << huberWS->getName()
+                   << std::endl;
+    loadHuber(huberWS);
+  }
+
+  // get wavelength
+  TimeSeriesProperty<double> *wlprop =
+      dynamic_cast<TimeSeriesProperty<double> *>(
+          expinfo->run().getProperty("Lambda"));
+  // assume, that lambda is in nm
+  double wavelength =
+      wlprop->minValue() * 10.0; // needed to estimate extents => minValue
+  run.addProperty("wavelength", wavelength);
+  run.getProperty("wavelength")->setUnits("Angstrom");
+
+  fillOutputWorkspace(wavelength);
+
+  std::string saveHuberTableWS = getProperty("SaveHuberTo");
+  if (!saveHuberTableWS.empty()) {
+    Mantid::API::ITableWorkspace_sptr huber_table = saveHuber();
+    setProperty("SaveHuberTo", huber_table);
+  }
+  setProperty("OutputWorkspace", m_OutWS);
+}
+
+//----------------------------------------------------------------------------------------------
+
+template <class T>
+void LoadDNSSCD::updateProperties(API::Run &run,
+                                  std::map<std::string, T> &metadata,
+                                  std::string time) {
+  typename std::map<std::string, T>::iterator it = metadata.begin();
+  while (it != metadata.end()) {
+    TimeSeriesProperty<T> *timeSeries(nullptr);
+    std::string name(it->first);
+    std::string units;
+    // std::regex does not work for rhel7, thus boost
+    boost::regex reg("([-_a-zA-Z]+)\\[(.*)]");
+    boost::smatch match;
+    if (boost::regex_search(name, match, reg) && match.size() > 2) {
+      std::string new_name(match.str(1));
+      units.assign(match.str(2));
+      name = new_name;
+    }
+    if (run.hasProperty(name)) {
+      timeSeries = dynamic_cast<TimeSeriesProperty<T> *>(run.getLogData(name));
+      if (!timeSeries)
+        throw std::invalid_argument(
+            "Log '" + name +
+            "' already exists but the values are a different type.");
+    } else {
+      timeSeries = new TimeSeriesProperty<T>(name);
+      if (!units.empty())
+        timeSeries->setUnits(units);
+      run.addProperty(timeSeries);
+    }
+    timeSeries->addValue(time, it->second);
+    ++it;
+  }
+}
+//----------------------------------------------------------------------------------------------
+/// Create output workspace
+void LoadDNSSCD::fillOutputWorkspace(double wavelength) {
+
+  // dimensions
+  std::vector<std::string> vec_ID(3);
+  vec_ID[0] = "H";
+  vec_ID[1] = "K";
+  vec_ID[2] = "L";
+
+  std::vector<std::string> dimensionNames(3);
+  dimensionNames[0] = "H";
+  dimensionNames[1] = "K";
+  dimensionNames[2] = "L";
+
+  Mantid::Kernel::SpecialCoordinateSystem coordinateSystem =
+      Mantid::Kernel::HKL;
+
+  double a, b, c, alpha, beta, gamma;
+  a = getProperty("a");
+  b = getProperty("b");
+  c = getProperty("c");
+  alpha = getProperty("alpha");
+  beta = getProperty("beta");
+  gamma = getProperty("gamma");
+  std::vector<double> u = getProperty("HKL1");
+  std::vector<double> v = getProperty("HKL2");
+
+  // estimate extents
+  double qmax = 4.0 * M_PI / wavelength;
+  std::vector<double> extentMins = {-qmax * a, -qmax * b, -qmax * c};
+  std::vector<double> extentMaxs = {qmax * a, qmax * b, qmax * c};
+
+  // Get MDFrame of HKL type with RLU
+  auto unitFactory = makeMDUnitFactoryChain();
+  auto unit = unitFactory->create(Units::Symbol::RLU.ascii());
+  Mantid::Geometry::HKL frame(unit);
+
+  // add dimensions
+  for (size_t i = 0; i < m_nDims; ++i) {
+    std::string id = vec_ID[i];
+    std::string name = dimensionNames[i];
+    m_OutWS->addDimension(
+        Geometry::MDHistoDimension_sptr(new Geometry::MDHistoDimension(
+            id, name, frame, static_cast<coord_t>(extentMins[i]),
+            static_cast<coord_t>(extentMaxs[i]), 5)));
+  }
+
+  // Set coordinate system
+  m_OutWS->setCoordinateSystem(coordinateSystem);
+
+  // calculate RUB matrix
+  Mantid::Geometry::OrientedLattice o;
+  o = Mantid::Geometry::OrientedLattice(a, b, c, alpha, beta, gamma);
+  o.setUFromVectors(Mantid::Kernel::V3D(u[0], u[1], u[2]),
+                    Mantid::Kernel::V3D(v[0], v[1], v[2]));
+
+  double omega_offset = getProperty("OmegaOffset");
+  omega_offset *= -1.0 * deg2rad;
+  DblMatrix rotm(3, 3);
+  rotm[0][0] = std::cos(omega_offset);
+  rotm[0][1] = 0.0;
+  rotm[0][2] = std::sin(omega_offset);
+  rotm[1][0] = 0.0;
+  rotm[1][1] = 1.0;
+  rotm[1][2] = 0.0;
+  rotm[2][0] = -std::sin(omega_offset);
+  rotm[2][1] = 0.0;
+  rotm[2][2] = std::cos(omega_offset);
+
+  DblMatrix ub(o.getUB());
+  ub = rotm * ub;
+  o.setUB(ub);
+  DblMatrix ub_inv(ub);
+  // invert the UB matrix
+  ub_inv.Invert();
+
+  // Creates a new instance of the MDEventInserter to output workspace
+  MDEventWorkspace<MDEvent<3>, 3>::sptr mdws_mdevt_3 =
+      boost::dynamic_pointer_cast<MDEventWorkspace<MDEvent<3>, 3>>(m_OutWS);
+  MDEventInserter<MDEventWorkspace<MDEvent<3>, 3>::sptr> inserter(mdws_mdevt_3);
+
+  // create a normalization workspace
+  IMDEventWorkspace_sptr normWS = m_OutWS->clone();
+
+  // Creates a new instance of the MDEventInserter to norm workspace
+  MDEventWorkspace<MDEvent<3>, 3>::sptr normws_mdevt_3 =
+      boost::dynamic_pointer_cast<MDEventWorkspace<MDEvent<3>, 3>>(normWS);
+  MDEventInserter<MDEventWorkspace<MDEvent<3>, 3>::sptr> norm_inserter(
+      normws_mdevt_3);
+
+  // scattering angle limits
+  std::vector<double> tth_limits = getProperty("TwoThetaLimits");
+  double theta_min = tth_limits[0] * deg2rad / 2.0;
+  double theta_max = tth_limits[1] * deg2rad / 2.0;
+
+  // Go though each element of m_data to convert to MDEvent
+  for (ExpData ds : m_data) {
+    uint16_t runindex = 0;
+    signal_t norm_signal(ds.norm);
+    signal_t norm_error = std::sqrt(m_normfactor * norm_signal);
+    double k = 2.0 / ds.wavelength;
+    for (size_t i = 0; i < ds.detID.size(); i++) {
+      signal_t signal(ds.signal[i]);
+      signal_t error = std::sqrt(signal);
+      detid_t detid(ds.detID[i]);
+      double theta = 0.5 * (ds.detID[i] * 5.0 - ds.deterota) * deg2rad;
+      if ((theta > theta_min) && (theta < theta_max)) {
+        double omega = (ds.huber - ds.deterota) * deg2rad - theta;
+        V3D uphi(-cos(omega), 0, -sin(omega));
+        V3D hphi = uphi * k * sin(theta);
+        V3D hkl = ub_inv * hphi;
+        std::vector<Mantid::coord_t> millerindex(3);
+        millerindex[0] = static_cast<float>(hkl.X());
+        millerindex[1] = static_cast<float>(hkl.Y());
+        millerindex[2] = static_cast<float>(hkl.Z());
+        inserter.insertMDEvent(
+            static_cast<float>(signal), static_cast<float>(error * error),
+            static_cast<uint16_t>(runindex), detid, millerindex.data());
+
+        norm_inserter.insertMDEvent(static_cast<float>(norm_signal),
+                                    static_cast<float>(norm_error * norm_error),
+                                    static_cast<uint16_t>(runindex), detid,
+                                    millerindex.data());
+      }
+    }
+  }
+  setProperty("NormalizationWorkspace", normWS);
+}
+
+void LoadDNSSCD::read_data(const std::string fname,
+                           std::map<std::string, std::string> &str_metadata,
+                           std::map<std::string, double> &num_metadata) {
+  std::ifstream file(fname);
+  std::string line;
+  std::string::size_type n;
+  std::string s;
+  boost::regex reg1("^#\\s+(\\w+):(.*)");
+  boost::regex reg2("^#\\s+((\\w+\\s)+)\\s+(-?\\d+(,\\d+)*(\\.\\d+(e\\d+)?)?)");
+  boost::smatch match;
+  getline(file, line);
+  n = line.find("DNS");
+  if (n == std::string::npos) {
+    throw std::invalid_argument("Not a DNS file");
+  }
+  // get file save time
+  Poco::File pfile(fname);
+  Poco::DateTime lastModified = pfile.getLastModified();
+  std::string wtime(
+      Poco::DateTimeFormatter::format(lastModified, "%Y-%m-%dT%H:%M:%S"));
+  str_metadata.insert(std::make_pair("file_save_time", wtime));
+
+  // get file basename
+  Poco::Path p(fname);
+  str_metadata.insert(std::make_pair("run_number", p.getBaseName()));
+
+  // parse metadata
+  while (getline(file, line)) {
+    n = line.find("Lambda");
+    if (n != std::string::npos) {
+      boost::regex re("[\\s]+");
+      s = line.substr(5);
+      boost::sregex_token_iterator it(s.begin(), s.end(), re, -1);
+      boost::sregex_token_iterator reg_end;
+      getline(file, line);
+      std::string s2 = line.substr(2);
+      boost::sregex_token_iterator it2(s2.begin(), s2.end(), re, -1);
+      for (; (it != reg_end) && (it2 != reg_end); ++it) {
+        std::string token(it->str());
+        if (token.find_first_not_of(' ') == std::string::npos) {
+          ++it2;
+          continue;
+        }
+        if (token == "Mono") {
+          str_metadata.insert(std::make_pair(token, it2->str()));
+        } else {
+          num_metadata.insert(std::make_pair(token, std::stod(it2->str())));
+        }
+        ++it2;
+      }
+    }
+    // parse start and stop time
+    n = line.find("start");
+    if (n != std::string::npos) {
+      str_metadata.insert(std::make_pair("start_time", parseTime(line)));
+      getline(file, line);
+      str_metadata.insert(std::make_pair("stop_time", parseTime(line)));
+      getline(file, line);
+    }
+    if (boost::regex_search(line, match, reg1) && match.size() > 2) {
+      str_metadata.insert(std::make_pair(match.str(1), match.str(2)));
+    }
+    if (boost::regex_search(line, match, reg2) && match.size() > 2) {
+      s = match.str(1);
+      s.erase(std::find_if_not(s.rbegin(), s.rend(), ::isspace).base(),
+              s.end());
+      num_metadata.insert(std::make_pair(s, std::stod(match.str(3))));
+    }
+    n = line.find("DATA");
+    if (n != std::string::npos) {
+      break;
+    }
+  }
+
+  // the algorithm does not work with TOF data for the moment
+  std::map<std::string, double>::const_iterator m =
+      num_metadata.lower_bound("TOF");
+  g_log.debug() << "TOF Channels number: " << m->second << std::endl;
+  if (m->second != 1)
+    throw std::runtime_error(
+        "Algorithm does not support TOF data. TOF Channels number must be 1.");
+
+  ExpData ds;
+  ds.deterota = num_metadata["DeteRota"];
+  ds.huber = num_metadata["Huber"];
+  ds.wavelength = 10.0 * num_metadata["Lambda[nm]"];
+  ds.norm = num_metadata[m_normtype];
+
+  // read data array
+  getline(file, line);
+  int d;
+  double x;
+  while (file) {
+    file >> d >> x;
+    ds.detID.push_back(d);
+    ds.signal.push_back(x);
+  }
+  // DNS PA detector bank has only 24 detectors
+  ds.detID.resize(24);
+  ds.signal.resize(24);
+  m_data.push_back(ds);
+}
+
+} // namespace MDAlgorithms
+} // namespace Mantid
diff --git a/Framework/MDAlgorithms/test/LoadDNSSCDTest.h b/Framework/MDAlgorithms/test/LoadDNSSCDTest.h
new file mode 100644
index 0000000000000000000000000000000000000000..1e2d0b2c31fe30ae03b9eeddb6245804ea92e290
--- /dev/null
+++ b/Framework/MDAlgorithms/test/LoadDNSSCDTest.h
@@ -0,0 +1,707 @@
+#ifndef MANTID_MDALGORITHMS_LOADDNSSCDEWTEST_H_
+#define MANTID_MDALGORITHMS_LOADDNSSCDEWTEST_H_
+
+#include "MantidKernel/Strings.h"
+#include "MantidAPI/AnalysisDataService.h"
+#include "MantidAPI/IMDIterator.h"
+#include "MantidAPI/IMDEventWorkspace.h"
+#include "MantidDataObjects/MDBox.h"
+#include "MantidDataObjects/MDGridBox.h"
+#include "MantidDataObjects/MDEventFactory.h"
+#include "MantidDataObjects/MDEventWorkspace.h"
+#include "MantidAPI/BoxController.h"
+#include "MantidGeometry/MDGeometry/HKL.h"
+#include "MantidAPI/ExperimentInfo.h"
+#include "MantidAPI/Run.h"
+#include "MantidKernel/TimeSeriesProperty.h"
+#include "MantidAPI/ITableWorkspace.h"
+#include "MantidAPI/WorkspaceFactory.h"
+#include "MantidMDAlgorithms/LoadDNSSCD.h"
+#include <cxxtest/TestSuite.h>
+
+using namespace Mantid;
+using namespace Mantid::Kernel;
+using namespace Mantid::API;
+using namespace Mantid::DataObjects;
+using namespace Mantid::MDAlgorithms;
+
+class LoadDNSSCDTest : 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 LoadDNSSCDTest *createSuite() { return new LoadDNSSCDTest(); }
+  static void destroySuite(LoadDNSSCDTest *suite) { delete suite; }
+
+  LoadDNSSCDTest() : m_fileName("dn134011vana.d_dat") {}
+
+  void test_Init() {
+    LoadDNSSCD alg;
+    TS_ASSERT_THROWS_NOTHING(alg.initialize());
+    TS_ASSERT(alg.isInitialized());
+  }
+
+  void test_Name() {
+    LoadDNSSCD alg;
+    TS_ASSERT_EQUALS(alg.name(), "LoadDNSSCD");
+  }
+
+  void test_Metadata() {
+    // test whether the metadata were loaded correctly
+
+    std::string outWSName("LoadDNSSCDTest_OutputWS");
+    std::string normWSName("LoadDNSSCDTest_OutputWS_norm");
+
+    LoadDNSSCD alg;
+    TS_ASSERT_THROWS_NOTHING(alg.initialize());
+    TS_ASSERT(alg.isInitialized());
+    TS_ASSERT_THROWS_NOTHING(alg.setPropertyValue("Filenames", m_fileName));
+    TS_ASSERT_THROWS_NOTHING(
+        alg.setPropertyValue("OutputWorkspace", outWSName));
+    TS_ASSERT_THROWS_NOTHING(
+        alg.setPropertyValue("NormalizationWorkspace", normWSName));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("Normalization", "monitor"));
+    TS_ASSERT_THROWS_NOTHING(alg.execute(););
+    TS_ASSERT(alg.isExecuted());
+
+    // Retrieve the workspace from data service.
+    IMDEventWorkspace_sptr iws;
+    TS_ASSERT_THROWS_NOTHING(
+        iws = AnalysisDataService::Instance().retrieveWS<IMDEventWorkspace>(
+            outWSName));
+    TS_ASSERT(iws);
+    TS_ASSERT_EQUALS(iws->getNumExperimentInfo(), 1);
+
+    ExperimentInfo_sptr expinfo = iws->getExperimentInfo(0);
+    auto &run = expinfo->run();
+    double d(1e-05);
+    TS_ASSERT_DELTA(run.getPropertyValueAsType<double>("wavelength"), 4.2, d);
+    TimeSeriesProperty<double> *p =
+        dynamic_cast<TimeSeriesProperty<double> *>(run.getProperty("Lambda"));
+    TS_ASSERT_DELTA(p->firstValue(), 0.42, d);
+    p = dynamic_cast<TimeSeriesProperty<double> *>(run.getProperty("Energy"));
+    TS_ASSERT_DELTA(p->firstValue(), 4.640, d);
+    p = dynamic_cast<TimeSeriesProperty<double> *>(run.getProperty("Speed"));
+    TS_ASSERT_DELTA(p->firstValue(), 949.0, d);
+    p = dynamic_cast<TimeSeriesProperty<double> *>(run.getProperty("DeteRota"));
+    TS_ASSERT_DELTA(p->firstValue(), -8.54, d);
+    p = dynamic_cast<TimeSeriesProperty<double> *>(run.getProperty("Huber"));
+    TS_ASSERT_DELTA(p->firstValue(), 79.0, d);
+    p = dynamic_cast<TimeSeriesProperty<double> *>(
+        run.getProperty("Flipper_precession"));
+    TS_ASSERT_DELTA(p->firstValue(), 0.970, d);
+    p = dynamic_cast<TimeSeriesProperty<double> *>(
+        run.getProperty("Flipper_z_compensation"));
+    TS_ASSERT_DELTA(p->firstValue(), 0.400, d);
+    p = dynamic_cast<TimeSeriesProperty<double> *>(run.getProperty("C_a"));
+    TS_ASSERT_DELTA(p->firstValue(), 0.0, d);
+    p = dynamic_cast<TimeSeriesProperty<double> *>(run.getProperty("C_b"));
+    TS_ASSERT_DELTA(p->firstValue(), 0.110, d);
+    p = dynamic_cast<TimeSeriesProperty<double> *>(run.getProperty("C_c"));
+    TS_ASSERT_DELTA(p->firstValue(), -0.500, d);
+    p = dynamic_cast<TimeSeriesProperty<double> *>(run.getProperty("C_z"));
+    TS_ASSERT_DELTA(p->firstValue(), 0.0, d);
+    p = dynamic_cast<TimeSeriesProperty<double> *>(run.getProperty("T1"));
+    TS_ASSERT_DELTA(p->firstValue(), 295.0, d);
+    p = dynamic_cast<TimeSeriesProperty<double> *>(run.getProperty("T2"));
+    TS_ASSERT_DELTA(p->firstValue(), 296.477, d);
+    p = dynamic_cast<TimeSeriesProperty<double> *>(
+        run.getProperty("sample_setpoint"));
+    TS_ASSERT_DELTA(p->firstValue(), 295.0, d);
+    p = dynamic_cast<TimeSeriesProperty<double> *>(run.getProperty("Timer"));
+    TS_ASSERT_DELTA(p->firstValue(), 600.0, d);
+    p = dynamic_cast<TimeSeriesProperty<double> *>(run.getProperty("Monitor"));
+    TS_ASSERT_DELTA(p->firstValue(), 8332872, d);
+    p = dynamic_cast<TimeSeriesProperty<double> *>(
+        run.getProperty("TOF channels"));
+    TS_ASSERT_DELTA(p->firstValue(), 1.0, d);
+    TimeSeriesProperty<std::string> *s =
+        dynamic_cast<TimeSeriesProperty<std::string> *>(
+            run.getProperty("start_time"));
+    TS_ASSERT_EQUALS(s->firstValue(), "2013-04-16T16:11:02");
+    s = dynamic_cast<TimeSeriesProperty<std::string> *>(
+        run.getProperty("stop_time"));
+    TS_ASSERT_EQUALS(s->firstValue(), "2013-04-16T16:21:03");
+    AnalysisDataService::Instance().remove(outWSName);
+  }
+
+  void test_DataWSStructure() {
+    std::string outWSName("LoadDNSSCDTest_OutputWS");
+    std::string normWSName("LoadDNSSCDTest_OutputWS_norm");
+
+    LoadDNSSCD alg;
+    TS_ASSERT_THROWS_NOTHING(alg.initialize());
+    TS_ASSERT(alg.isInitialized());
+    TS_ASSERT_THROWS_NOTHING(alg.setPropertyValue("Filenames", m_fileName));
+    TS_ASSERT_THROWS_NOTHING(
+        alg.setPropertyValue("OutputWorkspace", outWSName));
+    TS_ASSERT_THROWS_NOTHING(
+        alg.setPropertyValue("NormalizationWorkspace", normWSName));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("Normalization", "monitor"));
+    TS_ASSERT_THROWS_NOTHING(alg.execute(););
+    TS_ASSERT(alg.isExecuted());
+
+    // Retrieve the workspace from data service.
+    IMDEventWorkspace_sptr iws;
+    TS_ASSERT_THROWS_NOTHING(
+        iws = AnalysisDataService::Instance().retrieveWS<IMDEventWorkspace>(
+            outWSName));
+    TS_ASSERT(iws);
+
+    TS_ASSERT_EQUALS(iws->getNumDims(), 3);
+    TS_ASSERT_EQUALS(iws->getNPoints(), 24);
+    TS_ASSERT_EQUALS(iws->id(), "MDEventWorkspace<MDEvent,3>");
+
+    // test box controller
+    BoxController_sptr bc = iws->getBoxController();
+    TS_ASSERT(bc);
+    TS_ASSERT_EQUALS(bc->getNumMDBoxes().size(), 6);
+
+    // test dimensions
+    std::vector<std::string> v = {"H", "K", "L"};
+    for (auto i = 0; i < 3; i++) {
+      auto dim = iws->getDimension(i);
+      TS_ASSERT(dim);
+      TS_ASSERT_EQUALS(dim->getName(), v[i]);
+      TS_ASSERT_EQUALS(dim->getNBins(), 5);
+      double d(1.0e-05);
+      TS_ASSERT_DELTA(dim->getMinimum(), -2.991993, d);
+      TS_ASSERT_DELTA(dim->getMaximum(), 2.991993, d);
+    }
+    AnalysisDataService::Instance().remove(outWSName);
+  }
+
+  void test_DataWS() {
+    // test whether the metadata were loaded correctly
+
+    std::string outWSName("LoadDNSSCDTest_OutputWS");
+    std::string normWSName("LoadDNSSCDTest_OutputWS_norm");
+
+    LoadDNSSCD alg;
+    TS_ASSERT_THROWS_NOTHING(alg.initialize());
+    TS_ASSERT(alg.isInitialized());
+    TS_ASSERT_THROWS_NOTHING(alg.setPropertyValue("Filenames", m_fileName));
+    TS_ASSERT_THROWS_NOTHING(
+        alg.setPropertyValue("OutputWorkspace", outWSName));
+    TS_ASSERT_THROWS_NOTHING(
+        alg.setPropertyValue("NormalizationWorkspace", normWSName));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("Normalization", "monitor"));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("a", 6.84));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("b", 6.84));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("c", 4.77));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("alpha", 90.0));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("beta", 90.0));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("gamma", 120.0));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("OmegaOffset", -43.0));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("HKL1", "1,1,0"));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("HKL2", "0,0,1"));
+    TS_ASSERT_THROWS_NOTHING(alg.execute(););
+    TS_ASSERT(alg.isExecuted());
+
+    // Retrieve the workspace from data service.
+    IMDEventWorkspace_sptr iws;
+    TS_ASSERT_THROWS_NOTHING(
+        iws = AnalysisDataService::Instance().retrieveWS<IMDEventWorkspace>(
+            outWSName));
+    TS_ASSERT(iws);
+
+    std::vector<API::IMDNode *> boxes(0, NULL);
+    iws->getBoxes(boxes, 10000, false);
+    TSM_ASSERT_EQUALS("Number of boxes", boxes.size(), 1);
+    API::IMDNode *box = boxes[0];
+    // there are 24 points in the data file
+    TS_ASSERT_EQUALS(box->getNPoints(), 24);
+    std::vector<coord_t> events;
+    size_t ncols;
+    box->getEventsData(events, ncols);
+    // 7 columns: I, err^2, run_num, det_id, h, k, l
+    TS_ASSERT_EQUALS(ncols, 7);
+    // 7*24 = 168
+    TS_ASSERT_EQUALS(events.size(), 168);
+    // reference vector
+    const std::vector<coord_t> ref = {
+        4366, 4366, 0, 0, -0.09776273f, -0.09776273f, 0.10005156f, 31461, 31461,
+        0, 1, -0.15959044f, -0.15959044f, 0.14884006f, 33314, 33314, 0, 2,
+        -0.224231616093f, -0.224231616093f, 0.189927174618f, 32369, 32369, 0, 3,
+        -0.291194311172f, -0.291194311172f, 0.223000198347f, 31851, 31851, 0, 4,
+        -0.359968893923f, -0.359968893923f, 0.247807429194f, 30221, 30221, 0, 5,
+        -0.430031948245f, -0.430031948245f, 0.264160069153f, 26267, 26267, 0, 6,
+        -0.500850251989f, -0.500850251989f, 0.271933664761f, 26788, 26788, 0, 7,
+        -0.571884835101f, -0.571884835101f, 0.27106905426f, 29729, 29729, 0, 8,
+        -0.642595081514f, -0.642595081514f, 0.26157281786f, 30188, 30188, 0, 9,
+        -0.712442843555f, -0.712442843555f, 0.243517227652f, 28116, 28116, 0,
+        10, -0.78089653758f, -0.78089653758f, 0.217039697581f, 30277, 30277, 0,
+        11, -0.847435189645f, -0.847435189645f, 0.182341737639f, 20231, 20231,
+        0, 12, -0.911552400429f, -0.911552400429f, 0.13968742025f, 24538, 24538,
+        0, 13, -0.972760199244f, -0.972760199244f, 0.089401370527f, 16416,
+        16416, 0, 14, -1.03059275778f, -1.03059275778f, 0.0318662956709f, 20225,
+        20225, 0, 15, -1.08460993535f, -1.08460993535f, -0.0324799276578f,
+        19957, 19957, 0, 16, -1.13440062862f, -1.13440062862f, -0.103147585846f,
+        19570, 19570, 0, 17, -1.17958590034f, -1.17958590034f, -0.179598855345f,
+        20743, 20743, 0, 18, -1.21982186332f, -1.21982186332f, -0.261251895832f,
+        22758, 22758, 0, 19, -1.25480229757f, -1.25480229757f, -0.347485278364f,
+        23001, 23001, 0, 20, -1.28426098088f, -1.28426098088f, -0.437642714831f,
+        21836, 21836, 0, 21, -1.30797371487f, -1.30797371487f, -0.531038052704f,
+        23877, 23877, 0, 22, -1.32576003133f, -1.32576003133f, -0.626960497068f,
+        13340, 13340, 0, 23, -1.33748456564f, -1.33748456564f,
+        -0.724680020201f};
+    double d(1.0e-06);
+    for (auto i = 0; i < 168; i++) {
+      TS_ASSERT_DELTA(events[i], ref[i], d);
+    }
+
+    AnalysisDataService::Instance().remove(outWSName);
+  }
+
+  void test_NormWSStructure() {
+    std::string outWSName("LoadDNSSCDTest_OutputWS");
+    std::string normWSName("LoadDNSSCDTest_OutputWS_norm");
+
+    LoadDNSSCD alg;
+    TS_ASSERT_THROWS_NOTHING(alg.initialize());
+    TS_ASSERT(alg.isInitialized());
+    TS_ASSERT_THROWS_NOTHING(alg.setPropertyValue("Filenames", m_fileName));
+    TS_ASSERT_THROWS_NOTHING(
+        alg.setPropertyValue("OutputWorkspace", outWSName));
+    TS_ASSERT_THROWS_NOTHING(
+        alg.setPropertyValue("NormalizationWorkspace", normWSName));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("Normalization", "monitor"));
+    TS_ASSERT_THROWS_NOTHING(alg.execute(););
+    TS_ASSERT(alg.isExecuted());
+
+    // Retrieve the workspace from data service.
+    IMDEventWorkspace_sptr nws;
+    TS_ASSERT_THROWS_NOTHING(
+        nws = AnalysisDataService::Instance().retrieveWS<IMDEventWorkspace>(
+            normWSName));
+    TS_ASSERT(nws);
+
+    TS_ASSERT_EQUALS(nws->getNumDims(), 3);
+    TS_ASSERT_EQUALS(nws->getNPoints(), 24);
+    TS_ASSERT_EQUALS(nws->id(), "MDEventWorkspace<MDEvent,3>");
+
+    // test box controller
+    BoxController_sptr bc = nws->getBoxController();
+    TS_ASSERT(bc);
+    TS_ASSERT_EQUALS(bc->getNumMDBoxes().size(), 6);
+
+    // test dimensions
+    std::vector<std::string> v = {"H", "K", "L"};
+    for (auto i = 0; i < 3; i++) {
+      auto dim = nws->getDimension(i);
+      TS_ASSERT(dim);
+      TS_ASSERT_EQUALS(dim->getName(), v[i]);
+      TS_ASSERT_EQUALS(dim->getNBins(), 5);
+      double d(1.0e-05);
+      TS_ASSERT_DELTA(dim->getMinimum(), -2.991993, d);
+      TS_ASSERT_DELTA(dim->getMaximum(), 2.991993, d);
+    }
+    AnalysisDataService::Instance().remove(normWSName);
+  }
+
+  void test_NormMonitor() {
+    // test whether the metadata were loaded correctly
+
+    std::string outWSName("LoadDNSSCDTest_OutputWS");
+    std::string normWSName("LoadDNSSCDTest_OutputWS_norm");
+
+    LoadDNSSCD alg;
+    TS_ASSERT_THROWS_NOTHING(alg.initialize());
+    TS_ASSERT(alg.isInitialized());
+    TS_ASSERT_THROWS_NOTHING(alg.setPropertyValue("Filenames", m_fileName));
+    TS_ASSERT_THROWS_NOTHING(
+        alg.setPropertyValue("OutputWorkspace", outWSName));
+    TS_ASSERT_THROWS_NOTHING(
+        alg.setPropertyValue("NormalizationWorkspace", normWSName));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("Normalization", "monitor"));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("a", 6.84));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("b", 6.84));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("c", 4.77));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("alpha", 90.0));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("beta", 90.0));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("gamma", 120.0));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("OmegaOffset", -43.0));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("HKL1", "1,1,0"));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("HKL2", "0,0,1"));
+    TS_ASSERT_THROWS_NOTHING(alg.execute(););
+    TS_ASSERT(alg.isExecuted());
+
+    // Retrieve the workspace from data service.
+    IMDEventWorkspace_sptr nws;
+    TS_ASSERT_THROWS_NOTHING(
+        nws = AnalysisDataService::Instance().retrieveWS<IMDEventWorkspace>(
+            normWSName));
+    TS_ASSERT(nws);
+
+    std::vector<API::IMDNode *> boxes(0, NULL);
+    nws->getBoxes(boxes, 10000, false);
+    TSM_ASSERT_EQUALS("Number of boxes", boxes.size(), 1);
+    API::IMDNode *box = boxes[0];
+    // there are 24 points in the data file
+    TS_ASSERT_EQUALS(box->getNPoints(), 24);
+    std::vector<coord_t> events;
+    size_t ncols;
+    box->getEventsData(events, ncols);
+    // 7 columns: I, err^2, run_num, det_id, h, k, l
+    TS_ASSERT_EQUALS(ncols, 7);
+    // 7*24 = 168
+    TS_ASSERT_EQUALS(events.size(), 168);
+    // reference vector
+    const std::vector<coord_t> ref = {
+        8332872, 8332872, 0, 0, -0.09776273f, -0.09776273f, 0.10005156f,
+        8332872, 8332872, 0, 1, -0.15959044f, -0.15959044f, 0.14884006f,
+        8332872, 8332872, 0, 2, -0.224231616093f, -0.224231616093f,
+        0.189927174618f, 8332872, 8332872, 0, 3, -0.291194311172f,
+        -0.291194311172f, 0.223000198347f, 8332872, 8332872, 0, 4,
+        -0.359968893923f, -0.359968893923f, 0.247807429194f, 8332872, 8332872,
+        0, 5, -0.430031948245f, -0.430031948245f, 0.264160069153f, 8332872,
+        8332872, 0, 6, -0.500850251989f, -0.500850251989f, 0.271933664761f,
+        8332872, 8332872, 0, 7, -0.571884835101f, -0.571884835101f,
+        0.27106905426f, 8332872, 8332872, 0, 8, -0.642595081514f,
+        -0.642595081514f, 0.26157281786f, 8332872, 8332872, 0, 9,
+        -0.712442843555f, -0.712442843555f, 0.243517227652f, 8332872, 8332872,
+        0, 10, -0.78089653758f, -0.78089653758f, 0.217039697581f, 8332872,
+        8332872, 0, 11, -0.847435189645f, -0.847435189645f, 0.182341737639f,
+        8332872, 8332872, 0, 12, -0.911552400429f, -0.911552400429f,
+        0.13968742025f, 8332872, 8332872, 0, 13, -0.972760199244f,
+        -0.972760199244f, 0.089401370527f, 8332872, 8332872, 0, 14,
+        -1.03059275778f, -1.03059275778f, 0.0318662956709f, 8332872, 8332872, 0,
+        15, -1.08460993535f, -1.08460993535f, -0.0324799276578f, 8332872,
+        8332872, 0, 16, -1.13440062862f, -1.13440062862f, -0.103147585846f,
+        8332872, 8332872, 0, 17, -1.17958590034f, -1.17958590034f,
+        -0.179598855345f, 8332872, 8332872, 0, 18, -1.21982186332f,
+        -1.21982186332f, -0.261251895832f, 8332872, 8332872, 0, 19,
+        -1.25480229757f, -1.25480229757f, -0.347485278364f, 8332872, 8332872, 0,
+        20, -1.28426098088f, -1.28426098088f, -0.437642714831f, 8332872,
+        8332872, 0, 21, -1.30797371487f, -1.30797371487f, -0.531038052704f,
+        8332872, 8332872, 0, 22, -1.32576003133f, -1.32576003133f,
+        -0.626960497068f, 8332872, 8332872, 0, 23, -1.33748456564f,
+        -1.33748456564f, -0.724680020201f};
+    double d(1.0e-06);
+    for (auto i = 0; i < 168; i++) {
+      TS_ASSERT_DELTA(events[i], ref[i], d);
+    }
+
+    AnalysisDataService::Instance().remove(normWSName);
+  }
+
+  void test_NormTime() {
+    // test whether the metadata were loaded correctly
+
+    std::string outWSName("LoadDNSSCDTest_OutputWS");
+    std::string normWSName("LoadDNSSCDTest_OutputWS_norm");
+
+    LoadDNSSCD alg;
+    TS_ASSERT_THROWS_NOTHING(alg.initialize());
+    TS_ASSERT(alg.isInitialized());
+    TS_ASSERT_THROWS_NOTHING(alg.setPropertyValue("Filenames", m_fileName));
+    TS_ASSERT_THROWS_NOTHING(
+        alg.setPropertyValue("OutputWorkspace", outWSName));
+    TS_ASSERT_THROWS_NOTHING(
+        alg.setPropertyValue("NormalizationWorkspace", normWSName));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("Normalization", "time"));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("a", 6.84));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("b", 6.84));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("c", 4.77));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("alpha", 90.0));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("beta", 90.0));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("gamma", 120.0));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("OmegaOffset", -43.0));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("HKL1", "1,1,0"));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("HKL2", "0,0,1"));
+    TS_ASSERT_THROWS_NOTHING(alg.execute(););
+    TS_ASSERT(alg.isExecuted());
+
+    // Retrieve the workspace from data service.
+    IMDEventWorkspace_sptr nws;
+    TS_ASSERT_THROWS_NOTHING(
+        nws = AnalysisDataService::Instance().retrieveWS<IMDEventWorkspace>(
+            normWSName));
+    TS_ASSERT(nws);
+
+    std::vector<API::IMDNode *> boxes(0, NULL);
+    nws->getBoxes(boxes, 10000, false);
+    TSM_ASSERT_EQUALS("Number of boxes", boxes.size(), 1);
+    API::IMDNode *box = boxes[0];
+    // there are 24 points in the data file
+    TS_ASSERT_EQUALS(box->getNPoints(), 24);
+    std::vector<coord_t> events;
+    size_t ncols;
+    box->getEventsData(events, ncols);
+    // 7 columns: I, err^2, run_num, det_id, h, k, l
+    TS_ASSERT_EQUALS(ncols, 7);
+    // 7*24 = 168
+    TS_ASSERT_EQUALS(events.size(), 168);
+    // reference vector
+    const std::vector<coord_t> ref = {
+        600, 0, 0, 0, -0.09776273f, -0.09776273f, 0.10005156f, 600, 0, 0, 1,
+        -0.15959044f, -0.15959044f, 0.14884006f, 600, 0, 0, 2, -0.224231616093f,
+        -0.224231616093f, 0.189927174618f, 600, 0, 0, 3, -0.291194311172f,
+        -0.291194311172f, 0.223000198347f, 600, 0, 0, 4, -0.359968893923f,
+        -0.359968893923f, 0.247807429194f, 600, 0, 0, 5, -0.430031948245f,
+        -0.430031948245f, 0.264160069153f, 600, 0, 0, 6, -0.500850251989f,
+        -0.500850251989f, 0.271933664761f, 600, 0, 0, 7, -0.571884835101f,
+        -0.571884835101f, 0.27106905426f, 600, 0, 0, 8, -0.642595081514f,
+        -0.642595081514f, 0.26157281786f, 600, 0, 0, 9, -0.712442843555f,
+        -0.712442843555f, 0.243517227652f, 600, 0, 0, 10, -0.78089653758f,
+        -0.78089653758f, 0.217039697581f, 600, 0, 0, 11, -0.847435189645f,
+        -0.847435189645f, 0.182341737639f, 600, 0, 0, 12, -0.911552400429f,
+        -0.911552400429f, 0.13968742025f, 600, 0, 0, 13, -0.972760199244f,
+        -0.972760199244f, 0.089401370527f, 600, 0, 0, 14, -1.03059275778f,
+        -1.03059275778f, 0.0318662956709f, 600, 0, 0, 15, -1.08460993535f,
+        -1.08460993535f, -0.0324799276578f, 600, 0, 0, 16, -1.13440062862f,
+        -1.13440062862f, -0.103147585846f, 600, 0, 0, 17, -1.17958590034f,
+        -1.17958590034f, -0.179598855345f, 600, 0, 0, 18, -1.21982186332f,
+        -1.21982186332f, -0.261251895832f, 600, 0, 0, 19, -1.25480229757f,
+        -1.25480229757f, -0.347485278364f, 600, 0, 0, 20, -1.28426098088f,
+        -1.28426098088f, -0.437642714831f, 600, 0, 0, 21, -1.30797371487f,
+        -1.30797371487f, -0.531038052704f, 600, 0, 0, 22, -1.32576003133f,
+        -1.32576003133f, -0.626960497068f, 600, 0, 0, 23, -1.33748456564f,
+        -1.33748456564f, -0.724680020201f};
+    double d(1.0e-06);
+    for (auto i = 0; i < 168; i++) {
+      TS_ASSERT_DELTA(events[i], ref[i], d);
+    }
+
+    AnalysisDataService::Instance().remove(normWSName);
+  }
+
+  void test_SaveHuber() {
+    std::string outWSName("LoadDNSSCDTest_OutputWS");
+    std::string normWSName("LoadDNSSCDTest_OutputWS_norm");
+    std::string tWSName("LoadDNSSCDTest_Huber");
+
+    LoadDNSSCD alg;
+    TS_ASSERT_THROWS_NOTHING(alg.initialize());
+    TS_ASSERT(alg.isInitialized());
+    TS_ASSERT_THROWS_NOTHING(alg.setPropertyValue("Filenames", m_fileName));
+    TS_ASSERT_THROWS_NOTHING(
+        alg.setPropertyValue("OutputWorkspace", outWSName));
+    TS_ASSERT_THROWS_NOTHING(
+        alg.setPropertyValue("NormalizationWorkspace", normWSName));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("Normalization", "monitor"));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("SaveHuberTo", tWSName));
+    TS_ASSERT_THROWS_NOTHING(alg.execute(););
+    TS_ASSERT(alg.isExecuted());
+
+    // Retrieve the workspace from data service.
+    ITableWorkspace_sptr tws;
+    TS_ASSERT_THROWS_NOTHING(
+        tws = AnalysisDataService::Instance().retrieveWS<ITableWorkspace>(
+            tWSName));
+    TS_ASSERT(tws);
+
+    // check that workspace has 1 row and 1 column
+    TS_ASSERT_EQUALS(tws->rowCount(), 1);
+    TS_ASSERT_EQUALS(tws->columnCount(), 1);
+    std::vector<std::string> columnNames = {"Huber(degrees)"};
+    TS_ASSERT_EQUALS(tws->getColumnNames(), columnNames);
+
+    // test the value
+    TS_ASSERT_DELTA(tws->cell<double>(0, 0), 79.0, 1.0e-06);
+    AnalysisDataService::Instance().remove(tWSName);
+  }
+
+  void test_LoadHuber() {
+    std::string outWSName("LoadDNSSCDTest_OutputWS");
+    std::string normWSName("LoadDNSSCDTest_OutputWS_norm");
+    std::string tWSName2("LoadDNSSCDTest_Huber_save");
+    std::string tWSName1("LoadDNSSCDTest_Huber_load");
+
+    // create a test table workspace
+    ITableWorkspace_sptr huberWS =
+        WorkspaceFactory::Instance().createTable("TableWorkspace");
+    huberWS->addColumn("double", "Huber(degrees)");
+    const std::vector<double> vals = {77.0, 92.0, 122.0};
+    auto n = vals.size();
+    for (size_t i = 0; i < n; i++) {
+      huberWS->appendRow();
+      huberWS->cell<double>(i, 0) = vals[i];
+    }
+    AnalysisDataService::Instance().add(tWSName1, huberWS);
+
+    // run the algorithm
+    LoadDNSSCD alg;
+    TS_ASSERT_THROWS_NOTHING(alg.initialize());
+    TS_ASSERT(alg.isInitialized());
+    TS_ASSERT_THROWS_NOTHING(alg.setPropertyValue("Filenames", m_fileName));
+    TS_ASSERT_THROWS_NOTHING(
+        alg.setPropertyValue("OutputWorkspace", outWSName));
+    TS_ASSERT_THROWS_NOTHING(
+        alg.setPropertyValue("NormalizationWorkspace", normWSName));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("Normalization", "monitor"));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("LoadHuberFrom", tWSName1));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("SaveHuberTo", tWSName2));
+    TS_ASSERT_THROWS_NOTHING(alg.execute(););
+    TS_ASSERT(alg.isExecuted());
+
+    // Retrieve the workspace from data service.
+    IMDEventWorkspace_sptr iws;
+    TS_ASSERT_THROWS_NOTHING(
+        iws = AnalysisDataService::Instance().retrieveWS<IMDEventWorkspace>(
+            outWSName));
+    TS_ASSERT(iws);
+
+    TS_ASSERT_EQUALS(iws->getNumDims(), 3);
+    // data should be replicated for each huber value
+    TS_ASSERT_EQUALS(iws->getNPoints(), 24 * n);
+
+    // Retrieve the table workspace from data service.
+    ITableWorkspace_sptr tws;
+    TS_ASSERT_THROWS_NOTHING(
+        tws = AnalysisDataService::Instance().retrieveWS<ITableWorkspace>(
+            tWSName2));
+    TS_ASSERT(tws);
+
+    // check that workspace has 1 row and 1 column
+    TS_ASSERT_EQUALS(tws->rowCount(), n);
+    TS_ASSERT_EQUALS(tws->columnCount(), 1);
+    std::vector<std::string> columnNames = {"Huber(degrees)"};
+    TS_ASSERT_EQUALS(tws->getColumnNames(), columnNames);
+
+    // test the values
+    for (size_t i = 0; i < n; i++)
+      TS_ASSERT_DELTA(tws->cell<double>(i, 0), vals[i], 1.0e-06);
+    AnalysisDataService::Instance().remove(tWSName1);
+    AnalysisDataService::Instance().remove(tWSName2);
+    AnalysisDataService::Instance().remove(outWSName);
+  }
+
+  void test_2ThetaLimits() {
+    // test whether the scattering angle limits work correctly
+
+    std::string outWSName("LoadDNSSCDTest_OutputWS");
+    std::string normWSName("LoadDNSSCDTest_OutputWS_norm");
+
+    LoadDNSSCD alg;
+    TS_ASSERT_THROWS_NOTHING(alg.initialize());
+    TS_ASSERT(alg.isInitialized());
+    TS_ASSERT_THROWS_NOTHING(alg.setPropertyValue("Filenames", m_fileName));
+    TS_ASSERT_THROWS_NOTHING(
+        alg.setPropertyValue("OutputWorkspace", outWSName));
+    TS_ASSERT_THROWS_NOTHING(
+        alg.setPropertyValue("NormalizationWorkspace", normWSName));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("Normalization", "monitor"));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("a", 6.84));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("b", 6.84));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("c", 4.77));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("alpha", 90.0));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("beta", 90.0));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("gamma", 120.0));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("OmegaOffset", -43.0));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("HKL1", "1,1,0"));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("HKL2", "0,0,1"));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("TwoThetaLimits", "20.0,55.0"));
+    TS_ASSERT_THROWS_NOTHING(alg.execute(););
+    TS_ASSERT(alg.isExecuted());
+
+    // Retrieve the workspace from data service.
+    IMDEventWorkspace_sptr iws;
+    TS_ASSERT_THROWS_NOTHING(
+        iws = AnalysisDataService::Instance().retrieveWS<IMDEventWorkspace>(
+            outWSName));
+    TS_ASSERT(iws);
+
+    std::vector<API::IMDNode *> boxes(0, NULL);
+    iws->getBoxes(boxes, 10000, false);
+    TSM_ASSERT_EQUALS("Number of boxes", boxes.size(), 1);
+    API::IMDNode *box = boxes[0];
+    // there are 7 points (the rest is outside of 2theta limits)
+    TS_ASSERT_EQUALS(box->getNPoints(), 7);
+    std::vector<coord_t> events;
+    size_t ncols;
+    box->getEventsData(events, ncols);
+    // 7 columns: I, err^2, run_num, det_id, h, k, l
+    TS_ASSERT_EQUALS(ncols, 7);
+    // 7*7 = 49
+    TS_ASSERT_EQUALS(events.size(), 49);
+    // reference vector
+    const std::vector<coord_t> ref = {
+        32369, 32369, 0, 3, -0.291194311172f, -0.291194311172f, 0.223000198347f,
+        31851, 31851, 0, 4, -0.359968893923f, -0.359968893923f, 0.247807429194f,
+        30221, 30221, 0, 5, -0.430031948245f, -0.430031948245f, 0.264160069153f,
+        26267, 26267, 0, 6, -0.500850251989f, -0.500850251989f, 0.271933664761f,
+        26788, 26788, 0, 7, -0.571884835101f, -0.571884835101f, 0.27106905426f,
+        29729, 29729, 0, 8, -0.642595081514f, -0.642595081514f, 0.26157281786f,
+        30188, 30188, 0, 9, -0.712442843555f, -0.712442843555f,
+        0.243517227652f};
+    double d(1.0e-06);
+    for (auto i = 0; i < 49; i++) {
+      TS_ASSERT_DELTA(events[i], ref[i], d);
+    }
+
+    AnalysisDataService::Instance().remove(outWSName);
+
+    // test the normalization workspace as well
+    IMDEventWorkspace_sptr nws;
+    TS_ASSERT_THROWS_NOTHING(
+        nws = AnalysisDataService::Instance().retrieveWS<IMDEventWorkspace>(
+            normWSName));
+    TS_ASSERT(nws);
+    // there are 7 points (the rest is outside of 2theta limits)
+    TS_ASSERT_EQUALS(nws->getNPoints(), 7);
+
+    AnalysisDataService::Instance().remove(normWSName);
+  }
+
+  void test_Load2() {
+    // algorithm should load one file and skip the TOF file
+
+    std::string outWSName("LoadDNSSCDTest_OutputWS");
+    std::string normWSName("LoadDNSSCDTest_OutputWS_norm");
+    std::string filenames = "dn134011vana.d_dat,dnstof.d_dat";
+
+    LoadDNSSCD alg;
+    alg.setRethrows(true);
+    TS_ASSERT_THROWS_NOTHING(alg.initialize());
+    TS_ASSERT(alg.isInitialized());
+    TS_ASSERT_THROWS_NOTHING(alg.setPropertyValue("Filenames", filenames));
+    TS_ASSERT_THROWS_NOTHING(
+        alg.setPropertyValue("OutputWorkspace", outWSName));
+    TS_ASSERT_THROWS_NOTHING(
+        alg.setPropertyValue("NormalizationWorkspace", normWSName));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("Normalization", "monitor"));
+
+    // algorithm should throw only if no valid files is provided
+    TS_ASSERT_THROWS_NOTHING(alg.execute());
+    TS_ASSERT(alg.isExecuted());
+
+    // Retrieve the workspace from data service.
+    IMDEventWorkspace_sptr iws;
+    TS_ASSERT_THROWS_NOTHING(
+        iws = AnalysisDataService::Instance().retrieveWS<IMDEventWorkspace>(
+            outWSName));
+    TS_ASSERT(iws);
+
+    TS_ASSERT_EQUALS(iws->getNumDims(), 3);
+    TS_ASSERT_EQUALS(iws->getNPoints(), 24);
+    AnalysisDataService::Instance().remove(outWSName);
+  }
+
+  //-------------------- Test failure --------------------------------------
+  void test_failTOF() {
+    // algorithm does not load TOF files
+
+    std::string outWSName("LoadDNSSCDTest_OutputWS");
+    std::string normWSName("LoadDNSSCDTest_OutputWS_norm");
+
+    LoadDNSSCD alg;
+    alg.setRethrows(true);
+    TS_ASSERT_THROWS_NOTHING(alg.initialize());
+    TS_ASSERT(alg.isInitialized());
+    TS_ASSERT_THROWS_NOTHING(alg.setPropertyValue("Filenames", "dnstof.d_dat"));
+    TS_ASSERT_THROWS_NOTHING(
+        alg.setPropertyValue("OutputWorkspace", outWSName));
+    TS_ASSERT_THROWS_NOTHING(
+        alg.setPropertyValue("NormalizationWorkspace", normWSName));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("Normalization", "monitor"));
+
+    // algorithm should throw if no valid files is provided
+    TS_ASSERT_THROWS(alg.execute(), std::runtime_error);
+    TS_ASSERT(!alg.isExecuted());
+  }
+
+private:
+  std::string m_fileName;
+};
+
+#endif /* MANTID_MDALGORITHMS_LOADDNSSCDEWEST_H_ */
diff --git a/Framework/PythonInterface/plugins/algorithms/ApplyDetectorScanEffCorr.py b/Framework/PythonInterface/plugins/algorithms/ApplyDetectorScanEffCorr.py
index 621de29ce97b7e446b86097e4b6421fd0e70ff2c..9498be25f5c0a8e17afdd4c94cc2d12a024f1171 100644
--- a/Framework/PythonInterface/plugins/algorithms/ApplyDetectorScanEffCorr.py
+++ b/Framework/PythonInterface/plugins/algorithms/ApplyDetectorScanEffCorr.py
@@ -1,6 +1,6 @@
 from __future__ import print_function
 
-from mantid.simpleapi import CreateWorkspace, RenameWorkspace
+from mantid.simpleapi import CreateWorkspace, Transpose, Multiply
 from mantid.api import AlgorithmFactory, PropertyMode, PythonAlgorithm, WorkspaceProperty
 from mantid.kernel import Direction
 import numpy as np
@@ -36,34 +36,18 @@ class ApplyDetectorScanEffCorr(PythonAlgorithm):
 
     def PyExec(self):
         input_ws = self.getProperty("InputWorkspace").value
-        efficiency_workspace = self.getProperty("DetectorEfficiencyWorkspace").value
-
-        y_values = input_ws.extractY()
-        y_values = y_values.reshape(y_values.size)
-        e_values = input_ws.extractE()
-        e_values = e_values.reshape(e_values.size)
-
-        efficiency_values = efficiency_workspace.extractY()
-        efficiency_values = efficiency_values.reshape(efficiency_values.size)
-
-        detector_info = input_ws.detectorInfo()
-        for i in range(detector_info.size()):
-            if detector_info.isMonitor(i):
-                efficiency_values = np.insert(efficiency_values, 0, 1) # add the monitor efficiency
-
-        if (y_values.size % efficiency_values.size) is not 0:
-            raise ValueError('Number of entries in input workspace is not a multiple of number of efficiencies in detector efficiency '
+        eff_ws = self.getProperty("DetectorEfficiencyWorkspace").value
+        transposed = Transpose(InputWorkspace=eff_ws, StoreInADS=False)
+        efficiencies = transposed.extractY().flatten()
+        errors = transposed.extractE().flatten()
+        n_hist = input_ws.getNumberHistograms()
+        if n_hist % efficiencies.size != 0:
+            raise ValueError('Number of histograms in input workspace is not a multiple of number of entries in detector efficiency '
                              'workspace.')
-        number_time_indexes = y_values.size / efficiency_values.size
-
-        full_efficiency_values = np.repeat(efficiency_values, number_time_indexes)
-        y_values *= full_efficiency_values
-        e_values *= full_efficiency_values
-
-        __output_ws = CreateWorkspace(DataX=input_ws.extractX(), DataY=y_values, DataE=e_values, Nspec=y_values.size,
-                                      ParentWorkspace=input_ws)
-
-        RenameWorkspace(__output_ws, self.getPropertyValue("OutputWorkspace"))
-        self.setProperty("OutputWorkspace", __output_ws)
+        n_time_indexes = n_hist / efficiencies.size
+        to_multiply = CreateWorkspace(DataY=np.repeat(efficiencies, n_time_indexes),DataE=np.repeat(errors, n_time_indexes),
+                                      DataX=np.zeros(n_hist), NSpec=n_hist, StoreInADS=False)
+        output = Multiply(LHSWorkspace=input_ws, RHSWorkspace=to_multiply, OutputWorkspace=self.getPropertyValue("OutputWorkspace"))
+        self.setProperty("OutputWorkspace", output)
 
 AlgorithmFactory.subscribe(ApplyDetectorScanEffCorr)
diff --git a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/PowderDiffILLDetEffCorr.py b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/PowderDiffILLDetEffCorr.py
index d5444a4bab1b5629f551473dc63f503e89fe8948..81877ccdcff5835588a1d133faf00d0a0f5bf630 100644
--- a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/PowderDiffILLDetEffCorr.py
+++ b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/PowderDiffILLDetEffCorr.py
@@ -2,31 +2,69 @@ from __future__ import (absolute_import, division, print_function)
 
 import math
 import numpy as np
+import numpy.ma as ma
 from mantid.kernel import StringListValidator, Direction, IntArrayBoundedValidator, IntArrayProperty, \
     CompositeValidator, IntArrayLengthValidator, IntArrayOrderedPairsValidator, FloatArrayOrderedPairsValidator, \
-    FloatArrayProperty, VisibleWhenProperty, PropertyCriterion
-from mantid.api import PythonAlgorithm, FileProperty, FileAction, Progress, MatrixWorkspaceProperty, PropertyMode
+    FloatArrayProperty, VisibleWhenProperty, PropertyCriterion, IntBoundedValidator
+from mantid.api import PythonAlgorithm, FileProperty, FileAction, Progress, MatrixWorkspaceProperty, PropertyMode, \
+    MultipleFileProperty
 from mantid.simpleapi import *
 
 
+def _crop_bins(ws, bin_min, bin_max, out):
+    """
+        Extracts workspace data in [bin_min, bin_max] for ragged workspace
+    """
+    y = mtd[ws].extractY()
+    e = mtd[ws].extractE()
+    x = mtd[ws].extractX()
+    CreateWorkspace(DataX=x[:,bin_min:bin_max],
+                    DataY=y[:,bin_min:bin_max],
+                    DataE=e[:,bin_min:bin_max],
+                    NSpec=mtd[ws].getNumberHistograms(),
+                    OutputWorkspace=out)
+
+
+def _divide_friendly(ws1, ws2, out):
+    """
+        Divides ws1/ws2 ignoring the difference in x-axis
+    """
+    mtd[ws2].setX(0, mtd[ws1].readX(0))
+    Divide(LHSWorkspace=ws1, RHSWorkspace=ws2, OutputWorkspace=out)
+
+
+def _plus_friendly(ws1, ws2, out):
+    """
+        Sums ws1+ws2 ignoring the difference in x-axis
+    """
+    mtd[ws2].setX(0, mtd[ws1].readX(0))
+    Plus(LHSWorkspace=ws1, RHSWorkspace=ws2, OutputWorkspace=out)
+
+
 class PowderDiffILLDetEffCorr(PythonAlgorithm):
 
     _out_name = None            # the name of the output workspace
-    _input_file = None          # input file (numor), must be a detector scan
+    _input_files = None         # input files (numor), must be detector scans (to list for D2B, to merge for D20)
     _calib_file = None          # file containing previously derived calibration constants
     _progress = None            # progress tracking
     _method = None              # calibration method
     _scan_points = None         # number of scan points (time indices)
     _out_response = None        # the name of the second output workspace with merged response
     _bin_offset = None          # this holds int(scan step / pixel size)
-    _n_det = None               # number of detector pixels (typically 3072)
+    _n_det = None               # number of detector pixels for D20 (=3072)
     _normalise_to = None        # normalisation option
-    _pixel_range = None         # range of the pixels to derive calibration for, e.g. 65,3072
-    _regions_of_interest = None # ROI to normalise to, e.g. 10,50,70,100, typically just one range
-    _interpolate = None         # whether to interpolate 2thetas before taking relative ratios
-    _excluded_ranges = None     # 2theta ranges to exclude when deriving the relative calibration factor
-                                # e.g. -20,0,40,50
+    _pixel_range = None         # range of the pixels to derive calibration for D20, e.g. 65,3072
+    _regions_of_interest = None # ROI to normalise to, e.g. 10,50,70,100, typically just one range, used for D20
+    _interpolate = None         # whether to interpolate 2thetas before taking relative ratios (D20)
+    _excluded_ranges = None     # 2theta ranges to exclude when deriving the calibration factor, e.g. -20,0,40,50
     _live_pixels = None         # holds the list of cells that are not zero counting
+    _derivation_method = ''     # sequential reference (D20) or global reference (D2B)
+    _n_scan_files = None        # number of standard scan files for D2B (~30)
+    _n_scans_per_file = None    # number of scan points in a standard scan for D2B (=25)
+    _n_tubes = None             # number of tubes in D2B (=128)
+    _n_pixels_per_tube = None   # number of pixels per tube in D2B (=128)
+    _n_iterations = None        # number of iterations (=1); used for D2B
+    _pixels_to_trim = None      # number of pixels to trim from top and bottom of tubes for chi2 calculation (D2B)
 
     def _hide(self, name):
         return '__' + self._out_name + '_' + name
@@ -35,7 +73,8 @@ class PowderDiffILLDetEffCorr(PythonAlgorithm):
         return "ILL\\Diffraction;Diffraction\\Reduction;Diffraction\\Calibration"
 
     def summary(self):
-        return "Performs detector efficiency correction calculation for powder diffraction instrument D20 at ILL."
+        return "Performs detector efficiency correction calculation for scanning " \
+               "monochromatic powder diffraction instruments D20 and D2B at ILL."
 
     def seeAlso(self):
         return [ "ApplyDetectorScanEffCorr","PowderDiffILLReduction" ]
@@ -44,8 +83,8 @@ class PowderDiffILLDetEffCorr(PythonAlgorithm):
         return "PowderDiffILLDetEffCorr"
 
     def PyInit(self):
-        self.declareProperty(FileProperty('CalibrationRun', '', action=FileAction.Load, extensions=['nxs']),
-                             doc='File path of calibration run. Must be a detector scan.')
+        self.declareProperty(MultipleFileProperty('CalibrationRun', action=FileAction.Load, extensions=['nxs']),
+                             doc='File path of calibration runs (numors). Must be detector scans.')
 
         self.declareProperty(FileProperty('CalibrationFile', '', action=FileAction.OptionalLoad, extensions=['nxs']),
                              doc='Optional file containing previous calibration constants.')
@@ -53,28 +92,32 @@ class PowderDiffILLDetEffCorr(PythonAlgorithm):
         self.declareProperty(name='CalibrationMethod',
                              defaultValue='Median',
                              validator=StringListValidator(['Median', 'Mean', 'MostLikelyMean']),
-                             doc='The method of how the calibration constant of a '
-                                 'pixel relative to the neighbouring one is derived.')
+                             doc='The method of how the calibration constant of a pixel '
+                                 'is derived from the distribution of ratios.')
+
+        self.declareProperty(name='DerivationMethod', defaultValue='SequentialSummedReference1D',
+                             validator=StringListValidator(['SequentialSummedReference1D', 'GlobalSummedReference2D']),
+                             doc='Choose sequential for D20 (1D detector), global for D2B (2D detector).')
 
         self.declareProperty(name='InterpolateOverlappingAngles', defaultValue=False,
-                             doc='Wheter to interpolate 2theta values for overlapping regions between neighbouring cells.')
+                             doc='Whether to interpolate scattering angle values in overlapping regions (D20 only).')
 
         self.declareProperty(name='NormaliseTo',
                              defaultValue='None',
                              validator=StringListValidator(['None', 'Monitor', 'ROI']),
-                             doc='Normalise to time, monitor or ROI counts before deriving the calibration.')
+                             doc='Normalise to monitor or ROI counts before deriving the calibration.')
 
         thetaRangeValidator = FloatArrayOrderedPairsValidator()
 
         self.declareProperty(FloatArrayProperty(name='ROI', values=[0,100.], validator=thetaRangeValidator),
-                             doc='Regions of interest for normalisation [in scattering angle in degrees].')
+                             doc='Scattering angle regions of interest for normalisation [degrees].')
 
         normaliseToROI = VisibleWhenProperty('NormaliseTo', PropertyCriterion.IsEqualTo, 'ROI')
         self.setPropertySettings('ROI', normaliseToROI)
 
         self.declareProperty(FloatArrayProperty(name='ExcludedRange', values=[], validator=thetaRangeValidator),
-                             doc='2theta regions to exclude from the computation of relative calibration constants '
-                                 '[in scattering angle in degrees]. ')
+                             doc='Scattering angle regions to exclude from the computation of '
+                                 'relative calibration constants; for example, the beam stop [degrees]. ')
 
         pixelRangeValidator = CompositeValidator()
         greaterThanOne = IntArrayBoundedValidator()
@@ -87,19 +130,39 @@ class PowderDiffILLDetEffCorr(PythonAlgorithm):
         pixelRangeValidator.add(orderedPairsValidator)
 
         self.declareProperty(IntArrayProperty(name='PixelRange', values=[1,3072], validator=pixelRangeValidator),
-                             doc='Range of the pixel numbers to compute the calibration factors for. '
-                                 'For the other pixels outside the range, the factor will be set to 1.')
+                             doc='Range of the pixel numbers to compute the calibration factors for (D20 only); '
+                                 'for the other pixels outside the range, the factor will be set to 1.')
 
         self.declareProperty(MatrixWorkspaceProperty('OutputResponseWorkspace', '',
                                                      optional=PropertyMode.Optional, direction=Direction.Output),
-                             doc='Output workspace containing the summed diffraction patterns of all the pixels.')
+                             doc='Output workspace containing the summed diffraction patterns of all the overlapping pixels.')
 
         self.declareProperty(MatrixWorkspaceProperty('OutputWorkspace', '',
                                                      direction=Direction.Output),
-                             doc='Output workspace containing the detector efficiencies for each pixel.')
+                             doc='Output workspace containing the calibration constants (inverse of efficiency) for each pixel.')
+
+        self.declareProperty(name='NumberOfIterations',
+                             defaultValue=1,
+                             validator=IntBoundedValidator(lower=0, upper=10),
+                             doc='Number of iterations to perform (D2B only): 0 means auto; that is, the '
+                                 'iterations will terminate after reaching some Chi2/NdoF.')
 
     def validateInputs(self):
         issues = dict()
+
+        if self.getPropertyValue("DerivationMethod") == "GlobalSummedReference2D":
+            if self.getProperty("InterpolateOverlappingAngles").value:
+                issues["InterpolateOverlappingAngles"] = "Interpolation option is not supported for global method"
+            if self.getPropertyValue("NormaliseTo") == "ROI":
+                issues["NormaliseTo"] = "ROI normalisation is not supported for global method"
+            method = self.getPropertyValue("CalibrationMethod")
+            if method == "MostLikelyMean" or method == "Mean":
+                issues["CalibrationMethod"] = method + " is not supported for global reference method"
+
+        if self.getPropertyValue("DerivationMethod") == "SequentialSummedReference1D":
+            if self.getProperty("NumberOfIterations").value != 1:
+                issues["NumberOfIterations"] = "NumberOfIterations is not supported for sequential method"
+
         return issues
 
     def _update_reference(self, ws, cropped_ws, ref_ws, factor):
@@ -135,7 +198,8 @@ class PowderDiffILLDetEffCorr(PythonAlgorithm):
             @param ratio_ws : the name of the ratio workspace
         """
         ConvertToHistogram(InputWorkspace=ratio_ws, OutputWorkspace=ratio_ws)
-        x = mtd[ratio_ws].readX(0)
+        equator = int(mtd[ratio_ws].getNumberHistograms() / 2)
+        x = mtd[ratio_ws].readX(equator)
         xmin = x[0]
         xmax = x[-1]
         for excluded_range in self._excluded_ranges:
@@ -147,13 +211,15 @@ class PowderDiffILLDetEffCorr(PythonAlgorithm):
                 MaskBins(InputWorkspace=ratio_ws, OutputWorkspace=ratio_ws, XMin=xmin, XMax=xmax)
         ConvertToPointData(InputWorkspace=ratio_ws, OutputWorkspace=ratio_ws)
 
-    def _compute_relative_factor(self, ratio_ws):
+    def _compute_relative_factor_1D(self, ratio_ws):
         """
             Calculates the relative detector efficiency from the workspace containing response ratios.
             Implements mean, median and most likely mean methods.
             @param ratio_ws: input workspace containing response ratios
-            @returns: relative calibration factor
+            @returns: relative calibration factor (scalar)
         """
+        if len(self._excluded_ranges) != 0:
+            self._exclude_ranges(ratio_ws)
         ratios = mtd[ratio_ws].extractY()
         ratios = ratios[np.nonzero(ratios)]
         factor = 1.
@@ -166,6 +232,29 @@ class PowderDiffILLDetEffCorr(PythonAlgorithm):
                 factor = MostLikelyMean(ratios)
         return factor
 
+    def _compute_relative_factor_2D(self, ratio_ws, tube_index):
+        """
+            Calculates the relative detector efficiency from the workspace containing response ratios.
+            Implements mean, median and most likely mean methods.
+            @param ratio_ws: input workspace containing response ratios
+            @returns: relative calibration factor (1D array, factor per pixel in the tube)
+        """
+        if len(self._excluded_ranges) != 0:
+            self._exclude_ranges(ratio_ws)
+        ratios = mtd[ratio_ws].extractY()
+        if tube_index == 0:
+            ratios=ratios[:,0:-self._n_scans_per_file]
+        elif tube_index == self._n_tubes - 1:
+            ratios=ratios[:,self._n_scans_per_file:]
+        factors = np.ones(ratios.shape[0])
+        ratios = ma.masked_array(ratios, mask=[ratios == 0])
+        ratios = ma.masked_invalid(ratios)
+        if self._method == 'Median':
+            factors = np.array(ma.median(ratios, axis = 1))
+        elif self._method == 'Mean':
+            factors = np.array(ma.mean(ratios, axis = 1))
+        return factors
+
     def _validate_scan(self, scan_ws):
         """
             Ensures that the input workspace corresponds to a detector scan
@@ -178,7 +267,7 @@ class PowderDiffILLDetEffCorr(PythonAlgorithm):
         except RuntimeError:
             is_scanned = True
         if not is_scanned:
-            raise RuntimeError('The input run does not correspond to a detector scan.')
+            raise RuntimeError('The input run is not a detector scan.')
 
     def _reshape(self, raw_ws, ws_2d):
         """
@@ -199,13 +288,28 @@ class PowderDiffILLDetEffCorr(PythonAlgorithm):
         CreateWorkspace(DataX=x_2d, DataY=y_2d, DataE=e_2d, NSpec=self._n_det+1, OutputWorkspace=ws_2d)
         CopyLogs(InputWorkspace=raw_ws, OutputWorkspace=ws_2d)
 
+    def _chi_squared(self, calib_current):
+        """
+            Calculates the termination parameter for automatic iterations for global method (D2B)
+            @param calib_current : the residual calibration map at the current iteration
+            @return : chi2/NdoF
+        """
+        start = self._pixels_to_trim
+        end = self._n_pixels_per_tube - self._pixels_to_trim
+        y = mtd[calib_current].extractY()[:,start:end]
+        diff = (y-1)**2
+        chi2 = np.sum(diff)
+        ndof = (self._n_pixels_per_tube - 2 * self._pixels_to_trim) * self._n_tubes
+        return chi2/ndof
+
     def _set_input_properties(self):
         """
             Sets up the input properties of the algorithm
         """
-        self._input_file = self.getPropertyValue('CalibrationRun')
+        self._input_files = self.getPropertyValue('CalibrationRun')
         self._calib_file = self.getPropertyValue('CalibrationFile')
         self._method = self.getPropertyValue('CalibrationMethod')
+        self._derivation_method = self.getPropertyValue('DerivationMethod')
         self._normalise_to = self.getPropertyValue('NormaliseTo')
         self._regions_of_interest = self.getProperty('ROI').value
         self._excluded_ranges = self.getProperty('ExcludedRange').value
@@ -213,11 +317,12 @@ class PowderDiffILLDetEffCorr(PythonAlgorithm):
         self._pixel_range = self.getProperty('PixelRange').value
         self._out_response = self.getPropertyValue('OutputResponseWorkspace')
         self._out_name = self.getPropertyValue('OutputWorkspace')
+        self._n_iterations = self.getProperty('NumberOfIterations').value
 
-    def _configure(self, raw_ws):
+    def _configure_sequential(self, raw_ws):
         """
-            Configures the fundamental parameters for the algorithm.
-            @param : the name of the raw detector scan workspace
+            Configures the calibration with SequentialSummedReference1D method (D20)
+            @param : the name of the raw detector scan (merged) workspace
         """
         self._scan_points = mtd[raw_ws].getRun().getLogData('ScanSteps').value
         self.log().information('Number of scan steps is: ' + str(self._scan_points))
@@ -239,11 +344,26 @@ class PowderDiffILLDetEffCorr(PythonAlgorithm):
             n_excluded_ranges = int(len(self._excluded_ranges) / 2)
             self._excluded_ranges = np.split(self._excluded_ranges, n_excluded_ranges)
 
+    def _configure_global(self, raw_ws):
+        """
+            Configures the calibration with GlobalSummedReference2D method (D2B)
+            @param : first raw ws name in the list
+        """
+        inst = mtd[raw_ws].getInstrument()
+        self._n_tubes = inst.getComponentByName('detectors').nelements()
+        self._n_pixels_per_tube = inst.getComponentByName('detectors/tube_1').nelements()
+        self._n_scans_per_file = mtd[raw_ws].getRun().getLogData('ScanSteps').value
+        self._scan_points = self._n_scans_per_file * self._n_scan_files
+        if self._excluded_ranges.any():
+            n_excluded_ranges = int(len(self._excluded_ranges) / 2)
+            self._excluded_ranges = np.split(self._excluded_ranges, n_excluded_ranges)
+
     def _validate_roi(self, ws_2d):
         """
             ROI has to be fully within the aperture of the detector at any time index.
             Example:
             time index : detector span (degrees)
+            ------------------------------------
             first      : -30 -> 120
             last       :  10 -> 160
             ROI can not be wider than [10,120]
@@ -327,9 +447,10 @@ class PowderDiffILLDetEffCorr(PythonAlgorithm):
                 mtd[constants_ws].dataY(pixel)[0] = 1.
                 mtd[constants_ws].dataE(pixel)[0] = 0.
 
-    def _derive_calibration(self, ws_2d, constants_ws, response_ws):
+    def _derive_calibration_sequential(self, ws_2d, constants_ws, response_ws):
         """
             Computes the relative calibration factors sequentailly for all the pixels.
+            This is the main calculation, the performance is critical
             @param : 2D input workspace
             @param : the output workspace name containing the calibration constants
             @param : the output workspace name containing the combined response
@@ -377,9 +498,7 @@ class PowderDiffILLDetEffCorr(PythonAlgorithm):
                     RenameWorkspace(InputWorkspace=cloned_ref_ws, OutputWorkspace=cropped_ws)
 
                 Divide(LHSWorkspace=ref_ws, RHSWorkspace=cropped_ws, OutputWorkspace=ratio_ws, EnableLogging=False)
-                if len(self._excluded_ranges) != 0:
-                    self._exclude_ranges(ratio_ws)
-                factor = self._compute_relative_factor(ratio_ws)
+                factor = self._compute_relative_factor_1D(ratio_ws)
                 DeleteWorkspace(ratio_ws)
 
                 if str(factor) == 'nan' or str(factor) == 'inf' or factor == 0.:
@@ -410,31 +529,32 @@ class PowderDiffILLDetEffCorr(PythonAlgorithm):
         # end of loop over pixels
         DeleteWorkspace(ref_ws)
 
-    def PyExec(self):
-        self._set_input_properties()
-
+    def _process_sequential(self):
+        """
+            Performs the sequential derivation for D20 with the following logic:
+            1. Take first cell as the reference
+            2. Compute the coefficient for the second cell wrt reference
+            3. Scale the response of the second cell with the obtained factor
+            4. Merge the responses of first and second cells and set it as the new reference
+            5. Back to Step 2 for the third pixel and so on...
+        """
         raw_ws = self._hide('raw')
         mon_ws = self._hide('mon')
         ws_2d = self._hide('2d')
         response_ws = self._hide('resp')
         constants_ws = self._hide('constants')
-
-        LoadILLDiffraction(Filename=self._input_file, OutputWorkspace=raw_ws)
-        self._validate_scan(raw_ws)
-        self._configure(raw_ws)
+        calib_ws = self._hide('calib')
 
         ConvertSpectrumAxis(InputWorkspace=raw_ws, OutputWorkspace=raw_ws, Target='SignedTheta', OrderAxis=False)
         self._reshape(raw_ws, ws_2d)
         DeleteWorkspace(raw_ws)
         # extract the monitor spectrum
         ExtractSingleSpectrum(InputWorkspace=ws_2d, WorkspaceIndex=0, OutputWorkspace=mon_ws)
-
         if self._normalise_to == 'Monitor':
             Divide(LHSWorkspace=ws_2d, RHSWorkspace=mon_ws, OutputWorkspace=ws_2d)
         elif self._normalise_to == 'ROI':
             self._validate_roi(ws_2d)
             self._normalise_roi(ws_2d)
-
         DeleteWorkspace(mon_ws)
         # only now crop out the monitor spectrum
         CropWorkspace(InputWorkspace=ws_2d, StartWorkspaceIndex=1, OutputWorkspace=ws_2d)
@@ -442,8 +562,6 @@ class PowderDiffILLDetEffCorr(PythonAlgorithm):
                              NaNValue=0, NaNError=0, InfinityValue=0, InfinityError=0)
 
         if self._calib_file:
-            calib_ws = self._hide('calib')
-            LoadNexusProcessed(Filename=self._calib_file, OutputWorkspace=calib_ws)
             Multiply(LHSWorkspace=ws_2d, RHSWorkspace=calib_ws, OutputWorkspace=ws_2d)
             DeleteWorkspace(calib_ws)
 
@@ -451,9 +569,187 @@ class PowderDiffILLDetEffCorr(PythonAlgorithm):
             self._prepare_response_workspace(ws_2d, response_ws)
 
         # this is the main calculation
-        self._derive_calibration(ws_2d, constants_ws, response_ws)
+        self._derive_calibration_sequential(ws_2d, constants_ws, response_ws)
         DeleteWorkspace(ws_2d)
         self._perform_absolute_normalisation(constants_ws)
+        mtd[constants_ws].getAxis(1).setUnit('Label').setLabel('Cell #', '')
+        mtd[constants_ws].setYUnitLabel('Calibration constant')
+
+    def _process_global(self):
+        """
+            Performs the global derivation for D2B following the logic:
+            1. SumOverlappingTubes with 2D option to obtain the reference
+            2. Loop over tubes, make ratios wrt reference, obtain constants
+            3. Apply the constants, and iterate over if requested
+        """
+        constants_ws = self._hide('constants')
+        response_ws = self._hide('resp')
+        calib_ws = self._hide('calib')
+        ref_ws = self._hide('ref')
+        numors = []
+        self._progress = Progress(self, start=0.0, end=1.0, nreports=self._n_scan_files)
+
+        for index, numor in enumerate(self._input_files.split(',')):
+            self._progress.report('Pre-processing detector scan '+numor[-9:-3])
+            ws_name = '__raw_'+str(index)
+            numors.append(ws_name)
+            LoadILLDiffraction(Filename=numor, OutputWorkspace=ws_name, DataType="Raw")
+            self._validate_scan(ws_name)
+            if index == 0:
+                if mtd[ws_name].getInstrument().getName() != 'D2B':
+                    raise RuntimeError('Global reference method is not supported for the instrument given')
+                self._configure_global(ws_name)
+            if self._normalise_to == 'Monitor':
+                NormaliseToMonitor(InputWorkspace=ws_name, OutputWorkspace=ws_name, MonitorID=0)
+            ExtractMonitors(InputWorkspace=ws_name, DetectorWorkspace=ws_name)
+            ConvertSpectrumAxis(InputWorkspace=ws_name, OrderAxis=False, Target="SignedTheta", OutputWorkspace=ws_name)
+            if self._calib_file:
+                ApplyDetectorScanEffCorr(InputWorkspace=ws_name, DetectorEfficiencyWorkspace=calib_ws, OutputWorkspace=ws_name)
+
+        if self._calib_file:
+            DeleteWorkspace(calib_ws)
+
+        constants = np.ones([self._n_pixels_per_tube,self._n_tubes])
+        x = np.arange(self._n_tubes)
+        e = np.zeros([self._n_pixels_per_tube,self._n_tubes])
+        CreateWorkspace(DataX=np.tile(x, self._n_pixels_per_tube), DataY=constants, DataE=e,
+                        NSpec=self._n_pixels_per_tube, OutputWorkspace=constants_ws)
+        calib_current = self._hide('current')
+        CloneWorkspace(InputWorkspace=constants_ws, OutputWorkspace=calib_current)
+
+        iteration = 0
+        chi2_ndof = np.inf # set a large number to start with
+        self._pixels_to_trim = 28
+        chi2_ndof_threshold = 1.
+        inst = mtd[numors[0]].getInstrument()
+        if inst.hasParameter('pixels_to_trim'):
+            self._pixels_to_trim = inst.getIntParameter('pixels_to_trim')[0]
+        if inst.hasParameter('chi2_ndof'):
+            chi2_ndof_threshold = inst.getNumberParameter('chi2_ndof')[0]
+
+        while iteration < self._n_iterations or (self._n_iterations == 0 and chi2_ndof > chi2_ndof_threshold):
+            self._progress = Progress(self, start=0.0, end=1.0, nreports=5)
+            self._progress.report('Starting iteration #'+str(iteration))
+            self._derive_calibration_global(numors)
+            Multiply(LHSWorkspace=constants_ws, RHSWorkspace=calib_current, OutputWorkspace=constants_ws)
+            chi2_ndof = self._chi_squared(calib_current)
+            if iteration != 0:
+                self.log().warning('Iteration {0}: Chi2/NdoF={1} (termination criterion: < {2})'.
+                                   format(iteration, chi2_ndof, chi2_ndof_threshold))
+            iteration += 1
+
+        if self._out_response:
+            for index in range(self._n_scan_files):
+                ws_name = '__raw_'+str(index)
+                ApplyDetectorScanEffCorr(InputWorkspace=ws_name, DetectorEfficiencyWorkspace=calib_current, OutputWorkspace=ws_name)
+            SumOverlappingTubes(InputWorkspaces=numors, OutputWorkspace=response_ws, MirrorScatteringAngles=False,
+                                CropNegativeScatteringAngles=False, Normalise=True, OutputType="2DTubes", ScatteringAngleTolerance=1000)
+
+        DeleteWorkspace(ref_ws)
+        DeleteWorkspaces(numors)
+        DeleteWorkspace(calib_current)
+        mtd[constants_ws].getAxis(0).setUnit('Label').setLabel('Tube #', '')
+        mtd[constants_ws].getAxis(1).setUnit('Label').setLabel('Pixel #', '')
+        mtd[constants_ws].setYUnitLabel('Calibration constant')
+
+    def _derive_calibration_global(self, numors):
+        """
+            Derives one iteration of calibration with the global reference method (D2B)
+            This is the main calculation, so the performance is critical
+            @param numors : list of workspace names
+        """
+        y_tubes = []
+        x_tubes = []
+        e_tubes = []
+        calib_current = self._hide('current')
+        ref_ws = self._hide('ref')
+        tubes_group = self._hide('tubes')
+        ratios_group = self._hide('ratios')
+        shape = [self._n_tubes, self._n_pixels_per_tube, self._n_scans_per_file]
+        for i in range(self._n_tubes):
+            y_tubes.append([])
+            x_tubes.append([])
+            e_tubes.append([])
+
+        for index in range(self._n_scan_files):
+            ws_name = '__raw_'+str(index)
+            ApplyDetectorScanEffCorr(InputWorkspace=ws_name, DetectorEfficiencyWorkspace=calib_current, OutputWorkspace=ws_name)
+            y = mtd[ws_name].extractY()
+            e = mtd[ws_name].extractE()
+            x = mtd[ws_name].getAxis(1).extractValues()
+            y_3d = np.reshape(y, shape)
+            x_3d = np.reshape(x, shape)
+            e_3d = np.reshape(e, shape)
+            for tube in range(self._n_tubes):
+                y_tubes[tube].append(y_3d[tube,:,:])
+                x_tubes[tube].append(x_3d[tube,:,:])
+                e_tubes[tube].append(e_3d[tube,:,:])
+
+        self._progress.report('Constructing the global reference')
+        SumOverlappingTubes(InputWorkspaces=numors, OutputWorkspace=ref_ws, MirrorScatteringAngles=False,
+                            CropNegativeScatteringAngles=False, Normalise=True, OutputType="2DTubes", ScatteringAngleTolerance=1000)
+
+        to_group = []
+        self._progress.report('Preparing the tube responses')
+        for tube in range(self._n_tubes):
+            y_tube = np.concatenate(y_tubes[tube], axis=1)
+            x_tube = np.concatenate(x_tubes[tube], axis=1)
+            e_tube = np.concatenate(e_tubes[tube], axis=1)
+            ws_name = "__tube" + str(tube)
+            CreateWorkspace(DataX=x_tube, DataY=y_tube, DataE=e_tube, NSpec=self._n_pixels_per_tube, OutputWorkspace=ws_name)
+            SortXAxis(InputWorkspace=ws_name, OutputWorkspace=ws_name)
+            to_group.append(ws_name)
+        GroupWorkspaces(InputWorkspaces=to_group, OutputWorkspace=tubes_group)
+
+        ratios = []
+        self._progress.report('Constructing response ratios')
+        for tube in reversed(range(self._n_tubes)):
+            itube = self._n_tubes - tube - 1
+            ratio_ws = '__ratio'+str(tube)
+            _crop_bins(ref_ws, itube * self._n_scans_per_file, itube * self._n_scans_per_file + self._scan_points, '__cropped_ref')
+            _divide_friendly('__cropped_ref', '__tube'+str(tube), ratio_ws)
+            ratios.append(ratio_ws)
+            DeleteWorkspace('__cropped_ref')
+        GroupWorkspaces(InputWorkspaces=ratios, OutputWorkspace=ratios_group)
+        DeleteWorkspace(tubes_group)
+
+        self._progress.report('Computing the calibration constants')
+        Transpose(InputWorkspace=calib_current, OutputWorkspace=calib_current)
+        for tube in range(self._n_tubes):
+            coeff = self._compute_relative_factor_2D('__ratio'+str(tube), tube)
+            mtd[calib_current].setY(tube, coeff)
+        Transpose(InputWorkspace=calib_current, OutputWorkspace=calib_current)
+        DeleteWorkspace(ratios_group)
+
+        ReplaceSpecialValues(InputWorkspace=calib_current, OutputWorkspace=calib_current,
+                             NaNValue=1, InfinityValue=1, SmallNumberThreshold=0.00001, SmallNumberValue=1)
+
+    def PyExec(self):
+        self._set_input_properties()
+
+        raw_ws = self._hide('raw')
+        response_ws = self._hide('resp')
+        constants_ws = self._hide('constants')
+        calib_ws = self._hide('calib')
+
+        if self._calib_file:
+            LoadNexusProcessed(Filename=self._calib_file, OutputWorkspace=calib_ws)
+
+        if self._derivation_method == 'SequentialSummedReference1D': # D20
+            self._input_files = self._input_files.replace(',','+')
+            LoadAndMerge(Filename=self._input_files, OutputWorkspace=raw_ws, LoaderName='LoadILLDiffraction')
+            if not mtd[raw_ws].getInstrument().getName().startswith('D20'):
+                DeleteWorkspace(raw_ws)
+                raise RuntimeError('Sequential reference method is not supported for the instrument given')
+            self._validate_scan(raw_ws)
+            self._configure_sequential(raw_ws)
+            self._process_sequential()
+        elif self._derivation_method == 'GlobalSummedReference2D': # D2B
+            self._input_files = self._input_files.replace('+',',')
+            self._n_scan_files = self._input_files.count(',') + 1
+            if self._n_scan_files < 2:
+                raise RuntimeError('At least two overlapping scan files needed for the global method')
+            self._process_global()
 
         # set output workspace[s]
         RenameWorkspace(InputWorkspace=constants_ws, OutputWorkspace=self._out_name)
diff --git a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/PowderDiffILLDetScanReduction.py b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/PowderDiffILLDetScanReduction.py
index 3f9e6a8b6051b4e5889e7bd5a4b1245cc86195a6..88e50537d399b7223691c349e19f2f8407ef1a39 100644
--- a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/PowderDiffILLDetScanReduction.py
+++ b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/PowderDiffILLDetScanReduction.py
@@ -2,12 +2,13 @@ from __future__ import (absolute_import, division, print_function)
 
 from mantid.kernel import CompositeValidator, Direction, FloatArrayLengthValidator, FloatArrayOrderedPairsValidator, \
     FloatArrayProperty, StringListValidator
-from mantid.api import DataProcessorAlgorithm, MultipleFileProperty, Progress, WorkspaceGroupProperty
+from mantid.api import DataProcessorAlgorithm, MultipleFileProperty, Progress, WorkspaceGroupProperty, FileProperty, FileAction
 from mantid.simpleapi import *
 
 
 class PowderDiffILLDetScanReduction(DataProcessorAlgorithm):
     _progress = None
+    _tolerance = 0.
 
     def category(self):
         return "ILL\\Diffraction;Diffraction\\Reduction"
@@ -43,6 +44,9 @@ class PowderDiffILLDetScanReduction(DataProcessorAlgorithm):
                              validator=StringListValidator(['None', 'Monitor']),
                              doc='Normalise to monitor, or skip normalisation.')
 
+        self.declareProperty(FileProperty('CalibrationFile', '', action=FileAction.OptionalLoad, extensions=['nxs']),
+                             doc='File containing the detector efficiencies.')
+
         self.declareProperty(name='UseCalibratedData',
                              defaultValue=True,
                              doc='Whether or not to use the calibrated data in the NeXus files.')
@@ -59,6 +63,9 @@ class PowderDiffILLDetScanReduction(DataProcessorAlgorithm):
                              defaultValue=True,
                              doc='Output a 1D workspace with counts against scattering angle.')
 
+        self.declareProperty(name='CropNegativeScatteringAngles', defaultValue=True,
+                             doc='Whether or not to crop the negative scattering angles.')
+
         self.declareProperty(FloatArrayProperty(name='HeightRange', values=[],
                                                 validator=CompositeValidator([FloatArrayOrderedPairsValidator(),
                                                                               FloatArrayLengthValidator(0, 2)])),
@@ -83,16 +90,32 @@ class PowderDiffILLDetScanReduction(DataProcessorAlgorithm):
         # We might already have a group, but group just in case
         input_group = GroupWorkspaces(InputWorkspaces=input_workspace)
 
-        instrument_name = input_group[0].getInstrument().getName()
+        instrument = input_group[0].getInstrument()
         supported_instruments = ['D2B', 'D20']
-        if instrument_name not in supported_instruments:
+        if instrument.getName() not in supported_instruments:
             self.log.warning('Running for unsupported instrument, use with caution. Supported instruments are: '
                              + str(supported_instruments))
+        if instrument.getName() == 'D20':
+            if self.getProperty('Output2DTubes').value:
+                raise RuntimeError('Output2DTubes is not supported for D20 (1D detector)')
+            if self.getProperty('Output2D').value:
+                raise RuntimeError('Output2D is not supported for D20 (1D detector)')
+            self._tolerance = 1000.
 
         self._progress.report('Normalising to monitor')
         if self.getPropertyValue('NormaliseTo') == 'Monitor':
             input_group = NormaliseToMonitor(InputWorkspace=input_group, MonitorID=0)
 
+        calib_file = self.getPropertyValue('CalibrationFile')
+        if calib_file:
+            self._progress.report('Applying detector efficiencies')
+            LoadNexusProcessed(Filename=calib_file, OutputWorkspace='__det_eff')
+            for ws in input_group:
+                name = ws.getName()
+                ExtractMonitors(InputWorkspace=name, DetectorWorkspace=name, MonitorWorkspace='__mon')
+                DeleteWorkspace('__mon')
+                ApplyDetectorScanEffCorr(InputWorkspace=name,DetectorEfficiencyWorkspace='__det_eff',OutputWorkspace=name)
+
         height_range = ''
         height_range_prop = self.getProperty('HeightRange').value
         if (len(height_range_prop) == 2):
@@ -101,11 +124,18 @@ class PowderDiffILLDetScanReduction(DataProcessorAlgorithm):
         output_workspaces = []
         output_workspace_name = self.getPropertyValue('OutputWorkspace')
 
+        mirror_angles = False
+        crop_negative = self.getProperty('CropNegativeScatteringAngles').value
+        if instrument.hasParameter("mirror_scattering_angles"):
+            mirror_angles = instrument.getBoolParameter("mirror_scattering_angles")[0]
+
         self._progress.report('Doing Output2DTubes Option')
         if self.getProperty('Output2DTubes').value:
             output2DTubes = SumOverlappingTubes(InputWorkspaces=input_group,
                                                 OutputType='2DTubes',
                                                 HeightAxis=height_range,
+                                                MirrorScatteringAngles=mirror_angles,
+                                                CropNegativeScatteringAngles=crop_negative,
                                                 OutputWorkspace=output_workspace_name + '_2DTubes')
             output_workspaces.append(output2DTubes)
 
@@ -113,8 +143,9 @@ class PowderDiffILLDetScanReduction(DataProcessorAlgorithm):
         if self.getProperty('Output2D').value:
             output2D = SumOverlappingTubes(InputWorkspaces=input_group,
                                            OutputType='2D',
-                                           CropNegativeScatteringAngles=True,
                                            HeightAxis=height_range,
+                                           MirrorScatteringAngles=mirror_angles,
+                                           CropNegativeScatteringAngles=crop_negative,
                                            OutputWorkspace = output_workspace_name + '_2D')
             output_workspaces.append(output2D)
 
@@ -122,9 +153,11 @@ class PowderDiffILLDetScanReduction(DataProcessorAlgorithm):
         if self.getProperty('Output1D').value:
             output1D = SumOverlappingTubes(InputWorkspaces=input_group,
                                            OutputType='1D',
-                                           CropNegativeScatteringAngles=True,
                                            HeightAxis=height_range,
-                                           OutputWorkspace=output_workspace_name + '_1D')
+                                           MirrorScatteringAngles=mirror_angles,
+                                           CropNegativeScatteringAngles=crop_negative,
+                                           OutputWorkspace=output_workspace_name + '_1D',
+                                           ScatteringAngleTolerance=self._tolerance)
             output_workspaces.append(output1D)
         DeleteWorkspace('input_group')
 
diff --git a/Framework/PythonInterface/test/python/plugins/algorithms/ApplyDetectorScanEffCorrTest.py b/Framework/PythonInterface/test/python/plugins/algorithms/ApplyDetectorScanEffCorrTest.py
index ac25a60f6a4c9eb8703f428df6c51267c3674973..0a13784999a62f419f6f9925c02b0d08f983f77a 100644
--- a/Framework/PythonInterface/test/python/plugins/algorithms/ApplyDetectorScanEffCorrTest.py
+++ b/Framework/PythonInterface/test/python/plugins/algorithms/ApplyDetectorScanEffCorrTest.py
@@ -2,26 +2,25 @@ from __future__ import (absolute_import, division, print_function)
 
 import unittest
 import numpy as np
-from mantid.simpleapi import ApplyDetectorScanEffCorr, CreateWorkspace, CreateSampleWorkspace
+from mantid.simpleapi import ApplyDetectorScanEffCorr, CreateWorkspace, CreateSampleWorkspace, Transpose
 
 
 class ApplyDetectorScanEffCorrTest(unittest.TestCase):
     def test_non_scanning_case(self):
-        input_ws = CreateSampleWorkspace(NumMonitors=1, NumBanks=6, BankPixelWidth=1, XMin=0, XMax=1, BinWidth=1)
+        input_ws = CreateSampleWorkspace(NumMonitors=0, NumBanks=6, BankPixelWidth=1, XMin=0, XMax=1, BinWidth=1)
 
         calibration_x = np.array([0, 0, 0, 0, 0, 0])
         calibration_y = np.array([1.0, 2.0, 3.0, 4.0, 5.0, 6.0])
 
-        # Note the monitors are in the wrong place doing the test workspace creation like this - but it does not affect the test.
         calibration_ws = CreateWorkspace(DataX=calibration_x, DataY=calibration_y, Nspec=calibration_y.size)
 
         calibrated_ws = ApplyDetectorScanEffCorr(input_ws, calibration_ws)
-        for i in range(1, 7):
-            self.assertEquals(calibrated_ws.readY(i), input_ws.readY(i) * i)
-            self.assertEquals(calibrated_ws.readE(i), input_ws.readE(i) * i)
+        for i in range(6):
+            self.assertEquals(calibrated_ws.readY(i), input_ws.readY(i) * (i+1))
+            self.assertEquals(calibrated_ws.readE(i), input_ws.readE(i) * (i+1))
 
     def test_simple_scanning_case(self):
-        input_ws = CreateSampleWorkspace(NumMonitors=1, NumBanks=6, BankPixelWidth=1, XMin=0, XMax=1, BinWidth=1, NumScanPoints=2)
+        input_ws = CreateSampleWorkspace(NumMonitors=0, NumBanks=6, BankPixelWidth=1, XMin=0, XMax=1, BinWidth=1, NumScanPoints=2)
 
         calibration_x = np.array([0, 0, 0, 0, 0, 0])
         calibration_y = np.array([1.0, 2.0, 3.0, 4.0, 5.0, 6.0])
@@ -29,13 +28,14 @@ class ApplyDetectorScanEffCorrTest(unittest.TestCase):
         # Note the monitors are in the wrong place doing the test workspace creation like this - but it does not affect the test.
         calibration_ws = CreateWorkspace(DataX=calibration_x, DataY=calibration_y, Nspec=calibration_y.size)
 
+        expected = np.repeat(calibration_y, 2)
         calibrated_ws = ApplyDetectorScanEffCorr(input_ws, calibration_ws)
-        for i in range(2, 14):
-            self.assertEquals(calibrated_ws.readY(i), input_ws.readY(i) * (i//2))
-            self.assertEquals(calibrated_ws.readE(i), input_ws.readE(i) * (i//2))
+        for i in range(12):
+            self.assertEquals(calibrated_ws.readY(i), input_ws.readY(i) * expected[i])
+            self.assertEquals(calibrated_ws.readE(i), input_ws.readE(i) * expected[i])
 
     def test_mismatched_workspace_size(self):
-        input_ws = CreateSampleWorkspace(NumMonitors=1, NumBanks=6, BankPixelWidth=1, XMin=0, XMax=1, BinWidth=1)
+        input_ws = CreateSampleWorkspace(NumMonitors=0, NumBanks=6, BankPixelWidth=1, XMin=0, XMax=1, BinWidth=1)
 
         calibration_x = np.array([0, 0, 0, 0, 0, 0])
         calibration_y = np.array([1.0, 2.0, 3.0, 4.0, 5.0, 6.0])
@@ -44,5 +44,22 @@ class ApplyDetectorScanEffCorrTest(unittest.TestCase):
         self.assertRaises(ValueError, ApplyDetectorScanEffCorr, InputWorkspace=input_ws,
                           DetectorEfficiencyWorkspace=calibration_ws, OutputWorkspace='')
 
+    def test_2d_scanning_workspace(self):
+        input_ws = CreateSampleWorkspace(NumMonitors=0, NumBanks=3, BankPixelWidth=2, XMin=0, XMax=5, BinWidth=1, NumScanPoints=7)
+
+        calibration_x = np.array([0,1,2,0,1,2,0,1,2,0,1,2])
+        calibration_y = np.arange(12)
+        calibration_ws = CreateWorkspace(DataX=calibration_x, DataY=calibration_y, Nspec=4)
+        calibrated_ws = ApplyDetectorScanEffCorr(input_ws, calibration_ws)
+
+        tmp = Transpose(calibration_ws)
+        tmp = tmp.extractY().flatten()
+        to_multiply = np.repeat(tmp, 5*7)
+        to_multiply = np.reshape(to_multiply, [7*12,5])
+
+        for det in range(7*12):
+            for bin in range(5):
+                self.assertEquals(calibrated_ws.readY(det)[bin], input_ws.readY(det)[bin] * to_multiply[det][bin])
+
 if __name__ == "__main__":
     unittest.main()
diff --git a/Framework/TestHelpers/inc/MantidTestHelpers/WorkspaceCreationHelper.h b/Framework/TestHelpers/inc/MantidTestHelpers/WorkspaceCreationHelper.h
index b49022e4ae06281ec0d877d17aa5b09efc022b14..516e70ebfc2fcfa29f0c5b67db71fe0bb47336cb 100644
--- a/Framework/TestHelpers/inc/MantidTestHelpers/WorkspaceCreationHelper.h
+++ b/Framework/TestHelpers/inc/MantidTestHelpers/WorkspaceCreationHelper.h
@@ -135,10 +135,12 @@ Mantid::DataObjects::Workspace2D_sptr
 create2DWorkspaceWhereYIsWorkspaceIndex(int nhist, int numBoundaries);
 Mantid::DataObjects::Workspace2D_sptr create2DWorkspace123(
     int64_t nHist, int64_t nBins, bool isHist = false,
-    const std::set<int64_t> &maskedWorkspaceIndices = std::set<int64_t>());
+    const std::set<int64_t> &maskedWorkspaceIndices = std::set<int64_t>(),
+    bool hasDx = false);
 Mantid::DataObjects::Workspace2D_sptr create2DWorkspace154(
     int64_t nHist, int64_t nBins, bool isHist = false,
-    const std::set<int64_t> &maskedWorkspaceIndices = std::set<int64_t>());
+    const std::set<int64_t> &maskedWorkspaceIndices = std::set<int64_t>(),
+    bool hasDx = false);
 Mantid::DataObjects::Workspace2D_sptr create2DWorkspaceWithValuesAndXerror(
     int64_t nHist, int64_t nBins, bool isHist, double xVal, double yVal,
     double eVal, double dxVal,
@@ -164,8 +166,9 @@ create2DWorkspaceBinned(int nhist, int numVals, double x0 = 0.0,
  * Filled with Y = 2.0 and E = sqrt(2.0)w
  */
 Mantid::DataObjects::Workspace2D_sptr
-create2DWorkspaceBinned(int nhist, const int numBoundaries,
-                        const double xBoundaries[]);
+create2DWorkspaceNonUniformlyBinned(int nhist, const int numBoundaries,
+                                    const double xBoundaries[],
+                                    bool hasDx = false);
 
 struct returnOne {
   double operator()(const double, size_t) { return 1; }
@@ -228,7 +231,8 @@ void addNoise(Mantid::API::MatrixWorkspace_sptr ws, double noise,
 Mantid::DataObjects::Workspace2D_sptr create2DWorkspaceWithFullInstrument(
     int nhist, int nbins, bool includeMonitors = false,
     bool startYNegative = false, bool isHistogram = true,
-    const std::string &instrumentName = std::string("testInst"));
+    const std::string &instrumentName = std::string("testInst"),
+    bool hasDx = false);
 
 /**
  * Create a workspace as for create2DWorkspaceWithFullInstrument, but including
diff --git a/Framework/TestHelpers/src/WorkspaceCreationHelper.cpp b/Framework/TestHelpers/src/WorkspaceCreationHelper.cpp
index efa55cce50024e36445b7742e0e7b1f4f53c7b6e..bd6dbfe1d9601af31ad07a0bb3f1eb5da57d779b 100644
--- a/Framework/TestHelpers/src/WorkspaceCreationHelper.cpp
+++ b/Framework/TestHelpers/src/WorkspaceCreationHelper.cpp
@@ -30,12 +30,14 @@
 #include "MantidGeometry/Instrument/ReferenceFrame.h"
 #include "MantidGeometry/Objects/ShapeFactory.h"
 #include "MantidHistogramData/LinearGenerator.h"
+#include "MantidHistogramData/HistogramDx.h"
 #include "MantidIndexing/IndexInfo.h"
 #include "MantidKernel/MersenneTwister.h"
 #include "MantidKernel/OptionalBool.h"
 #include "MantidKernel/TimeSeriesProperty.h"
 #include "MantidKernel/UnitFactory.h"
 #include "MantidKernel/VectorHelper.h"
+#include "MantidKernel/make_cow.h"
 #include "MantidKernel/make_unique.h"
 #include "MantidKernel/V3D.h"
 
@@ -195,20 +197,36 @@ Workspace2D_sptr create2DWorkspaceThetaVsTOF(int nHist, int nBins) {
   return outputWS;
 }
 
+/**
+ * @brief create2DWorkspaceWithValues
+ * @param nHist :: Number of spectra
+ * @param nBins :: Number of points (not bin edges!)
+ * @param isHist :: Flag if it is a histogram or point data
+ * @param maskedWorkspaceIndices :: Mask workspace indices
+ * @param xVal :: bin edge or point
+ * @param yVal :: y value
+ * @param eVal :: error values
+ * @param hasDx :: wether workspace has dx values defined (default is false)
+ * @return A workspace filled with nBins bins or points and nHist spectra of the
+ * values yVal and the error eVal as well as Dx values which are copies of the y
+ * values
+ */
 Workspace2D_sptr
 create2DWorkspaceWithValues(int64_t nHist, int64_t nBins, bool isHist,
                             const std::set<int64_t> &maskedWorkspaceIndices,
-                            double xVal, double yVal, double eVal) {
+                            double xVal, double yVal, double eVal,
+                            bool hasDx = false) {
   auto x1 = Kernel::make_cow<HistogramData::HistogramX>(
       isHist ? nBins + 1 : nBins, LinearGenerator(xVal, 1.0));
   Counts y1(nBins, yVal);
   CountStandardDeviations e1(nBins, eVal);
+  auto dx = Kernel::make_cow<HistogramData::HistogramDx>(nBins, yVal);
   auto retVal = boost::make_shared<Workspace2D>();
-  retVal->initialize(nHist, isHist ? nBins + 1 : nBins, nBins);
+  retVal->initialize(nHist, createHisto(isHist, y1, e1));
   for (int i = 0; i < nHist; i++) {
-    retVal->setX(i, x1);
-    retVal->setCounts(i, y1);
-    retVal->setCountStandardDeviations(i, e1);
+    retVal->setSharedX(i, x1);
+    if (hasDx)
+      retVal->setSharedDx(i, dx);
     retVal->getSpectrum(i).setDetectorID(i);
     retVal->getSpectrum(i).setSpectrumNo(i);
   }
@@ -231,16 +249,18 @@ Workspace2D_sptr create2DWorkspaceWithValuesAndXerror(
 
 Workspace2D_sptr
 create2DWorkspace123(int64_t nHist, int64_t nBins, bool isHist,
-                     const std::set<int64_t> &maskedWorkspaceIndices) {
-  return create2DWorkspaceWithValues(nHist, nBins, isHist,
-                                     maskedWorkspaceIndices, 1.0, 2.0, 3.0);
+                     const std::set<int64_t> &maskedWorkspaceIndices,
+                     bool hasDx) {
+  return create2DWorkspaceWithValues(
+      nHist, nBins, isHist, maskedWorkspaceIndices, 1.0, 2.0, 3.0, hasDx);
 }
 
 Workspace2D_sptr
 create2DWorkspace154(int64_t nHist, int64_t nBins, bool isHist,
-                     const std::set<int64_t> &maskedWorkspaceIndices) {
-  return create2DWorkspaceWithValues(nHist, nBins, isHist,
-                                     maskedWorkspaceIndices, 1.0, 5.0, 4.0);
+                     const std::set<int64_t> &maskedWorkspaceIndices,
+                     bool hasDx) {
+  return create2DWorkspaceWithValues(
+      nHist, nBins, isHist, maskedWorkspaceIndices, 1.0, 5.0, 4.0, hasDx);
 }
 
 Workspace2D_sptr maskSpectra(Workspace2D_sptr workspace,
@@ -303,31 +323,34 @@ Workspace2D_sptr create2DWorkspaceBinned(int nhist, int numVals, double x0,
   Counts y(numVals, 2);
   CountStandardDeviations e(numVals, M_SQRT2);
   auto retVal = boost::make_shared<Workspace2D>();
-  retVal->initialize(nhist, numVals + 1, numVals);
-  for (int i = 0; i < nhist; i++) {
+  retVal->initialize(nhist, createHisto(true, y, e));
+  for (int i = 0; i < nhist; i++)
     retVal->setBinEdges(i, x);
-    retVal->setCounts(i, y);
-    retVal->setCountStandardDeviations(i, e);
-  }
   return retVal;
 }
 
 /** Create a 2D workspace with this many histograms and bins. The bins are
  * assumed to be non-uniform and given by the input array
  * Filled with Y = 2.0 and E = M_SQRT2w
+ * If hasDx is true, all spectra will have dx values, starting from 0.1 and
+ * increased by 0.1 for each bin.
  */
-Workspace2D_sptr create2DWorkspaceBinned(int nhist, const int numBoundaries,
-                                         const double xBoundaries[]) {
+Workspace2D_sptr create2DWorkspaceNonUniformlyBinned(int nhist,
+                                                     const int numBoundaries,
+                                                     const double xBoundaries[],
+                                                     bool hasDx) {
   BinEdges x(xBoundaries, xBoundaries + numBoundaries);
   const int numBins = numBoundaries - 1;
   Counts y(numBins, 2);
   CountStandardDeviations e(numBins, M_SQRT2);
+  auto dx = Kernel::make_cow<HistogramData::HistogramDx>(
+      numBins, LinearGenerator(0.1, .1));
   auto retVal = boost::make_shared<Workspace2D>();
-  retVal->initialize(nhist, numBins + 1, numBins);
+  retVal->initialize(nhist, createHisto(true, y, e));
   for (int i = 0; i < nhist; i++) {
     retVal->setBinEdges(i, x);
-    retVal->setCounts(i, y);
-    retVal->setCountStandardDeviations(i, e);
+    if (hasDx)
+      retVal->setSharedDx(i, dx);
   }
   return retVal;
 }
@@ -360,11 +383,11 @@ void addNoise(Mantid::API::MatrixWorkspace_sptr ws, double noise,
  * from the centre of the
  * previous.
  * Data filled with: Y: 2.0, E: M_SQRT2, X: nbins of width 1 starting at 0
+ * The flag hasDx is responsible for creating dx values or not
  */
-Workspace2D_sptr
-create2DWorkspaceWithFullInstrument(int nhist, int nbins, bool includeMonitors,
-                                    bool startYNegative, bool isHistogram,
-                                    const std::string &instrumentName) {
+Workspace2D_sptr create2DWorkspaceWithFullInstrument(
+    int nhist, int nbins, bool includeMonitors, bool startYNegative,
+    bool isHistogram, const std::string &instrumentName, bool hasDx) {
   if (includeMonitors && nhist < 2) {
     throw std::invalid_argument("Attempting to 2 include monitors for a "
                                 "workspace with fewer than 2 histograms");
@@ -373,9 +396,10 @@ create2DWorkspaceWithFullInstrument(int nhist, int nbins, bool includeMonitors,
   Workspace2D_sptr space;
   if (isHistogram)
     space = create2DWorkspaceBinned(
-        nhist, nbins); // A 1:1 spectra is created by default
+        nhist, nbins, hasDx); // A 1:1 spectra is created by default
   else
-    space = create2DWorkspace123(nhist, nbins, false);
+    space =
+        create2DWorkspace123(nhist, nbins, false, std::set<int64_t>(), hasDx);
   space->setTitle(
       "Test histogram"); // actually adds a property call run_title to the logs
   space->getAxis(0)->setUnit("TOF");
diff --git a/Framework/WorkflowAlgorithms/test/ExtractQENSMembersTest.h b/Framework/WorkflowAlgorithms/test/ExtractQENSMembersTest.h
index 044e013d5764b5d0268098caa6e413ccf71c5b01..592fac50c42b58868b2dafc16f4149087169e173 100644
--- a/Framework/WorkflowAlgorithms/test/ExtractQENSMembersTest.h
+++ b/Framework/WorkflowAlgorithms/test/ExtractQENSMembersTest.h
@@ -178,7 +178,7 @@ private:
   createResultWorkspace(const std::vector<std::string> &members,
                         const std::vector<double> &dataX) const {
     MatrixWorkspace_sptr resultWorkspace =
-        WorkspaceCreationHelper::create2DWorkspaceBinned(
+        WorkspaceCreationHelper::create2DWorkspaceNonUniformlyBinned(
             static_cast<int>(members.size()), static_cast<int>(dataX.size()),
             dataX.data());
 
diff --git a/Testing/Data/SystemTest/ILL/D2B/532008.nxs.md5 b/Testing/Data/SystemTest/ILL/D2B/532008.nxs.md5
new file mode 100644
index 0000000000000000000000000000000000000000..63c8642c0b99b306bc49724f47b688d1beb7c00f
--- /dev/null
+++ b/Testing/Data/SystemTest/ILL/D2B/532008.nxs.md5
@@ -0,0 +1 @@
+d5b95c6cae111b15d8744d23df643306
diff --git a/Testing/Data/SystemTest/ILL/D2B/532009.nxs.md5 b/Testing/Data/SystemTest/ILL/D2B/532009.nxs.md5
new file mode 100644
index 0000000000000000000000000000000000000000..b6052941f3b8cf67596a945d635fa8f312cd6a27
--- /dev/null
+++ b/Testing/Data/SystemTest/ILL/D2B/532009.nxs.md5
@@ -0,0 +1 @@
+161d6e2ac2bde6e04b7fbf801bcc9652
diff --git a/Testing/SystemTests/tests/analysis/CodeConventions.py b/Testing/SystemTests/tests/analysis/CodeConventions.py
index d0364e1bcaae9354cd6604b39bb83ef9fd9184b7..8b0010b9d0570976f89837db548b5373790fd323 100644
--- a/Testing/SystemTests/tests/analysis/CodeConventions.py
+++ b/Testing/SystemTests/tests/analysis/CodeConventions.py
@@ -23,6 +23,7 @@ ALG_BAD_PARAMS = {
     "EstimateDivergence(v1)":("alpha", "beta0", "beta1"),
     "FindUBUsingLatticeParameters(v1)":("a", "b", "c", "alpha", "beta", "gamma"),
     "IndexSXPeaks(v1)":("a", "b", "c", "alpha", "beta", "gamma", "dTolerance"),
+    "LoadDNSSCD(v1)":("a", "b", "c", "alpha", "beta", "gamma"),
     "ModeratorTzero(v1)":("tolTOF"),
     "MuscatFunc(v1)":("dQ", "dW"),
     "OptimizeCrystalPlacement(v1)":("nPeaks", "nParams", "nIndexed"),
diff --git a/Testing/SystemTests/tests/analysis/PowderDiffILLDetScanReductionTest.py b/Testing/SystemTests/tests/analysis/ILLPowderDiffDetScanReductionTest.py
similarity index 93%
rename from Testing/SystemTests/tests/analysis/PowderDiffILLDetScanReductionTest.py
rename to Testing/SystemTests/tests/analysis/ILLPowderDiffDetScanReductionTest.py
index 1740eb86816577a69ad84149c41254340bb3cf99..8c502913d2173a1b7846af31059b3d4afd899403 100644
--- a/Testing/SystemTests/tests/analysis/PowderDiffILLDetScanReductionTest.py
+++ b/Testing/SystemTests/tests/analysis/ILLPowderDiffDetScanReductionTest.py
@@ -5,10 +5,10 @@ from mantid.simpleapi import PowderDiffILLDetScanReduction, \
 from mantid import config
 
 
-class PowderDiffILLDetScanReductionTest(stresstesting.MantidStressTest):
+class ILLPowderDiffDetScanReductionTest(stresstesting.MantidStressTest):
 
     def __init__(self):
-        super(PowderDiffILLDetScanReductionTest, self).__init__()
+        super(ILLPowderDiffDetScanReductionTest, self).__init__()
         self.setUp()
 
     def setUp(self):
@@ -17,7 +17,7 @@ class PowderDiffILLDetScanReductionTest(stresstesting.MantidStressTest):
         config.appendDataSearchSubDir('ILL/D2B/')
 
     def requiredFiles(self):
-        return ["508093.nxs, 508094.nxs, 508095.nxs, D2B_scan_test.nxs"]
+        return ["508093.nxs, 508094.nxs, 508095.nxs"]
 
     def d2b_2d_tubes_test(self):
         ws_2d_tubes = PowderDiffILLDetScanReduction(
@@ -88,4 +88,5 @@ class PowderDiffILLDetScanReductionTest(stresstesting.MantidStressTest):
         GroupWorkspaces([ws_2d_tubes[0], ws_2d[0], ws_1d[0]], OutputWorkspace='grouped_output')
 
     def validate(self):
+        self.tolerance = 0.0001
         return 'grouped_output', 'D2B_scan_test.nxs'
diff --git a/Testing/SystemTests/tests/analysis/ILL_D2B_DetEffCorrTest.py b/Testing/SystemTests/tests/analysis/ILL_D2B_DetEffCorrTest.py
new file mode 100644
index 0000000000000000000000000000000000000000..e9fe1a0d6bd19ee0132f6e5906d771dd430b3273
--- /dev/null
+++ b/Testing/SystemTests/tests/analysis/ILL_D2B_DetEffCorrTest.py
@@ -0,0 +1,36 @@
+from __future__ import (absolute_import, division, print_function)
+
+import stresstesting
+from mantid.simpleapi import PowderDiffILLDetEffCorr, GroupWorkspaces
+from mantid import config, mtd
+
+
+class ILL_D2B_DetEffCorrTest(stresstesting.MantidStressTest):
+
+    def __init__(self):
+        super(ILL_D2B_DetEffCorrTest, self).__init__()
+        self.setUp()
+
+    def setUp(self):
+        config['default.facility'] = 'ILL'
+        config['default.instrument'] = 'D2B'
+        config.appendDataSearchSubDir('ILL/D2B/')
+
+    def requiredFiles(self):
+        return ['532008.nxs', '532009.nxs']
+
+    def tearDown(self):
+        mtd.clear()
+
+    def runTest(self):
+
+        PowderDiffILLDetEffCorr(CalibrationRun='532008,532009',
+                                DerivationMethod='GlobalSummedReference2D',
+                                ExcludedRange=[-5,10],
+                                OutputWorkspace='calib',
+                                OutputResponseWorkspace='response')
+        GroupWorkspaces(InputWorkspaces=['calib','response'], OutputWorkspace='group')
+
+    def validate(self):
+        self.tolerance = 0.01
+        return ['group', 'D2B_DetEffCorr_Ref.nxs']
diff --git a/Testing/SystemTests/tests/analysis/SANSMaskWorkspaceTest.py b/Testing/SystemTests/tests/analysis/SANSMaskWorkspaceTest.py
index 068fbf1bec195b7a5aa00a9d8e95f11c0c9b2045..3a3090f35db724d3737803b7eefcaa98e3aa4a2a 100644
--- a/Testing/SystemTests/tests/analysis/SANSMaskWorkspaceTest.py
+++ b/Testing/SystemTests/tests/analysis/SANSMaskWorkspaceTest.py
@@ -478,6 +478,44 @@ class SANSMaskWorkspaceTest(unittest.TestCase):
         # Assert
         self._do_assert(workspace, expected_spectra)
 
+    def test_that_beam_stop_masking_is_applied_for_LOQ(self):
+        # Arrange
+        file_information_factory = SANSFileInformationFactory()
+        file_information = file_information_factory.create_sans_file_information("LOQ74044")
+        data_builder = get_data_builder(SANSFacility.ISIS, file_information)
+        data_builder.set_sample_scatter("LOQ74044")
+        data_info = data_builder.build()
+        mask_builder = get_mask_builder(data_info)
+
+        beam_stop_arm_width = .01
+        beam_stop_arm_angle = 180.0
+        beam_stop_arm_pos1 = 0.0
+        beam_stop_arm_pos2 = 0.0
+
+        # Expected_spectra, again the tubes are shifted and that will produce the slightly strange masking
+        expected_spectra = []
+        expected_spectra.extend((7811 + x for x in range(0, 63)))
+        expected_spectra.extend((7939 + x for x in range(0, 63)))
+
+        mask_builder.set_beam_stop_arm_width(beam_stop_arm_width)
+        mask_builder.set_beam_stop_arm_angle(beam_stop_arm_angle)
+        mask_builder.set_beam_stop_arm_pos1(beam_stop_arm_pos1)
+        mask_builder.set_beam_stop_arm_pos2(beam_stop_arm_pos2)
+
+        mask_info = mask_builder.build()
+
+        test_director = TestDirector()
+        test_director.set_states(data_state=data_info, mask_state=mask_info)
+        state = test_director.construct()
+
+        workspace = self._load_workspace(state, move_workspace=False)
+
+        # Act
+        workspace = self._run_mask(state, workspace, "LAB")
+
+        # Assert
+        self._do_assert(workspace, expected_spectra)
+
     def test_that_cylinder_masking_is_applied(self):
         # Arrange
         file_information_factory = SANSFileInformationFactory()
diff --git a/Testing/SystemTests/tests/analysis/reference/D2B_DetEffCorr_Ref.nxs.md5 b/Testing/SystemTests/tests/analysis/reference/D2B_DetEffCorr_Ref.nxs.md5
new file mode 100644
index 0000000000000000000000000000000000000000..504297da5d23c05a51b89eb2de86f05ef5cae222
--- /dev/null
+++ b/Testing/SystemTests/tests/analysis/reference/D2B_DetEffCorr_Ref.nxs.md5
@@ -0,0 +1 @@
+6e114402c861e2129b6c095a083cbd67
diff --git a/Testing/SystemTests/tests/analysis/reference/D2B_scan_test.nxs.md5 b/Testing/SystemTests/tests/analysis/reference/D2B_scan_test.nxs.md5
index 85523cf75a0c8c2d013027436922979b415d23be..64f587253a4513584257f6fda2a56b28970ddb87 100644
--- a/Testing/SystemTests/tests/analysis/reference/D2B_scan_test.nxs.md5
+++ b/Testing/SystemTests/tests/analysis/reference/D2B_scan_test.nxs.md5
@@ -1 +1 @@
-9a20698231398a7b4b1036fdffa97e50
+6726846e1fcd84952bc28ec9e1eee441
diff --git a/buildconfig/CMake/CPackLinuxSetup.cmake b/buildconfig/CMake/CPackLinuxSetup.cmake
index 2451a62301876c487594442e9837ae531da42686..f35b586ec25e460559b5feece5fc45979296a8ec 100644
--- a/buildconfig/CMake/CPackLinuxSetup.cmake
+++ b/buildconfig/CMake/CPackLinuxSetup.cmake
@@ -6,7 +6,7 @@
 string ( TOLOWER "${CPACK_PACKAGE_NAME}" CPACK_PACKAGE_NAME )
 
 # define the source generators
-set ( CPACK_SOURCE_GENERATOR TGZ )
+set ( CPACK_SOURCE_GENERATOR "TGZ;TXZ" )
 set ( CPACK_SOURCE_IGNORE_FILES "/\\\\.git*")
 if (CMAKE_BINARY_DIR MATCHES "^${CMAKE_SOURCE_DIR}/.+")
   # In-source build add the binary directory to files to ignore for the tarball
@@ -21,6 +21,7 @@ if ( "${UNIX_DIST}" MATCHES "Ubuntu" )
   find_program (DPKG_CMD dpkg)
   if ( DPKG_CMD )
     set ( CPACK_GENERATOR "DEB" )
+    set ( CPACK_DEBIAN_COMPRESSION_TYPE "xz" )
     execute_process( COMMAND ${DPKG_CMD} --print-architecture
       OUTPUT_VARIABLE CPACK_DEBIAN_PACKAGE_ARCHITECTURE
       OUTPUT_STRIP_TRAILING_WHITESPACE )
@@ -38,6 +39,7 @@ if ( "${UNIX_DIST}" MATCHES "RedHatEnterprise" OR "${UNIX_DIST}" MATCHES "Fedora
     set ( CPACK_GENERATOR "RPM" )
     set ( CPACK_RPM_PACKAGE_ARCHITECTURE "${CMAKE_SYSTEM_PROCESSOR}" )
     set ( CPACK_RPM_PACKAGE_URL "http://www.mantidproject.org" )
+    set ( CPACK_RPM_COMPRESSION_TYPE "xz" )
 
     # determine the distribution number
     if(NOT CPACK_RPM_DIST)
diff --git a/buildconfig/Jenkins/dev_site.sh b/buildconfig/Jenkins/dev_site.sh
new file mode 100755
index 0000000000000000000000000000000000000000..85f6bd4682d79189b7e956a2de4befaa742fa060
--- /dev/null
+++ b/buildconfig/Jenkins/dev_site.sh
@@ -0,0 +1,77 @@
+#!/bin/bash -ex
+if [ -z "$BUILD_DIR" ]; then
+ if [ -z "$WORKSPACE" ]; then
+     echo "WORKSPACE not set. Cannot continue"
+     exit 1
+ fi
+
+ BUILD_DIR=$WORKSPACE/build
+ echo "Setting BUILD_DIR to $BUILD_DIR"
+fi
+
+if [ -d $BUILD_DIR ]; then
+  echo "$BUILD_DIR exists"
+else
+  mkdir $BUILD_DIR
+fi
+
+###############################################################################
+# Print out the versions of things we are using
+###############################################################################
+# we use cmake3 on rhel because cmake is too old
+if [ $(command -v cmake3) ]; then
+    CMAKE_EXE=cmake3
+else
+    CMAKE_EXE=cmake
+fi
+${CMAKE_EXE} --version
+
+###############################################################################
+# Generator
+###############################################################################
+if [ "$(command -v ninja)" ]; then
+  CMAKE_GENERATOR="-G Ninja"
+elif [ "$(command -v ninja-build)" ]; then
+  CMAKE_GENERATOR="-G Ninja"
+fi
+##### set up the build directory
+cd $BUILD_DIR
+if [ -e $BUILD_DIR/CMakeCache.txt ]; then
+  ${CMAKE_EXE} .
+else
+  ${CMAKE_EXE} ${CMAKE_GENERATOR} ..
+fi
+
+if [ -d dev-docs/html ]; then
+  echo "Updating existing checkout"
+  cd dev-docs/html
+  git pull --rebase
+  cd -
+else
+  echo "Cloning developer site"
+  git clone git@github.com:mantidproject/developer.git dev-docs/html || exit -1
+  cd dev-docs/html
+  git checkout gh-pages
+  cd -
+fi
+
+##### build the developer site
+${CMAKE_EXE} --build . --target dev-docs-html
+
+cd dev-docs/html
+
+if [ "builder" == "$USER" ]; then
+    echo "Setting username"
+    git config user.name mantid-builder
+    git config user.email "mantid-buildserver@mantidproject.org"
+fi
+
+##### push the results
+if [ $(git diff --quiet) ]; then
+    echo "Committing new site"
+    git add .
+    git commit -m "Automatic update of developer site"
+    git push
+else
+    echo "Nothing has changed"
+fi
diff --git a/docs/source/algorithms/ConjoinXRuns-v1.rst b/docs/source/algorithms/ConjoinXRuns-v1.rst
index 97c0261bf535c436e3955d48893217a9398ad0ae..72524c195fbf05108da8f62627e89c842481e8e8 100644
--- a/docs/source/algorithms/ConjoinXRuns-v1.rst
+++ b/docs/source/algorithms/ConjoinXRuns-v1.rst
@@ -10,7 +10,7 @@
 Description
 -----------
 
-This algorithm joins the input workspaces into a single one by concatenating their spectra. The concatenation is done in the same order as in the input workspaces list. Consider using :ref:`SortXAxis <algm-SortXAxis>` afterwards, if necessary. The instrument and the units are copied from the first workspace. The sample logs are also copied from the first input, but the behaviour can be controlled by the instrument parameter file (IPF), as described in :ref:`MergeRuns <algm-MergeRuns>`. Furthermore, that behaviour can be overriden by providing input to the relevant optional properties of the algorithm.
+This algorithm joins the input workspaces into a single one by concatenating their spectra. The concatenation is done in the same order as in the input workspaces list. Consider using :ref:`SortXAxis <algm-SortXAxis>` afterwards, if necessary. The instrument and the units are copied from the first workspace. The sample logs are also copied from the first input, but the behaviour can be controlled by the instrument parameter file (IPF), as described in :ref:`MergeRuns <algm-MergeRuns>`. Furthermore, that behaviour can be overriden by providing input to the relevant optional properties of the algorithm. This algorithm joins Dx values, if present.
 
 InputWorkspaces
 ---------------
diff --git a/docs/source/algorithms/ExtractMask-v1.rst b/docs/source/algorithms/ExtractMask-v1.rst
index 78f5160f5b756fc53865afea7526231ee3fdb4d1..1f13ca0e28b5f4324f99ae8718128bbd45487917 100644
--- a/docs/source/algorithms/ExtractMask-v1.rst
+++ b/docs/source/algorithms/ExtractMask-v1.rst
@@ -18,6 +18,8 @@ new MatrixWorkspace with a single X bin where:
 The spectra containing 0 are also marked as masked and the instrument
 link is preserved so that the instrument view functions correctly.
 
+A list of masked detector IDs is also output. Note this contains the detector IDs which 
+are masked rather than the index or spectrum number. 
 
 Usage
 -----
@@ -29,11 +31,11 @@ Usage
     #create a workspace with a 3*3 pixel detector
     bankPixelWidth = 3
     ws = CreateSampleWorkspace(NumBanks=1,BankPixelWidth=bankPixelWidth)
-    
+
     #Mask out every other detector
     MaskDetectors(ws,WorkspaceIndexList=range(0,bankPixelWidth*bankPixelWidth,2))
 
-    wsMask = ExtractMask(ws)
+    wsMask, maskList = ExtractMask(ws)
 
     #This mask can then be applied to another workspace
     ws2 = CreateSampleWorkspace(NumBanks=1,BankPixelWidth=bankPixelWidth)
@@ -43,6 +45,9 @@ Usage
     print("n ws    ws2")
     for i in range (ws.getNumberHistograms()):
         print("%i %-5s %s" % (i, ws.getDetector(i).isMasked(), ws2.getDetector(i).isMasked()))
+        
+    print("\nMasked detector IDs")
+    print(maskList)
 
 Output:
 
@@ -59,6 +64,9 @@ Output:
     6 True  True
     7 False False
     8 True  True
+    
+    Masked detector IDs
+    [ 9 11 13 15 17]
 
 .. categories::
 
diff --git a/docs/source/algorithms/FakeISISEventDAE-v1.rst b/docs/source/algorithms/FakeISISEventDAE-v1.rst
index 1cbf89c3dd270c4a5301ee45f36d8ed352c2d49f..721609488025ed9f0fb4dd61d19050208dc3106b 100644
--- a/docs/source/algorithms/FakeISISEventDAE-v1.rst
+++ b/docs/source/algorithms/FakeISISEventDAE-v1.rst
@@ -25,10 +25,7 @@ Usage
 
 **Example:**
 
-.. This test is currently hanging on macOS as the MonitorLiveData algorithm
-   is taking a long time to cancel
-
-.. code-block:: python
+.. testcode:: exFakeISISEventDAE
 
     from threading import Thread
     import time
@@ -45,20 +42,22 @@ Usage
     def captureLive():
         ConfigService.setFacility("TEST_LIVE")
 
-        # start a Live data listener updating every second, that rebins the data
-        # and replaces the results each time with those of the last second.
-        StartLiveData(Instrument='ISIS_Event', OutputWorkspace='wsOut', UpdateEvery=1,
-                      ProcessingAlgorithm='Rebin', ProcessingProperties='Params=10000,1000,20000;PreserveEvents=1',
-                      AccumulationMethod='Add', PreserveEvents=True)
-
-        # give it a couple of seconds before stopping it
-        time.sleep(2)
-
-        # This will cancel both algorithms
-        # you can do the same in the GUI
-        # by clicking on the details button on the bottom right
-        AlgorithmManager.newestInstanceOf("MonitorLiveData").cancel()
-        AlgorithmManager.newestInstanceOf("FakeISISEventDAE").cancel()
+        try:
+            # start a Live data listener updating every second, that rebins the data
+            # and replaces the results each time with those of the last second.
+            StartLiveData(Instrument='ISIS_Event', OutputWorkspace='wsOut', UpdateEvery=1,
+                          ProcessingAlgorithm='Rebin', ProcessingProperties='Params=10000,1000,20000;PreserveEvents=1',
+                          AccumulationMethod='Add', PreserveEvents=True)
+
+            # give it a couple of seconds before stopping it
+            time.sleep(2)
+        finally:
+            # This will cancel both algorithms
+            # you can do the same in the GUI
+            # by clicking on the details button on the bottom right
+            AlgorithmManager.newestInstanceOf("MonitorLiveData").cancel()
+            AlgorithmManager.newestInstanceOf("FakeISISEventDAE").cancel()
+            time.sleep(1)
     #--------------------------------------------------------------------------------------------------
 
     oldFacility = ConfigService.getFacility().name()
diff --git a/docs/source/algorithms/FakeISISHistoDAE-v1.rst b/docs/source/algorithms/FakeISISHistoDAE-v1.rst
index 8d65f2a33f540f5147c24b2be4059c25c3db4e0e..6cbe8c7711f5fc23fe8da78f72c276a675f400d0 100644
--- a/docs/source/algorithms/FakeISISHistoDAE-v1.rst
+++ b/docs/source/algorithms/FakeISISHistoDAE-v1.rst
@@ -36,20 +36,21 @@ Usage
     def captureLive():
         ConfigService.setFacility("TEST_LIVE")
 
-        # start a Live data listener updating every second, that rebins the data
-        # and replaces the results each time with those of the last second.
-        StartLiveData(Instrument='ISIS_Histogram', OutputWorkspace='wsOut', UpdateEvery=1,
-                      AccumulationMethod='Replace')
-
-        # give it a couple of seconds before stopping it
-        time.sleep(2)
-
-        # This will cancel both algorithms
-        # you can do the same in the GUI
-        # by clicking on the details button on the bottom right
-        AlgorithmManager.newestInstanceOf("MonitorLiveData").cancel()
-        AlgorithmManager.newestInstanceOf("FakeISISHistoDAE").cancel()
-        time.sleep(1) # give them time to cancel
+        try:
+            # start a Live data listener updating every second, that rebins the data
+            # and replaces the results each time with those of the last second.
+            StartLiveData(Instrument='ISIS_Histogram', OutputWorkspace='wsOut', UpdateEvery=1,
+                          AccumulationMethod='Replace')
+
+            # give it a couple of seconds before stopping it
+            time.sleep(2)
+        finally:
+            # This will cancel both algorithms
+            # you can do the same in the GUI
+            # by clicking on the details button on the bottom right
+            AlgorithmManager.newestInstanceOf("MonitorLiveData").cancel()
+            AlgorithmManager.newestInstanceOf("FakeISISHistoDAE").cancel()
+            time.sleep(1)
     #--------------------------------------------------------------------------------------------------
 
     oldFacility = ConfigService.getFacility().name()
diff --git a/docs/source/algorithms/FilterEvents-v1.rst b/docs/source/algorithms/FilterEvents-v1.rst
index b8032b1853d2694102b52b2139355fa92b27155d..3697e881b78c2552e5f2c8ebb64cd4b1baa6a620 100644
--- a/docs/source/algorithms/FilterEvents-v1.rst
+++ b/docs/source/algorithms/FilterEvents-v1.rst
@@ -100,6 +100,17 @@ name ended with '\_unfiltered'.
 If input property 'OutputWorkspaceIndexedFrom1' is set to True, then
 this workspace shall not be outputed.
 
+Using FilterEvents with fast-changing logs
+##########################################
+
+There are a few parameters to consider when the log filtering is expected to produce a large
+splitter table. An example of such a case would be a data file for which the events need to be split
+according to a log with two or more states changing in the kHz range. To reduce the filtering time,
+one may do the following:
+
+- Make sure the ``SplitterWorkspace`` input is a :ref:`MatrixWorkspace <MatrixWorkspace>`. Such a workspace can be produced by using the ``FastLog = True`` option when calling :ref:`GenerateEventsFilter <algm-GenerateEventsFilter>`.
+- Choose the logs to split. Filtering the logs can take a substantial amount of time. To save time, you may want to split only the logs you will need for analysis. To do so, set ``ExcludeSpecifiedLogs = False`` and list the logs you need in ``TimeSeriesPropertyLogs``. For example, if we only need to know the accumulated proton charge for each filtered workspace, we would set ``TimeSeriesPropertyLogs = proton_charge``.
+
 Difference from FilterByLogValue
 ################################
 
diff --git a/docs/source/algorithms/GenerateEventsFilter-v1.rst b/docs/source/algorithms/GenerateEventsFilter-v1.rst
index d1f49e3ce329239d488a837551aad01df27958bb..3bd3a0ede0018a5de42d7b73bb8bec6e9873ad78 100644
--- a/docs/source/algorithms/GenerateEventsFilter-v1.rst
+++ b/docs/source/algorithms/GenerateEventsFilter-v1.rst
@@ -95,12 +95,12 @@ Generate event filters by time
 ##############################
 
 Event filters can be created by defining start time, stop time and time intervals. 
-The three input properties for them are *StartTime*, *StopTime* and *TimeInterval*, 
+The three input properties for them are ``StartTime``, ``StopTime`` and ``TimeInterval``, 
 respectively. 
 
-*TimeInterval* accepts an array of doubles.  
+``TimeInterval`` accepts an array of doubles.  
 If the array size is zero, then there will be one and only splitter will be 
-created from *StartTime* and *StopTime*.  
+created from ``StartTime`` and ``StopTime``.  
 If the size of the array is one, then all event splitters will have the same duration
 of time. 
 In general if the array is composed as :math:`t_1, t_2, \cdots, t_n`, 
@@ -115,7 +115,7 @@ Unit of time
 ============
 
 There are three types of units that are supported for time. 
-They are second, nanosecond and percentage of duration from *StartTime* to *StopTime*. 
+They are second, nanosecond and percentage of duration from ``StartTime`` to ``StopTime``. 
 
 .. _filterbylogv-GenerateEventFilter-ref:
 
@@ -158,19 +158,25 @@ Algorithm Parameters and Examples
 Here are the introductions to some important parameters (i.e., algorithm's properties). 
 
 
-Parameter: *Centre*
-###################
+Parameter: ``Centre``
+#####################
 
-The input Boolean parameter *centre* is for filtering by log value(s).
-If option *centre* is taken, then for each interval,
+The input Boolean parameter ``centre`` is for filtering by log value(s).
+If option ``centre`` is taken, then for each interval,
 
 -  starting time = log\_time - tolerance\_time;
 -  stopping time = log\_time - tolerance\_time;
 
 It is a shift to left.
 
-Parameter: *MinimumLogValue*, *MaximumLogValue*, *LogValueTolerance* and *LogValueInterval*
-###########################################################################################
+Parameter: ``FastLog``
+######################
+
+When ``FastLog`` is set to True, a :ref:`MatrixWorkspace <MatrixWorkspace>` will be used to store the event
+splitters, which is more appropriate for fast changing logs. (see above for details).
+
+Parameter: ``MinimumLogValue``, ``MaximumLogValue``, ``LogValueTolerance`` and ``LogValueInterval``
+###################################################################################################
 
 These four parameters are used to determine the log value intervals for
 filtering events.
@@ -191,11 +197,11 @@ Integer value log
 
 It is a little bit different for sample log recorded with integer. 
 
-- *MinimumLogValue* and *MaximumLogValue* can be same such that only entries with exacly the same log value 
+- ``MinimumLogValue`` and ``MaximumLogValue`` can be same such that only entries with exacly the same log value 
   will be considered;
-- If *LogValueInterval* is not give (i.e., default value is used), then any log enetry with log value
-  larger and equal to *MinimumLogValue* and smaller and equal to *MaximumLogValue* will be considered. 
-  Be noticed that in the same case for double value log, log entry with value equal to *MaximumLogValue*
+- If ``LogValueInterval`` is not give (i.e., default value is used), then any log enetry with log value
+  larger and equal to ``MinimumLogValue`` and smaller and equal to ``MaximumLogValue`` will be considered. 
+  Be noticed that in the same case for double value log, log entry with value equal to ``MaximumLogValue``
   will be excluded. 
 
 
diff --git a/docs/source/algorithms/LoadDNSLegacy-v1.rst b/docs/source/algorithms/LoadDNSLegacy-v1.rst
index 96aa684c7829712fde4674216648c00f20e1b251..3096529d6988007540dafbcdcfb171846a9ab235 100644
--- a/docs/source/algorithms/LoadDNSLegacy-v1.rst
+++ b/docs/source/algorithms/LoadDNSLegacy-v1.rst
@@ -57,15 +57,15 @@ This algorithm only supports DNS instrument in its configuration with one detect
 Usage
 -----
 
-**Example - Load a DNS legacy .d_dat file:**
+**Example - Load DNS legacy .d_dat files:**
 
 .. code-block:: python
 
    # data file.
-   datafile = 'dn134011vana.d_dat'
+   datafiles = 'dn134011vana.d_dat,dnstof.d_dat'
 
    # Load dataset
-   ws = LoadDNSLegacy(datafile, Normalization='monitor')
+   ws = LoadDNSLegacy(datafiles, Normalization='monitor')
 
    print("This workspace has {} dimensions and has {} histograms.".format(ws.getNumDims(), ws.getNumberHistograms()))
 
diff --git a/docs/source/algorithms/LoadDNSSCD-v1.rst b/docs/source/algorithms/LoadDNSSCD-v1.rst
new file mode 100644
index 0000000000000000000000000000000000000000..d3b24caaf87f879236a4aa96b3150f23a7673dff
--- /dev/null
+++ b/docs/source/algorithms/LoadDNSSCD-v1.rst
@@ -0,0 +1,221 @@
+.. algorithm::
+
+.. summary::
+
+.. relatedalgorithms::
+
+.. properties::
+
+Description
+-----------
+
+.. warning::
+
+   This algorithm does not perform any consistency check of the input data. It is the users responsibility to choose a physically reasonable dataset.
+
+This algorithm loads a list  of DNS `.d_dat` data files into a `MDEventWorkspace <http://www.mantidproject.org/MDEventWorkspace>`_. If the algorithm fails to process a file, this file will be ignored. In this case the algorithm produces a warning and continues to process further files. Only if no valid files are provided, the algorithm terminates with an error message.
+
+This algorithm is meant to replace the :ref:`algm-LoadDNSLegacy` for single crystal diffraction data.
+
+**Output**
+
+As a result, two workspaces are created:
+
+- `OutputWorkspace` contains the raw neutron counts.
+
+- `NormalizationWorkspace` contains the choosen normalization data (either monitor counts or experiment duration time).
+
+Both workspaces have :math:`(H,K,L)` dimensions. The metadata are loaded into time series sample logs.
+
+.. note::
+
+   For the further data reduction :ref:`algm-BinMD` should be used to bin the data.
+
+Restrictions
+------------
+
+- This algorithm only supports the *DNS* instrument in its configuration with one detector bank (polarisation analysis).
+
+- This algorithm does not support DNS TOF mode data. It is meant only for single crystal diffraction experiments with 1 TOF channel.
+
+
+Data replication
+________________
+
+For standard data (vanadium, NiCr, background) the sample rotation angle is assumed to be not important. These data are typically measured only for one sample rotation angle. The algorithm can replicate these data for the same sample rotation angles as a single crystal sample has been measured. For this purpose optional input fields *SaveHuberTo* and *LoadHuberFrom* can be used.
+
+- *SaveHuberTo* should contain a name of the `TableWorkspace <http://www.mantidproject.org/TableWorkspace>`_ where sample rotation angles (Huber) read from the data files will be saved. If the specified workspace exists, it will be overwritten.
+
+- *LoadHuberFrom* should contain a name of the `TableWorkspace <http://www.mantidproject.org/TableWorkspace>`_. The workspace must exist and contain one column with the name *Huber(degrees)*, where the sample rotation angles are specified.
+
+.. note::
+
+   If *LoadHuberFrom* option is applied, sample rotation angles in the data files will be ignored. Only sample rotation angles from the table will be considered.
+
+Usage
+-----
+
+**Example 1 - Load a DNS .d_dat file to MDEventWorkspace:**
+
+.. testcode:: LoadDNSSCDEx1
+
+   # data file.
+   filename = "dn134011vana.d_dat"
+
+   # lattice parameters
+   a = 5.0855
+   b = 5.0855
+   c = 14.0191
+   omega_offset = 225.0
+   hkl1="1,0,0"
+   hkl2="0,0,1"
+   alpha=90.0
+   beta=90.0
+   gamma=120.0
+
+   # load data to MDEventWorkspace
+   ws, ws_norm, huber_ws = LoadDNSSCD(FileNames=filename, NormalizationWorkspace='ws_norm',
+                                      Normalization='monitor', a=a, b=b, c=c, alpha=alpha, beta=beta, gamma=gamma,
+                                      OmegaOffset=omega_offset, HKL1=hkl1, HKL2=hkl2, SaveHuberTo='huber_ws')
+
+   # print output workspace information
+   print("Output Workspace Type is:  {}".format(ws.id()))
+   print("It has {0} events and {1} dimensions:".format(ws.getNEvents(), ws.getNumDims()))
+   for i in range(ws.getNumDims()):
+       dimension = ws.getDimension(i)
+       print("Dimension {0} has name: {1}, id: {2}, Range: {3:.2f} to {4:.2f} {5}".format(i,
+             dimension.getDimensionId(),
+             dimension.name,
+             dimension.getMinimum(),
+             dimension.getMaximum(),
+             dimension.getUnits()))
+
+   # print information about the table workspace
+   print ("TableWorkspace '{0}' has {1} row in the column '{2}'.".format(huber_ws.getName(),
+                                                                         huber_ws.rowCount(),
+                                                                         huber_ws.getColumnNames()[0]))
+   print("It contains sample rotation angle {} degrees".format(huber_ws.cell(0, 0)))
+
+**Output:**
+
+.. testoutput:: LoadDNSSCDEx1
+
+    Output Workspace Type is:  MDEventWorkspace<MDEvent,3>
+    It has 24 events and 3 dimensions:
+    Dimension 0 has name: H, id: H, Range: -15.22 to 15.22 r.l.u
+    Dimension 1 has name: K, id: K, Range: -15.22 to 15.22 r.l.u
+    Dimension 2 has name: L, id: L, Range: -41.95 to 41.95 r.l.u
+    TableWorkspace 'huber_ws' has 1 row in the column 'Huber(degrees)'.
+    It contains sample rotation angle 79.0 degrees
+
+
+**Example 2 - Specify scattering angle limits:**
+
+.. testcode:: LoadDNSSCDEx2
+
+   # data file.
+   filename = "dn134011vana.d_dat"
+
+   # lattice parameters
+   a = 5.0855
+   b = 5.0855
+   c = 14.0191
+   omega_offset = 225.0
+   hkl1="1,0,0"
+   hkl2="0,0,1"
+   alpha=90.0
+   beta=90.0
+   gamma=120.0
+
+   # scattering angle limits, degrees
+   tth_limits = "20,70"
+
+   # load data to MDEventWorkspace
+   ws, ws_norm, huber_ws = LoadDNSSCD(FileNames=filename, NormalizationWorkspace='ws_norm',
+                                      Normalization='monitor', a=a, b=b, c=c, alpha=alpha, beta=beta, gamma=gamma,
+                                      OmegaOffset=omega_offset, HKL1=hkl1, HKL2=hkl2, TwoThetaLimits=tth_limits)
+
+   # print output workspace information
+   print("Output Workspace Type is:  {}".format(ws.id()))
+   print("It has {0} events and {1} dimensions.".format(ws.getNEvents(), ws.getNumDims()))
+
+   # print normalization workspace information
+   print("Normalization Workspace Type is:  {}".format(ws_norm.id()))
+   print("It has {0} events and {1} dimensions.".format(ws_norm.getNEvents(), ws_norm.getNumDims()))
+
+**Output:**
+
+.. testoutput:: LoadDNSSCDEx2
+
+    Output Workspace Type is:  MDEventWorkspace<MDEvent,3>
+    It has 10 events and 3 dimensions.
+    Normalization Workspace Type is:  MDEventWorkspace<MDEvent,3>
+    It has 10 events and 3 dimensions.
+
+**Example 3 - Load sample rotation angles from the table**
+
+.. testcode:: LoadDNSSCDEx3
+
+   # data file.
+   filename = "dn134011vana.d_dat"
+
+   # construct table workspace with 10 raw sample rotation angles from 70 to 170 degrees
+   table = CreateEmptyTableWorkspace()
+   table.addColumn( "double", "Huber(degrees)")
+   for huber in range(70, 170, 10):
+       table.addRow([huber])
+
+   # lattice parameters
+   a = 5.0855
+   b = 5.0855
+   c = 14.0191
+   omega_offset = 225.0
+   hkl1="1,0,0"
+   hkl2="0,0,1"
+   alpha=90.0
+   beta=90.0
+   gamma=120.0
+
+   # load data to MDEventWorkspace
+   ws, ws_norm, huber_ws = LoadDNSSCD(FileNames=filename, NormalizationWorkspace='ws_norm',
+                                      Normalization='monitor', a=a, b=b, c=c, alpha=alpha, beta=beta, gamma=gamma,
+                                      OmegaOffset=omega_offset, HKL1=hkl1, HKL2=hkl2, LoadHuberFrom=table)
+
+   # print output workspace information
+   print("Output Workspace Type is:  {}".format(ws.id()))
+   print("It has {0} events and {1} dimensions.".format(ws.getNEvents(), ws.getNumDims()))
+
+   # setting for the BinMD algorithm
+   bvec0 = '[100],unit,1,0,0'
+   bvec1 = '[001],unit,0,0,1'
+   bvec2 = '[010],unit,0,1,0'
+   extents = '-2,1.5,-0.2,6.1,-10,10'
+   bins = '10,10,1'
+   # bin the data
+   data_raw = BinMD(ws, AxisAligned='0', BasisVector0=bvec0, BasisVector1=bvec1,
+                    BasisVector2=bvec2, OutputExtents=extents, OutputBins=bins, NormalizeBasisVectors='0')
+   # bin normalization
+   data_norm = BinMD(ws_norm, AxisAligned='0', BasisVector0=bvec0, BasisVector1=bvec1,
+                     BasisVector2=bvec2, OutputExtents=extents, OutputBins=bins, NormalizeBasisVectors='0')
+   # normalize data
+   data = data_raw/data_norm
+
+   # print reduced workspace information
+   print("Reduced Workspace Type is:  {}".format(data.id()))
+   print("It has {} dimensions.".format(data.getNumDims()))
+   s =  data.getSignalArray()
+   print("Signal at some points: {0:.4f}, {1:.4f}, {2:.4f}".format(s[7,1][0], s[7,2][0], s[7,3][0]))
+
+**Output:**
+
+.. testoutput:: LoadDNSSCDEx3
+
+    Output Workspace Type is:  MDEventWorkspace<MDEvent,3>
+    It has 240 events and 3 dimensions.
+    Reduced Workspace Type is:  MDHistoWorkspace
+    It has 3 dimensions.
+    Signal at some points: 0.0035, 0.0033, 0.0035
+
+.. categories::
+
+.. sourcelink::
diff --git a/docs/source/algorithms/PowderDiffILLDetEffCorr-v1.rst b/docs/source/algorithms/PowderDiffILLDetEffCorr-v1.rst
index 39733a221e4dfd84f7585620ac8fd14257297fbc..bb2ecc3d790da6bb3c330ab31f427a3ab0a70956 100644
--- a/docs/source/algorithms/PowderDiffILLDetEffCorr-v1.rst
+++ b/docs/source/algorithms/PowderDiffILLDetEffCorr-v1.rst
@@ -9,84 +9,198 @@
 Description
 -----------
 
-This algorithm calculates the detector efficiency corrections for the instrument D20 at the ILL.
+This algorithm calculates the detector efficiency corrections for scanning monochromatic powder diffractometers D20 and D2B at ILL.
+Detector scan in this context stands for a set of discrete rotations of the detector around the sample in the horizontal (scattering) plane.
+The general philosophy is based on the fact that different detector cells pass through the same spatial coordinates during the scan.
+Under the assumption that at a certain coordinate all the detectors should measure the same counts, one could derive relative inter-calibration between the cells.
+The input to the algorithm is a set of detector scan acquisitions recorded for vanadium sample.
+However, **no assumption** that vanadium response has to be flat and isotropic, is made.
+That is, the geometrical effects, off-plane self-attenuation effects, and the small Bragg peaks in the vanadium data are well taken into account.
+The output is a map of calibration constants that have to be applied multiplicatively to the sample data; that is, the calibration constant is the inverse of the relative detector efficiency.
 
-It performs as follows:
+DerivationMethod
+----------------
 
-1. Takes the first cell response as a function of scattering angle as a reference
+There are two strategies to derive the efficiencies, as follows:
 
-2. Takes the second cell, and divides the reference to the second cell response in the overlapping region in terms of scattering angle. This results in the array of relative response ratios.
+  - **SequentialSummedReference1D** (referred hereafter as the **D20** method) must be used for D20, which is a 1D detector with solid rectangular panels.
 
-3. Uses one of the 3 suggested options to compute the relative calibration factor from the array of relative response ratios. This factor is saved in the output as the calibration factor for the second cell.
+    1. Takes the first cell response as a function of scattering angle (during the scan) as a reference
 
-4. The response of the second cell is scaled up with that factor, and then merged (in the overlapping region) with the reference using :ref:`WeightedMean <algm-WeightedMean>`.
+    2. Takes the second cell and divides the reference to the second cell response in the overlapping region in terms of scattering angle. This results in an array of relative response ratios.
 
-5. Repeat from Step 2 for the next cell and so on until the last cell.
+    3. Uses one of the 3 calibration methods (see below) to compute the relative calibration factor from the array of relative response ratios. This factor is saved in the output as the calibration factor for the second cell.
 
-For the zero-counting cells, the calibration factor cannot be computed, and it will be set to 1. Cells are treated as zero-counting, if they count zero more than 80% of time.
+    4. The response of the second cell is scaled up with that factor, and then merged (in the overlapping region) with the reference using :ref:`WeightedMean <algm-WeightedMean>`. This results in a new reference.
 
-After the calibration factors are computed for all the cells, they are divided by the median of all the factors (excluding the zero counting cells),
-in order to absolutely normalise the calibration curve.
+    5. Repeat from Step 2 for the next cell using the updated reference and so on until the last cell.
 
-Input
------
 
-The input must be a single **detector-scan** run in `.nxs` format produced for vanadium.
+    For the zero-counting cells, the calibration factor cannot be computed, hence they will be set to 1. Cells are treated as zero-counting if they count zero more than 80% of time.
 
-Optionally the previously derived calibration file can be seeded, and the algorithm will then compute the residual calibration factors on top of that.
+    After the calibration factors are computed for all the cells, they are divided by the median of all the factors (excluding the zero-counting cells),
+    in order to absolutely normalize the calibration curve.
 
-Method
-------
 
-You can choose the method of how the relative calibration factor is extracted from the relative response ratios (Step 3).
-It can be the Median (default), Mean or :ref:`MostLikelyMean <algm-MostLikelyMean>`.
+  - **GlobalSummedReference2D** (referred hereafter as the **D2B** method) must be used for D2B, which is a 2D detector composed of PSD tubes that are placed at some distance from each other, hence the detector has gaps between the tubes.
 
-Excluded range
+    1. Averages the responses of all the pixels at a certain height level using :ref:`SumOverlappingTubes <algm-SumOverlappingTubes>`. This results in a global reference, which is a 2D matrix of averaged counts.
+
+    2. For each tube, constructs the ratio of the global reference wrt the tube response in the overlapping region; this results in arrays of relative response ratios for each pixel in that tube.
+
+    3. Takes the median of the response ratios as calibration constant for the given pixel in the given tube.
+
+    4. Optionally, if iterations are requested (see below), it applies the calibration of the first run to the input, and repeats from Step 1.
+
+
+    As mentioned, the calibration constants are computed pixel by pixel; no grouping of pixels inside the tubes is done.
+
+CalibrationMethod
+-----------------
+
+When relative response ratios are constructed for a given pixel, the algorithm offers 3 ways to get the calibration factor out: median (default), mean and :ref:`MostLikelyMean <algm-MostLikelyMean>`.
+For the **D2B** case, for the moment only median is supported, as the convergence of the other methods through the iterations is currently under investigation.
+
+CalibrationRun
 --------------
 
-Provide ranges in scattering angle in degrees, to exclude non-desired regions, e.g. the beam stop.
-Multiple regions can be set, **-20,0,10,20** will exclude **[-20,0]** and **[10,20]**.
-This exclusion will happen at Step 3.
+The input must be a set of **detector-scan** numors in **.nxs** format produced for vanadium.
 
-Pixel range
------------
+CalibrationFile
+---------------
+
+Optionally a previously derived calibration file (e.g. the output of this algorithm saved with :ref:`SaveNexusProcessed <algm-SaveNexusProcessed>`) can be provided.
+In this case this calibration will be applied first, and then the algorithm will compute residual calibration factors on top of that.
+
+ExcludedRange
+-------------
+
+Provide ranges in scattering angle in degrees (in equatorial plane) to exclude non-desired regions, e.g. the beam stop.
+In principle, multiple regions can be set, **-20,0,10,20** will exclude **[-20,0]** and **[10,20]**.
+The exclusion happens at Step 3 for both of the derivation methods, before computing the calibration factor out of the relative response ratios.
+
+PixelRange
+----------
 
 Provide the range of detector cells to compute the calibration factors for.
 For the rest of the cells, the factor will be set to 1.
+This is used for **D20** only, and by default the factors will be computed for all the cells.
 
-Output
-------
+NormaliseTo
+-----------
 
-Output will be a single-column workspace containing the calibration factors for each cell. This should be normally saved with
-:ref:`SaveNexusProcessed <algm-SaveNexusProcessed>` to be later used in :ref:`PowderDiffILLReduction <algm-PowderDiffILLReduction>`.
+The input data can be optionally normalised to monitor counts or region-of-interest (ROI, for **D20** only) counts.
 
-Optionally, the full absolute response resulted from the combination of the data for all the cells (see Step 4 above) can also be output.
+ROI
+---
 
-Workflow
---------
+Regions of scattering angle in degrees (in equatorial plane), where the counts are summed, and the data is normalised to the sum. Relevant only for **D20**.
 
-.. diagram:: PowderDiffILLDetEffCorr-v1_wkflw.dot
+OutputWorkspace
+---------------
 
-Related Algorithms
+For **D20**, the output is a single-column workspace containing the calibration factors for each cell.
+For **D2B**, it is a 2D workspace (x axis is the tube index, spectrum axis is the pixel index in the tube).
+The output should be normally saved with
+:ref:`SaveNexusProcessed <algm-SaveNexusProcessed>` to be later used in :ref:`PowderDiffILLReduction <algm-PowderDiffILLReduction>` and :ref:`PowderDiffILLDetScanReduction <algm-PowderDiffILLDetScanReduction>`.
+
+OutputResponseWorkspace
+-----------------------
+
+Optionally, the merged response of the cells taking into account the newly derived calibration can be output. This is a 1D spectrum for **D20** and 2D workspace for **D2B**.
+
+NumberOfIterations
 ------------------
 
-:ref:`PowderDiffILLReduction <algm-PowderDiffILLReduction>` performs the data reduction.
+This is used for **D2B** only.
+For **D20** there is no need for iterations, since a single shot derivation is already convergent; that is, the residual calibration factors are identical to unity.
+
+This specifies how many times the calibration has to be derived (see Step 4 above for **D2B** method):
+
+  - 1 by default: The calibration will be derived only once (single-shot) and no iteration will be performed. Typically this gives reasonably good result already.
 
-Usage
------
+  - User specified positive integer: Iterations will be performed as many times as requested. It is not advised to iterate too much, since after local convergence it may start to diverge again; hence there is a hard limit of 10.
 
-**Example - PowderDiffILLDetEffCorr**
+  - 0 stands for auto: Iterations will be run automatically until the termination criteria is satisfied. Termination criteria is:
+
+      .. math:: \chi^2/NdoF = \frac{\sum_{i,j}(c_{ij} - 1)ˆ2}{N_{tubes} * N_{pixels_per_tube}} < t
+
+      where :math:`c_{ij}` is the residual calibration factor for tube *i* and pixel *j*, :math:`t` is the threshold defined in :ref:`Instrument Parameter File (IPF)<InstrumentParameterFile>` as *chi2_ndof*.
+
+      The top and bottom parts of the tubes are excluded from this calculation. How many pixels exactly are excluded is again defined in :ref:`IPF <InstrumentParameterFile>` as *pixels_to_trim*.
+
+      Currently, for **D2B**, *pixels_to_trim=28* and *t=1%*. With this settings iterations typically terminate after the first one, i. e. one run and one iteration give results already convergent within 1%.
+
+      This has to be interpreted as: the residual calibration is close enough to unity, so further iterations will not change the calibration much.
+
+      However, this criterion does not prevent from divergence in all the cases.
+      It can happen that for a given pixel the residual calibration factor (albeit close to unity) is always on the same side (i.e. always above 1 or below 1); this will cause the absolute calibration to gradually diverge with iterations.
+      Anyways, the method implemented does not provide enough precision to resolve residual calibration better than in the percent range.
+      Hence, care must be taken when using the iterations.
+      It is not recommended to use more than 2 iterations.
+
+Limitations
+-----------
+
+For **D2B** it is assumed that the tubes and pixels pass through the exact same positions during the scan.
+That is, the tubes have to be aligned vertically and horizontally and the gap between each pair of neighboring tubes must be integer multiple of the scan step.
+
+D20 Workflow
+------------
+
+.. diagram:: PowderDiffILLDetEffCorr-v1_D20_wkflw.dot
+
+.. include:: ../usagedata-note.txt
+
+**Example - D20**
 
 .. code-block:: python
 
-   calib = PowderDiffILLDetEffCorr(CalibrationRun='967076', OutputWorkspace='constants')
-   print("Reduced workspace contains {0} constants, one for each cell.".format(calib.getNumberHistograms()))
+   import matplotlib.pyplot as plt
+   from mantid import plots
+   from mantid.simpleapi import PowderDiffILLDetEffCorr
+   PowderDiffILLDetEffCorr(CalibrationRun='967076.nxs', DerivationMethod='SequentialSummedReference1D', OutputWorkspace='calib')
+   Transpose(InputWorkspace='calib', OutputWorkspace='calib')
+   fig, ax = plt.subplots(subplot_kw={'projection':'mantid'})
+   ax.plot(mtd['calib'],'-')
+   ax.set_xlabel('Pixel #')
+   ax.set_ylabel('Calibration constant')
+   fig.show()
+
+.. figure:: /images/D20_calib.png
+  :align: center
+  :width: 600
+
+D2B Workflow
+------------
+
+.. diagram:: PowderDiffILLDetEffCorr-v1_D2B_wkflw.dot
 
-Output:
+**Example - D2B**
 
 .. code-block:: python
 
-   Reduced workspace contains 3072 constants, one for each cell.
+   import matplotlib.pyplot as plt
+   from mantid import plots
+   from mantid.simpleapi import PowderDiffILLDetEffCorr
+   PowderDiffILLDetEffCorr(CalibrationRun='532008,532009', DerivationMethod='GlobalSummedReference2D', OutputWorkspace='calib')
+   fig, ax = plt.subplots(subplot_kw={'projection':'mantid'})
+   c = ax.pcolormesh(mtd['calib'], vmin=0.8, vmax=1.2)
+   ax.set_xlabel('Tube #')
+   ax.set_ylabel('Pixel #')
+   cbar = fig.colorbar(c)
+   cbar.set_label('Calibration constant')
+   fig.show()
+
+.. figure:: /images/D2B_calib.png
+  :align: center
+  :width: 600
+
+Related Algorithms
+------------------
+
+:ref:`PowderDiffILLReduction <algm-PowderDiffILLReduction>` performs the data reduction.
+:ref:`PowderDiffILLDetScanReduction <algm-PowderDiffILLDetScanReduction>` performs the data reduction for detector scans.
 
 .. categories::
 
diff --git a/docs/source/algorithms/PowderDiffILLDetScanReduction-v1.rst b/docs/source/algorithms/PowderDiffILLDetScanReduction-v1.rst
index ab7e09765d7595ceedaca6b0480e41d6ed1f88aa..78114e0436400435861d458811c9c24a6de5b180 100644
--- a/docs/source/algorithms/PowderDiffILLDetScanReduction-v1.rst
+++ b/docs/source/algorithms/PowderDiffILLDetScanReduction-v1.rst
@@ -85,7 +85,7 @@ Output:
 
 .. testoutput:: ExPowderDiffDetScanILLReduction
 
-    '2DTubes' output workspace has 128 diffractograms having 3250 bins each
+    '2DTubes' output workspace has 128 diffractograms having 3025 bins each
     '2D' output workspace has 128 diffractograms having 3025 bins each
     '1D' output workspace has 1 diffractograms having 3025 bins each
 
diff --git a/docs/source/algorithms/StartLiveData-v1.rst b/docs/source/algorithms/StartLiveData-v1.rst
index 27df59b0e3d074c69a50808a8b5bee49f0344bfe..03cf63fcf7ac035c691aaaab96cbb2b3afd223bd 100644
--- a/docs/source/algorithms/StartLiveData-v1.rst
+++ b/docs/source/algorithms/StartLiveData-v1.rst
@@ -123,20 +123,22 @@ Usage
     def captureLive():
         ConfigService.setFacility("TEST_LIVE")
 
-        # start a Live data listener updating every second, that rebins the data
-        # and replaces the results each time with those of the last second.
-        StartLiveData(Instrument='ISIS_Event', OutputWorkspace='wsOut', UpdateEvery=1,
-                      ProcessingAlgorithm='Rebin', ProcessingProperties='Params=10000,1000,20000;PreserveEvents=1',
-                      AccumulationMethod='Add', PreserveEvents=True)
-
-        # give it a couple of seconds before stopping it
-        time.sleep(2)
-
-        # This will cancel both algorithms
-        # you can do the same in the GUI
-        # by clicking on the details button on the bottom right
-        AlgorithmManager.newestInstanceOf("MonitorLiveData").cancel()
-        AlgorithmManager.newestInstanceOf("FakeISISEventDAE").cancel()
+        try:
+            # start a Live data listener updating every second, that rebins the data
+            # and replaces the results each time with those of the last second.
+            StartLiveData(Instrument='ISIS_Event', OutputWorkspace='wsOut', UpdateEvery=1,
+                          ProcessingAlgorithm='Rebin', ProcessingProperties='Params=10000,1000,20000;PreserveEvents=1',
+                          AccumulationMethod='Add', PreserveEvents=True)
+
+            # give it a couple of seconds before stopping it
+            time.sleep(2)
+        finally:
+            # This will cancel both algorithms
+            # you can do the same in the GUI
+            # by clicking on the details button on the bottom right
+            AlgorithmManager.newestInstanceOf("MonitorLiveData").cancel()
+            AlgorithmManager.newestInstanceOf("FakeISISEventDAE").cancel()
+            time.sleep(1)
     #--------------------------------------------------------------------------------------------------
 
     oldFacility = ConfigService.getFacility().name()
@@ -187,20 +189,22 @@ Output:
     def captureLive():
         ConfigService.setFacility("TEST_LIVE")
 
-        # Start a Live data listener updating every second,
-        # that replaces the results each time with those of the last second.
-        # Load only spectra 2,4, and 6 from periods 1 and 3
-        StartLiveData(Instrument='ISIS_Histogram', OutputWorkspace='wsOut', UpdateEvery=1,
-                            AccumulationMethod='Replace', PeriodList=[1,3],SpectraList=[2,4,6])
-
-        # give it a couple of seconds before stopping it
-        time.sleep(2)
-
-        # This will cancel both algorithms
-        # you can do the same in the GUI
-        # by clicking on the details button on the bottom right
-        AlgorithmManager.newestInstanceOf("MonitorLiveData").cancel()
-        AlgorithmManager.newestInstanceOf("FakeISISHistoDAE").cancel()
+        try:
+            # Start a Live data listener updating every second,
+            # that replaces the results each time with those of the last second.
+            # Load only spectra 2,4, and 6 from periods 1 and 3
+            StartLiveData(Instrument='ISIS_Histogram', OutputWorkspace='wsOut', UpdateEvery=1,
+                          AccumulationMethod='Replace', PeriodList=[1,3],SpectraList=[2,4,6])
+
+            # give it a couple of seconds before stopping it
+            time.sleep(2)
+        finally:
+            # This will cancel both algorithms
+            # you can do the same in the GUI
+            # by clicking on the details button on the bottom right
+            AlgorithmManager.newestInstanceOf("MonitorLiveData").cancel()
+            AlgorithmManager.newestInstanceOf("FakeISISHistoDAE").cancel()
+            time.sleep(1)
     #--------------------------------------------------------------------------------------------------
 
     oldFacility = ConfigService.getFacility().name()
diff --git a/docs/source/algorithms/SumOverlappingTubes-v1.rst b/docs/source/algorithms/SumOverlappingTubes-v1.rst
index 852392968b96c13fb7c00adb746de126d5c542db..f8aaa9319484d4567d0f23baf94ada994ffdaf5b 100644
--- a/docs/source/algorithms/SumOverlappingTubes-v1.rst
+++ b/docs/source/algorithms/SumOverlappingTubes-v1.rst
@@ -20,9 +20,9 @@ For the output workspace a grid is made, the x-axis is the scattering angle and
 
 If the Normalise option is set then the counts for each point in the OutputWorkspace are divided by the number of contributing points to that pixel. The scaling goes as
 
-.. math:: C_{scaled, i} = \frac{N_{max}}{N_{i}} C_i
+.. math:: C_{scaled, i} = \frac{C_i}{N_{i}}
 
-where :math:`C_{scaled, i}` is the scaled counts, :math:`C_i` the raw counts, :math:`N_{max}` is the maximum number of tube pixels contributing to any point in the OutputWorkspace, and :math:`N_{i}` is the number of tube pixels contributing to the point being scaled.
+where :math:`C_{scaled, i}` is the scaled counts, :math:`C_i` the raw counts, and :math:`N_{i}` is the number of tube pixels contributing to the point being scaled.
 
 2DTubes Option
 ++++++++++++++
@@ -51,40 +51,28 @@ Usage
 .. testcode:: SumOverlappingTubes2DComponent
 
     ws_508093 = Load('ILL/D2B/508093.nxs')
-    ws = SumOverlappingTubes(InputWorkspaces=ws_508093, OutputType='2DTubes')
+    ws = SumOverlappingTubes(InputWorkspaces=ws_508093, OutputType='2DTubes', MirrorScatteringAngles=True)
     print('X Size: ' + str(ws.blocksize()) + ', Y Size: ' + str(ws.getNumberHistograms()))
-    print('Counts: ' + str(ws.dataY(63)[2068:2078]))
-    print('Errors: ' + str(ws.dataE(63)[2068:2078]))
 
 Output:
 
 .. testoutput:: SumOverlappingTubes2DComponent
 
     X Size: 3200, Y Size: 128
-    Counts: [  4.46083333   9.72705882  13.1016      19.74509804  27.49234043
-      24.62941176  30.25076923  23.2776      10.01591837   1.06      ]
-    Errors: [ 2.03598273  3.00072517  3.46216187  4.1661015   5.29106717  4.88935243
-      5.28366714  4.68457643  3.1474581   0.96348311]
 
 **Example - an example of running SumOverlappingTubes in the 1D case.**
 
 .. testcode:: SumOverlappingTubes1DHeightRange
 
     ws_508093 = Load('ILL/D2B/508093.nxs')
-    ws = SumOverlappingTubes(InputWorkspaces=ws_508093, OutputType='1D', CropNegativeScatteringAngles=True, HeightAxis='-0.05,0.05')
+    ws = SumOverlappingTubes(InputWorkspaces=ws_508093, OutputType='1D', CropNegativeScatteringAngles=True, HeightAxis='-0.05,0.05', MirrorScatteringAngles=True)
     print('X Size: ' + str(ws.blocksize()) + ', Y Size: ' + str(ws.getNumberHistograms()))
-    print('Counts: ' + str(ws.dataY(0)[2068:2078]))
-    print('Errors: ' + str(ws.dataE(0)[2068:2078]))
 
 Output:
 
 .. testoutput:: SumOverlappingTubes1DHeightRange
 
     X Size: 2975, Y Size: 1
-    Counts: [ 127.08681254  131.10979889  201.71370827  233.54556754  296.48915172
-      286.24790285  260.59967375  188.05934431  143.70447835  113.86610964]
-    Errors: [ 12.79221591  12.49380558  15.76125177  16.4410194   20.01917432
-      19.39744376  18.06430971  15.28768958  13.52007099  11.44274953]
 
 .. categories::
 
diff --git a/docs/source/diagrams/PowderDiffILLDetEffCorr-v1_wkflw.dot b/docs/source/diagrams/PowderDiffILLDetEffCorr-v1_D20_wkflw.dot
similarity index 98%
rename from docs/source/diagrams/PowderDiffILLDetEffCorr-v1_wkflw.dot
rename to docs/source/diagrams/PowderDiffILLDetEffCorr-v1_D20_wkflw.dot
index 10ceee7c46f2015604deaa7414002a0b3c72b599..7fe1b7baa06a93e86598e8bae17b396500a8a762 100644
--- a/docs/source/diagrams/PowderDiffILLDetEffCorr-v1_wkflw.dot
+++ b/docs/source/diagrams/PowderDiffILLDetEffCorr-v1_D20_wkflw.dot
@@ -1,5 +1,5 @@
 digraph PowderDiffILLDetEffCorr {
-  label="PowderDiffILLDetEffCorr flowchart"
+  label="SequentialSummedReference1D Flowchart"
   $global_style
 
   subgraph values {
diff --git a/docs/source/diagrams/PowderDiffILLDetEffCorr-v1_D2B_wkflw.dot b/docs/source/diagrams/PowderDiffILLDetEffCorr-v1_D2B_wkflw.dot
new file mode 100644
index 0000000000000000000000000000000000000000..c05db43f1b3764b1dfdc3fbed6e684fe5b1fec39
--- /dev/null
+++ b/docs/source/diagrams/PowderDiffILLDetEffCorr-v1_D2B_wkflw.dot
@@ -0,0 +1,55 @@
+digraph PowderDiffILLDetEffCorr {
+  label="GlobalSummedReference2D Flowchart"
+  $global_style
+
+  subgraph values {
+    $value_style
+    reference
+    single_tube
+    input_data
+    factor
+  }
+
+  subgraph decision {
+    $decision_style
+    Iterate
+  }
+
+  subgraph params {
+    $param_style
+    CalibrationRun
+    ExcludedRange
+    OutputWorkspace
+  }
+
+  subgraph algorithms {
+    $algorithm_style
+    LoadILLDiffraction
+    SumOverlappingTubes
+    ApplyDetectorScanEffCorr
+    MaskBins
+    Divide
+  }
+
+  subgraph process {
+    $process_style
+    Median
+  }
+
+  CalibrationRun -> LoadILLDiffraction
+  LoadILLDiffraction -> input_data
+  input_data -> single_tube
+  input_data -> SumOverlappingTubes
+  SumOverlappingTubes -> reference
+  reference -> Divide
+  single_tube -> Divide
+  Divide -> MaskBins
+  ExcludedRange -> MaskBins
+  MaskBins -> Median
+  Median -> factor
+  factor -> Iterate
+  Iterate -> ApplyDetectorScanEffCorr [label="Yes"]
+  ApplyDetectorScanEffCorr -> input_data
+  Iterate -> OutputWorkspace [label="No"]
+
+}
diff --git a/docs/source/images/D20_calib.png b/docs/source/images/D20_calib.png
new file mode 100644
index 0000000000000000000000000000000000000000..89bab62244f494ce40e67e30a0c3be079c56ffff
Binary files /dev/null and b/docs/source/images/D20_calib.png differ
diff --git a/docs/source/images/D2B_calib.png b/docs/source/images/D2B_calib.png
new file mode 100644
index 0000000000000000000000000000000000000000..1eee6596fe88ec552fc6b65078c210d30c380018
Binary files /dev/null and b/docs/source/images/D2B_calib.png differ
diff --git a/docs/source/interfaces/ISIS SANS v2.rst b/docs/source/interfaces/ISIS SANS v2.rst
index 541f3c47e8ca0601c3fa1e0f5d615320a546d5ea..2bce433d08a5d67429e365fbadfab470307b09b9 100644
--- a/docs/source/interfaces/ISIS SANS v2.rst	
+++ b/docs/source/interfaces/ISIS SANS v2.rst	
@@ -284,7 +284,7 @@ Masking information
 
 The masking table shows detailed information about the masks which will be applied.
 These masks include bin masks, cylinder masks, mask files, spectrum masks, angle masks
-and masks for the beam stop. If as mask is applied only to a particular detector
+and line masks for the beam stop arm. If a mask is applied only to a particular detector
 then this will be shown in the masking table. Note that data needs to be specified
 in order to see the masking information. Also note if a manual change to the
 data table or other settings, requires you to update the row selection by
diff --git a/docs/source/release/v3.12.0/framework.rst b/docs/source/release/v3.12.0/framework.rst
index 4c17faf5adb15ed86974a2a79d43963aa0f6d3e8..48a170b13aa8bafd5aa709b519d4ea6503c82049 100644
--- a/docs/source/release/v3.12.0/framework.rst
+++ b/docs/source/release/v3.12.0/framework.rst
@@ -79,6 +79,7 @@ New
 
 Improved
 ########
+- DEB and RPM package sizes reduced by 17% and 6% respectively.
 - :class:`mantid.kernel.FloatTimeSeriesProperty` now returns :class:`numpy.datetime64` for the log times.
 - The duration reported by a running algorithm now includes time spent for validation of properties and inputs. This fixes a discrepancy between observed and reported timings if validation is expensive, e.g., when checking if a file exists. More detailed timing information is now available when setting the log level to ``debug``.
 - The status of a fit in the fit window is now at the top of the of the dialog instead of the bottom.
diff --git a/docs/source/release/v3.12.1/index.rst b/docs/source/release/v3.12.1/index.rst
index 28e1a43f6f23d885776767653c089919fb0c2752..438f8b0e7be70b92e3653b33806fb5a33038e4c2 100644
--- a/docs/source/release/v3.12.1/index.rst
+++ b/docs/source/release/v3.12.1/index.rst
@@ -15,6 +15,7 @@ The main changes are:
 * Several issues which caused mantid to crash have been fixed.
 * Allowing the live listener funtionality to be used outside ISIS and from the python API.
 * Fixing the header for TOPAS files.
+* Removed version 1 of ``ReflectometryReductionOne`` and ``ReflectometryReductionOneAuto``
 
 Citation
 --------
@@ -44,38 +45,43 @@ Changes in this version
 * `22177 <https://github.com/mantidproject/mantid/pull/22177>`_ Muon analysis and results table
 * `21655 <https://github.com/mantidproject/mantid/pull/21655>`_ Remove dependence of Kafka Live Listener on ISIS specific event data
 * `22226 <https://github.com/mantidproject/mantid/pull/22226>`_ Error when deleting a workspace group in MantidPlot
+* `20997 <https://github.com/mantidproject/mantid/pull/20997>`_ Re #20991: Updated Reflectometry IDFs
 
 Summary of impact
 -----------------
 
-+-------+---------------------------------------------------------------------------+---------------------------+--------------+
-| Issue | Impact                                                                    | Solution                  | Side Effect  |
-|       |                                                                           |                           | Probability  |
-+=======+===========================================================================+===========================+==============+
-| 22205 | Fix header for TOPAS files                                                | Check for header type     | **low**      |
-+-------+---------------------------------------------------------------------------+---------------------------+--------------+
-| 22215 | Fix bug when using StartLiveData through Python API                       | Remove kwarg if None      | **medium**   |
-+-------+---------------------------------------------------------------------------+---------------------------+--------------+
-| 22195 | CrystalField Multi-spectrum resolution model segfault                     | Check sizes               | **low**      |
-+-------+---------------------------------------------------------------------------+---------------------------+--------------+
-| 22194 | SofQW3 segfault no longer occurs                                          | Indexing change           | **medium**   |
-+-------+---------------------------------------------------------------------------+---------------------------+--------------+
-| 22190 | OSX Muon Interface data requirments fixed                                 | GUI changes               | **low**      |
-+-------+---------------------------------------------------------------------------+---------------------------+--------------+
-| 22182 | Update mslice to fix issue with matplotlib < 1.5                          | Update sha1               | **medium**   |
-+-------+---------------------------------------------------------------------------+---------------------------+--------------+
-| 22200 | Fix unreliable tests: Disable ClearCache doc test                         | Clear cache before build  | **low**      |
-+-------+---------------------------------------------------------------------------+---------------------------+--------------+
-| 22244 | Fix dQ calculation in MR Reduction                                        | Now uses radians          | **low**      |
-+-------+---------------------------------------------------------------------------+---------------------------+--------------+
-| 22178 | Fix menu is Muon Analysis not disabled                                    | Change enabled conditions | **medium**   |
-+-------+---------------------------------------------------------------------------+---------------------------+--------------+
-| 22177 | Muon analysis results table generated correctly                           | Additional checks         | **medium**   |
-+-------+---------------------------------------------------------------------------+---------------------------+--------------+
-| 21655 | Remove dependence of Kafka Live Listener on ISIS specific event data      | Remove dependence         | **low**      |
-+-------+---------------------------------------------------------------------------+---------------------------+--------------+
-| 22226 | Error when deleting a workspace group in MantidPlot                       | Better thread safety      | **low**      |
-+-------+---------------------------------------------------------------------------+---------------------------+--------------+
++-------+-----------------------------------------------------------------------------------------+---------------------------+--------------+
+| Issue | Impact                                                                                  | Solution                  | Side Effect  |
+|       |                                                                                         |                           | Probability  |
++=======+=========================================================================================+===========================+==============+
+| 22205 | Fix header for TOPAS files                                                              | Check for header type     | **low**      |
++-------+-----------------------------------------------------------------------------------------+---------------------------+--------------+
+| 22215 | Fix bug when using StartLiveData through Python API                                     | Remove kwarg if None      | **medium**   |
++-------+-----------------------------------------------------------------------------------------+---------------------------+--------------+
+| 22195 | CrystalField Multi-spectrum resolution model segfault                                   | Check sizes               | **low**      |
++-------+-----------------------------------------------------------------------------------------+---------------------------+--------------+
+| 22194 | SofQW3 segfault no longer occurs                                                        | Indexing change           | **medium**   |
++-------+-----------------------------------------------------------------------------------------+---------------------------+--------------+
+| 22190 | OSX Muon Interface data requirments fixed                                               | GUI changes               | **low**      |
++-------+-----------------------------------------------------------------------------------------+---------------------------+--------------+
+| 22182 | Update mslice to fix issue with matplotlib < 1.5                                        | Update sha1               | **medium**   |
++-------+-----------------------------------------------------------------------------------------+---------------------------+--------------+
+| 22200 | Fix unreliable tests: Disable ClearCache doc test                                       | Clear cache before build  | **low**      |
++-------+-----------------------------------------------------------------------------------------+---------------------------+--------------+
+| 22244 | Fix dQ calculation in MR Reduction                                                      | Now uses radians          | **low**      |
++-------+-----------------------------------------------------------------------------------------+---------------------------+--------------+
+| 22178 | Fix menu is Muon Analysis not disabled                                                  | Change enabled conditions | **medium**   |
++-------+-----------------------------------------------------------------------------------------+---------------------------+--------------+
+| 22177 | Muon analysis results table generated correctly                                         | Additional checks         | **medium**   |
++-------+-----------------------------------------------------------------------------------------+---------------------------+--------------+
+| 21655 | Remove dependence of Kafka Live Listener on ISIS specific event data                    | Remove dependence         | **low**      |
++-------+-----------------------------------------------------------------------------------------+---------------------------+--------------+
+| 22226 | Error when deleting a workspace group in MantidPlot                                     | Better thread safety      | **low**      |
++-------+-----------------------------------------------------------------------------------------+---------------------------+--------------+
+| 20997 | Updated Reflectometry IDFs                                                              | Changed IDFs              | **low**      |
++-------+-----------------------------------------------------------------------------------------+---------------------------+--------------+
+| 20997 | Removed version 1 of ``ReflectometryReductionOne`` and ``ReflectometryReductionOneAuto``| Removed old algorithms    | **low**      |
++-------+-----------------------------------------------------------------------------------------+---------------------------+--------------+
 
 .. _download page: http://download.mantidproject.org
 
diff --git a/docs/source/release/v3.13.0/diffraction.rst b/docs/source/release/v3.13.0/diffraction.rst
index e601443a5b7720d2af87d06c1b5035cf5a87b89d..6d0ad9a3c3f89556cea7493354023f31448f7a9a 100644
--- a/docs/source/release/v3.13.0/diffraction.rst
+++ b/docs/source/release/v3.13.0/diffraction.rst
@@ -9,6 +9,12 @@ Diffraction Changes
     putting new features at the top of the section, followed by
     improvements, followed by bug fixes.
 
+New Features
+------------
+
+- :ref:`PowderDiffILLDetEffCorr <algm-PowderDiffILLDetEffCorr>` is extended to compute the detector efficiencies also for the 2-dimensional scanning diffractometer D2B at the ILL.
+
+
 Engineering Diffraction
 -----------------------
 
@@ -19,9 +25,12 @@ Engineering Diffraction
 Single Crystal Diffraction
 --------------------------
 
+- New algorithm :ref:`LoadDNSSCD <algm-LoadDNSSCD>` to load multiple single crystal diffraction data files from the DNS instrument into MDEventWorkspace.
+
 Improvements
 ############
 
 - PeaksWorkspace has column added for the unique peak number so peaks can be found after sorting or filtering.
 
 - :ref:`StatisticsOfPeaksWorkspace <algm-StatisticsOfPeaksWorkspace>` has option to use a weighted Z score for determining which peaks are outliers and has a new output workspace for plotting intensities of equivalent peaks.
+
diff --git a/docs/source/release/v3.13.0/framework.rst b/docs/source/release/v3.13.0/framework.rst
index 25369666a740f7a8c20b601a34954d83120774ec..d9ad8f68c71293b0051f66d40a9d748721d5596c 100644
--- a/docs/source/release/v3.13.0/framework.rst
+++ b/docs/source/release/v3.13.0/framework.rst
@@ -10,7 +10,6 @@ Framework Changes
     improvements, followed by bug fixes.
 
 
-
 Algorithms
 ----------
 
@@ -30,6 +29,7 @@ Improved
   returns as many items as iterations done for each spectrum, making the iterations easy to count.
 - :ref:`ConvertToPointData <algm-ConvertToPointData>` and :ref:`ConvertToHistogram <algm-ConvertToHistogram>` now propagate the Dx errors to the output.
 - The algorithm :ref:`CreateWorkspace <algm-CreateWorkspace>` can now optionally receive the Dx errors.
+- :ref:`ConjoinXRuns <algm-ConjoinXRuns>` joins Dx errors if present
 - The algorithm :ref:`SortXAxis <algm-SortXAxis>` has a new input option that allows ascending (default) and descending sorting. Furthermore, Dx values will be considered if present. The documentation needed to be corrected.
 
 New
@@ -41,5 +41,6 @@ Bug fixes
 #########
 
 - The documentation of the algorithm :ref:`algm-CreateSampleWorkspace` did not match its implementation. The axis in beam direction will now be correctly described as Z instead of X.
+- The :ref:`ExtractMask <algm-ExtractMask>` algorithm now returns a non-empty list of detector ID's when given a MaskWorkspace.
 
 :ref:`Release 3.13.0 <v3.13.0>`
diff --git a/docs/source/release/v3.13.0/sans.rst b/docs/source/release/v3.13.0/sans.rst
index 5975f8423828c85847be61ae44e7cf60e72c44d1..2c9a9f23fe801dec1650b0df228de4c7885f4343 100644
--- a/docs/source/release/v3.13.0/sans.rst
+++ b/docs/source/release/v3.13.0/sans.rst
@@ -9,4 +9,21 @@ SANS Changes
     putting new features at the top of the section, followed by
     improvements, followed by bug fixes.
 
+ISIS SANS Interface
+----------------------------
+
+New features
+############
+
+Improvements
+############
+
+Bug fixes
+#########
+* Fixed a bug where the beam stop arm was not being masked on LOQ.
+
+Features Removed
+################
+
+
 :ref:`Release 3.13.0 <v3.13.0>`
\ No newline at end of file
diff --git a/docs/source/techniques/DirecttoolsPythonModule.rst b/docs/source/techniques/DirecttoolsPythonModule.rst
index 0f78bf1aea744bae77fbdfd061040d169c13d7ec..c31c3f77c1ccc351c9c6e2c4eb64479384be488c 100644
--- a/docs/source/techniques/DirecttoolsPythonModule.rst
+++ b/docs/source/techniques/DirecttoolsPythonModule.rst
@@ -52,14 +52,20 @@ An important aspect of examining the :math:`S(Q,E)` workspace is to plot cuts at
 
    import directtools as dt
    from mantid.simpleapi import *
+   import warnings
    
    DirectILLCollectData(Run='ILL/IN4/084447.nxs', OutputWorkspace='data')
    DirectILLReduction(InputWorkspace='data', OutputWorkspace='SofQW')
    
    Q = 2.
    dQ = 0.2
-   fig, axes, cuts = dt.plotconstQ('SofQW', Q, dQ)
-   #fig.show()
+   # plotconstQ produces a warning on some versions of numpy.
+   # The "with" statement catches this warning so that the automated
+   # builds don't fail.
+   with warnings.catch_warnings():
+       warnings.simplefilter("ignore", category=UserWarning)
+       fig, axes, cuts = dt.plotconstQ('SofQW', Q, dQ)
+       #fig.show()
 
 Any of the workspace, cut centre or cut width arguments can be a :class:`list` instead. This enables data comparison:
 
diff --git a/instrument/D20_Parameters.xml b/instrument/D20_Parameters.xml
index 9f4ac0587877c80ecde601fa073aeaa48aa44f67..17d29c8f6f0bf58e1f891219a3f9bd1069d3cab6 100644
--- a/instrument/D20_Parameters.xml
+++ b/instrument/D20_Parameters.xml
@@ -2,9 +2,15 @@
 <parameter-file instrument="D20" valid-from="1900-01-31 23:59:59">
 	<component-link name="D20">
 
+		<!-- The detector name for the height axis used in SumOverlappingTubes -->
 		<parameter name="detector_for_height_axis" type="string">
 			<value val="panel_1" />
 		</parameter>
 
+		<!-- The flag for mirroring the angles in SumOverlappingTubes -->
+		<parameter name="mirror_scattering_angles" type="bool">
+			<value val="False" />
+	  </parameter>
+
 	</component-link>
 </parameter-file>
diff --git a/instrument/D2B_Parameters.xml b/instrument/D2B_Parameters.xml
index 49b19be1c5f237c146aa83a022cce4c4c207b2c4..cb1d2dee3f0ef69ede8fc45124b377acb4a31373 100644
--- a/instrument/D2B_Parameters.xml
+++ b/instrument/D2B_Parameters.xml
@@ -2,12 +2,27 @@
 <parameter-file instrument="D2B" valid-from="1900-01-31 23:59:59">
 	<component-link name="D2B">
 
-		<parameter name="mirror_detector_angles" type="bool">
-			<value val="true" />
-		</parameter>
+		<!-- The detector name for the height axis used in SumOverlappingTubes -->
 		<parameter name="detector_for_height_axis" type="string">
 			<value val="tube_1" />
 		</parameter>
 
+		<!-- The flag for mirroring the angles in SumOverlappingTubes -->
+		<parameter name="mirror_scattering_angles" type="bool">
+			<value val="True" />
+	  </parameter>
+
+		<!-- Number of pixels to trim from the top and the bottom of the tubes when
+		calculating the chiˆ2 in the detector efficiencies in case of autoiterations -->
+		<parameter name="pixels_to_trim" type="int">
+			<value val="28" />
+	  </parameter>
+
+		<!-- The chiˆ2/NdoF threshold for termination of autoiterations
+		in detector efficiency calculation -->
+		<parameter name="chi2_ndof" type="float">
+			<value val="0.01" />
+	  </parameter>
+
 	</component-link>
 </parameter-file>
diff --git a/instrument/OFFSPEC_Parameters.xml b/instrument/OFFSPEC_Parameters.xml
index e3e5d0f4383c75c378345407cdcd31878fb57f34..9f82f9daf36552947ab6474067aa78066fae98af 100644
--- a/instrument/OFFSPEC_Parameters.xml
+++ b/instrument/OFFSPEC_Parameters.xml
@@ -21,14 +21,6 @@
    <value val="14.0"/>
   </parameter>
 
-  <parameter name="PointDetectorStart">
-    <value val="3"/>
-  </parameter>
-
-  <parameter name="PointDetectorStop">
-    <value val="3"/>
-  </parameter>
-
   <parameter name="MultiDetectorStart">
   <!--
   This channel number is marks the start of all other linear and/or area detectors.
@@ -48,13 +40,27 @@
     <value val="14.0"/>
   </parameter>
   
-  <parameter name="TransRunStartOverlap"> <!-- in wavelength -->
-    <value val="10"/>
+  <parameter name="TransRunStartOverlap">
+    <value val="10.0"/>
   </parameter>
 
-  <parameter name="TransRunEndOverlap"> <!-- in wavelength -->
-    <value val="12"/>
+  <parameter name="TransRunEndOverlap"> 
+    <value val="12.0"/>
+    
+  </parameter>
+  <parameter name="ProcessingInstructions" type="string"> 
+    <value val="390-415"/>
   </parameter>
   
+<parameter name="DetectorCorrectionType" type="string">
+    <value val="RotateAroundSample"/>
+</parameter>
+
+<parameter name="AnalysisMode" type="string">
+    <value val="MultiDetectorAnalysis"/>
+</parameter>
+
+   
+   
 </component-link>
 </parameter-file>
diff --git a/qt/widgets/common/src/SaveWorkspaces.cpp b/qt/widgets/common/src/SaveWorkspaces.cpp
index 838afb6a84ba13ebc88b0f10fc0ecaf0fae3f9a8..ef1f1d0152798604da132b87fd9d668a82b88ba2 100644
--- a/qt/widgets/common/src/SaveWorkspaces.cpp
+++ b/qt/widgets/common/src/SaveWorkspaces.cpp
@@ -342,7 +342,7 @@ void SaveWorkspaces::saveSel() {
     } // end if save in this format
   }   // end loop over formats
 
-  saveCommands += "print 'success'";
+  saveCommands += "print('success')";
   QString status(runPythonCode(saveCommands).trimmed());
 
   if (m_saveAsZeroErrorFree) {
diff --git a/scripts/SANS/sans/algorithm_detail/mask_workspace.py b/scripts/SANS/sans/algorithm_detail/mask_workspace.py
index 10caf14904c97a8cd0e277d8087150da9927e1eb..951a5bee34c9de7104eeba0eb1a5c70223c2f158 100644
--- a/scripts/SANS/sans/algorithm_detail/mask_workspace.py
+++ b/scripts/SANS/sans/algorithm_detail/mask_workspace.py
@@ -299,9 +299,9 @@ def mask_angle(mask_info, workspace):
     return workspace
 
 
-def mask_beam_stop(mask_info, workspace, instrument):
+def mask_beam_stop(mask_info, workspace, instrument, detector_names):
     """
-    The beam stop is being masked here. Note that this is only implemented for SANS2D
+    The beam stop is being masked here.
 
     :param mask_info: a SANSStateMask object.
     :param workspace: the workspace which is to be masked.
@@ -313,18 +313,17 @@ def mask_beam_stop(mask_info, workspace, instrument):
     beam_stop_arm_pos1 = mask_info.beam_stop_arm_pos1
     beam_stop_arm_pos2 = mask_info.beam_stop_arm_pos2
     if beam_stop_arm_width is not None and beam_stop_arm_angle is not None:
-        if instrument is SANSInstrument.SANS2D:
-            detector = workspace.getInstrument().getComponentByName('rear-detector')
-            z_position = detector.getPos().getZ()
-            start_point = [beam_stop_arm_pos1, beam_stop_arm_pos2, z_position]
-            line_mask = create_line_mask(start_point, 100., beam_stop_arm_width, beam_stop_arm_angle)
+        detector = workspace.getInstrument().getComponentByName(detector_names['LAB'])
+        z_position = detector.getPos().getZ()
+        start_point = [beam_stop_arm_pos1, beam_stop_arm_pos2, z_position]
+        line_mask = create_line_mask(start_point, 100., beam_stop_arm_width, beam_stop_arm_angle)
 
-            mask_name = "MaskDetectorsInShape"
-            mask_options = {"Workspace": workspace,
-                            "ShapeXML": line_mask}
-            mask_alg = create_unmanaged_algorithm(mask_name, **mask_options)
-            mask_alg.execute()
-            workspace = mask_alg.getProperty("Workspace").value
+        mask_name = "MaskDetectorsInShape"
+        mask_options = {"Workspace": workspace,
+                        "ShapeXML": line_mask}
+        mask_alg = create_unmanaged_algorithm(mask_name, **mask_options)
+        mask_alg.execute()
+        workspace = mask_alg.getProperty("Workspace").value
     return workspace
 
 
@@ -351,10 +350,11 @@ class NullMasker(Masker):
 
 
 class MaskerISIS(Masker):
-    def __init__(self, spectra_block, instrument):
+    def __init__(self, spectra_block, instrument, detector_names):
         super(MaskerISIS, self).__init__()
         self._spectra_block = spectra_block
         self._instrument = instrument
+        self._detector_names = detector_names
 
     def mask_workspace(self, mask_info, workspace_to_mask, detector_type, progress):
         """
@@ -388,7 +388,7 @@ class MaskerISIS(Masker):
 
         # Mask beam stop
         progress.report("Masking beam stop.")
-        return mask_beam_stop(mask_info, workspace_to_mask, self._instrument)
+        return mask_beam_stop(mask_info, workspace_to_mask, self._instrument, self._detector_names)
 
 
 class MaskFactory(object):
@@ -406,13 +406,14 @@ class MaskFactory(object):
         """
         data_info = state.data
         instrument = data_info.instrument
+        detector_names = state.reduction.detector_names
         if instrument is SANSInstrument.LARMOR or instrument is SANSInstrument.LOQ or\
                         instrument is SANSInstrument.SANS2D or instrument is SANSInstrument.ZOOM:  # noqa
             run_number = data_info.sample_scatter_run_number
             file_name = data_info.sample_scatter
             _, ipf_path = get_instrument_paths_for_sans_file(file_name)
             spectra_block = SpectraBlock(ipf_path, run_number, instrument, detector_type)
-            masker = MaskerISIS(spectra_block, instrument)
+            masker = MaskerISIS(spectra_block, instrument, detector_names)
         else:
             masker = NullMasker()
             NotImplementedError("MaskFactory: Other instruments are not implemented yet.")