diff --git a/Framework/API/src/FrameworkManager.cpp b/Framework/API/src/FrameworkManager.cpp
index c2c995445879b48ef42e507911d943ee0c900de1..fb0e20472637831374ffd7b49d5b0ded19f44e52 100644
--- a/Framework/API/src/FrameworkManager.cpp
+++ b/Framework/API/src/FrameworkManager.cpp
@@ -128,6 +128,7 @@ FrameworkManagerImpl::FrameworkManagerImpl()
   _set_output_format(_TWO_DIGIT_EXPONENT);
 #endif
 
+  ConfigService::Instance();
   g_log.notice() << Mantid::welcomeMessage() << '\n';
   loadPlugins();
   disableNexusOutput();
diff --git a/Framework/API/src/MDFrameValidator.cpp b/Framework/API/src/MDFrameValidator.cpp
index f8968c71e10bbeab428c9e932242a25f34579323..e80d0e6f442ed1b2137bcb0d40a22a1f412d90c7 100644
--- a/Framework/API/src/MDFrameValidator.cpp
+++ b/Framework/API/src/MDFrameValidator.cpp
@@ -36,8 +36,8 @@ std::string
 MDFrameValidator::checkValidity(const IMDWorkspace_sptr &workspace) const {
 
   for (size_t index = 0; index < workspace->getNumDims(); ++index) {
-    const auto &frame = workspace->getDimension(index)->getMDFrame();
-    if (frame.name() != m_frameID)
+    const auto dimension = workspace->getDimension(index);
+    if (dimension->getMDFrame().name() != m_frameID)
       return "MDWorkspace must be in the " + m_frameID + " frame.";
   }
 
diff --git a/Framework/Algorithms/CMakeLists.txt b/Framework/Algorithms/CMakeLists.txt
index d513840a134f95c5ba095507bba5e1b684539c14..833a810433fb12e2128ee3a379f310da5dfd9749 100644
--- a/Framework/Algorithms/CMakeLists.txt
+++ b/Framework/Algorithms/CMakeLists.txt
@@ -95,6 +95,7 @@ set ( SRC_FILES
 	src/CrossCorrelate.cpp
 	src/CuboidGaugeVolumeAbsorption.cpp
 	src/CylinderAbsorption.cpp
+	src/DeadTimeCorrection.cpp
 	src/DeleteLog.cpp
 	src/DeleteWorkspace.cpp
 	src/DeleteWorkspaces.cpp
@@ -215,6 +216,7 @@ set ( SRC_FILES
 	src/PDDetermineCharacterizations.cpp
 	src/PDFFourierTransform.cpp
 	src/PaddingAndApodization.cpp
+	src/ParallaxCorrection.cpp
 	src/Pause.cpp
 	src/PerformIndexOperations.cpp
 	src/Plus.cpp
@@ -430,6 +432,7 @@ set ( INC_FILES
 	inc/MantidAlgorithms/CrossCorrelate.h
 	inc/MantidAlgorithms/CuboidGaugeVolumeAbsorption.h
 	inc/MantidAlgorithms/CylinderAbsorption.h
+	inc/MantidAlgorithms/DeadTimeCorrection.h
 	inc/MantidAlgorithms/DeleteLog.h
 	inc/MantidAlgorithms/DeleteWorkspace.h
 	inc/MantidAlgorithms/DeleteWorkspaces.h
@@ -554,6 +557,7 @@ set ( INC_FILES
 	inc/MantidAlgorithms/PDDetermineCharacterizations.h
 	inc/MantidAlgorithms/PDFFourierTransform.h
 	inc/MantidAlgorithms/PaddingAndApodization.h
+	inc/MantidAlgorithms/ParallaxCorrection.h
 	inc/MantidAlgorithms/Pause.h
 	inc/MantidAlgorithms/PerformIndexOperations.h
 	inc/MantidAlgorithms/Plus.h
@@ -779,6 +783,7 @@ set ( TEST_FILES
 	CrossCorrelateTest.h
 	CuboidGaugeVolumeAbsorptionTest.h
 	CylinderAbsorptionTest.h
+	DeadTimeCorrectionTest.h
 	DeleteLogTest.h
 	DeleteWorkspaceTest.h
 	DeleteWorkspacesTest.h
@@ -896,6 +901,7 @@ set ( TEST_FILES
 	PDDetermineCharacterizationsTest.h
 	PDFFourierTransformTest.h
 	PaddingAndApodizationTest.h
+	ParallaxCorrectionTest.h
 	PauseTest.h
 	PerformIndexOperationsTest.h
 	PlusTest.h
@@ -941,6 +947,7 @@ set ( TEST_FILES
 	ResizeRectangularDetectorTest.h
 	RingProfileTest.h
 	RunCombinationHelperTest.h
+	SampleLogsBehaviourTest.h
 	SANSCollimationLengthEstimatorTest.h
 	SassenaFFTTest.h
 	ScaleTest.h
diff --git a/Framework/Algorithms/inc/MantidAlgorithms/ConjoinXRuns.h b/Framework/Algorithms/inc/MantidAlgorithms/ConjoinXRuns.h
index 864a948e661c77f044c184b097b733b4855e728a..bfb718362ec1f6510a42476f65f1a77edf434373 100644
--- a/Framework/Algorithms/inc/MantidAlgorithms/ConjoinXRuns.h
+++ b/Framework/Algorithms/inc/MantidAlgorithms/ConjoinXRuns.h
@@ -27,6 +27,15 @@ public:
   const std::string summary() const override;
   std::map<std::string, std::string> validateInputs() override;
 
+  /// ConjoinXRuns parameter names of the paramter file for sample log merging
+  static const std::string SUM_MERGE;
+  static const std::string TIME_SERIES_MERGE;
+  static const std::string LIST_MERGE;
+  static const std::string WARN_MERGE;
+  static const std::string WARN_MERGE_TOLERANCES;
+  static const std::string FAIL_MERGE;
+  static const std::string FAIL_MERGE_TOLERANCES;
+
 protected:
   void fillHistory() override;
 
diff --git a/Framework/Algorithms/inc/MantidAlgorithms/DeadTimeCorrection.h b/Framework/Algorithms/inc/MantidAlgorithms/DeadTimeCorrection.h
new file mode 100644
index 0000000000000000000000000000000000000000..4065e161b01cf426cf28086acee9b4f9d261677b
--- /dev/null
+++ b/Framework/Algorithms/inc/MantidAlgorithms/DeadTimeCorrection.h
@@ -0,0 +1,33 @@
+// Mantid Repository : https://github.com/mantidproject/mantid
+//
+// Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
+//     NScD Oak Ridge National Laboratory, European Spallation Source
+//     & Institut Laue - Langevin
+// SPDX - License - Identifier: GPL - 3.0 +
+#ifndef MANTID_ALGORITHMS_DEADTIMECORRECTION_H_
+#define MANTID_ALGORITHMS_DEADTIMECORRECTION_H_
+
+#include "MantidAPI/Algorithm.h"
+#include "MantidAlgorithms/DllConfig.h"
+
+namespace Mantid {
+namespace Algorithms {
+
+/** DeadTimeCorrection : Performs dead time correction.
+ */
+class MANTID_ALGORITHMS_DLL DeadTimeCorrection : public API::Algorithm {
+public:
+  const std::string name() const override;
+  int version() const override;
+  const std::string category() const override;
+  const std::string summary() const override;
+
+private:
+  void init() override;
+  void exec() override;
+};
+
+} // namespace Algorithms
+} // namespace Mantid
+
+#endif /* MANTID_ALGORITHMS_DEADTIMECORRECTION_H_ */
diff --git a/Framework/Algorithms/inc/MantidAlgorithms/FitPeaks.h b/Framework/Algorithms/inc/MantidAlgorithms/FitPeaks.h
index 442cde37c7835ddb454e6523269d65aa1021c2f8..7e0bdbe45a8ae83e0b2b80413616f93b567ce948 100644
--- a/Framework/Algorithms/inc/MantidAlgorithms/FitPeaks.h
+++ b/Framework/Algorithms/inc/MantidAlgorithms/FitPeaks.h
@@ -42,6 +42,7 @@ public:
   size_t getNumberParameters() const;
   size_t getNumberPeaks() const;
   double getParameterValue(size_t ipeak, size_t iparam) const;
+  double getParameterError(size_t ipeak, size_t iparam) const;
   void setRecord(size_t ipeak, const double cost, const double peak_position,
                  const FitFunction fit_functions);
   void setBadRecord(size_t ipeak, const double peak_position);
@@ -56,6 +57,8 @@ private:
   std::vector<double> m_fitted_peak_positions;
   // fitted peak and background parameters
   std::vector<std::vector<double>> m_function_parameters_vector;
+  /// fitted peak and background parameters' fitting error
+  std::vector<std::vector<double>> m_function_errors_vector;
 };
 } // namespace FitPeaksAlgorithm
 
@@ -97,7 +100,7 @@ private:
   void processInputFitRanges();
 
   /// Generate output workspaces
-  void generateFittedParametersValueWorkspace();
+  void generateFittedParametersValueWorkspaces();
   /// main method to create output workspaces
   void generateOutputPeakPositionWS();
   /// Generate workspace for calculated values
@@ -159,6 +162,10 @@ private:
                                    API::IPeakFunction_sptr peakfunction,
                                    API::IBackgroundFunction_sptr bkgdfunc);
 
+  void setupParameterTableWorkspace(API::ITableWorkspace_sptr table_ws,
+                                    const std::vector<std::string> &param_names,
+                                    bool with_chi2);
+
   /// get vector X, Y and E in a given range
   void getRangeData(size_t iws, const std::pair<double, double> &fit_window,
                     std::vector<double> &vec_x, std::vector<double> &vec_y,
@@ -198,7 +205,7 @@ private:
   int observePeakCenter(const HistogramData::Histogram &histogram,
                         API::FunctionValues &bkgd_values, size_t start_index,
                         size_t stop_index, double &peak_center,
-                        size_t &peak_center_index, double &peak_intensity);
+                        size_t &peak_center_index, double &peak_height);
 
   /// Observe peak width
   double observePeakWidth(const HistogramData::Histogram &histogram,
@@ -244,9 +251,11 @@ private:
   /// output workspace for peak positions
   API::MatrixWorkspace_sptr
       m_outputPeakPositionWorkspace; // output workspace for peak positions
-  /// optional output analysis workspaces
+  /// output analysis workspaces
   /// table workspace for fitted parameters
   API::ITableWorkspace_sptr m_fittedParamTable;
+  /// table workspace for fitted parameters' fitting error. This is optional
+  API::ITableWorkspace_sptr m_fitErrorTable;
   /// flag to show that the pamarameters in table are raw parameters or
   /// effective parameters
   bool m_rawPeaksTable;
@@ -269,6 +278,8 @@ private:
   std::string m_costFunction;
   /// Fit from right or left
   bool m_fitPeaksFromRight;
+  /// Fit iterations
+  int m_fitIterations;
 
   //-------- Input param init values --------------------------------
   /// input starting parameters' indexes in peak function
diff --git a/Framework/Algorithms/inc/MantidAlgorithms/MergeRuns.h b/Framework/Algorithms/inc/MantidAlgorithms/MergeRuns.h
index 257bc4f7fae14e2fd8ea716b0dc6523dfd04edad..182a5773d43d25d0afadb68b1fb0b060cc3c722c 100644
--- a/Framework/Algorithms/inc/MantidAlgorithms/MergeRuns.h
+++ b/Framework/Algorithms/inc/MantidAlgorithms/MergeRuns.h
@@ -49,6 +49,17 @@ namespace Algorithms {
     @date 22/09/2008
 */
 
+namespace MergeRunsParameter {
+/// MergeRuns parameter names of the paramter file for sample log merging
+static const std::string SUM_MERGE = "sample_logs_sum";
+static const std::string TIME_SERIES_MERGE = "sample_logs_time_series";
+static const std::string LIST_MERGE = "sample_logs_list";
+static const std::string WARN_MERGE = "sample_logs_warn";
+static const std::string WARN_MERGE_TOLERANCES = "sample_logs_warn_tolerances";
+static const std::string FAIL_MERGE = "sample_logs_fail";
+static const std::string FAIL_MERGE_TOLERANCES = "sample_logs_fail_tolerances";
+} // namespace MergeRunsParameter
+
 class DLLExport MergeRuns : public API::MultiPeriodGroupAlgorithm {
 public:
   /// Algorithm's name for identification overriding a virtual method
diff --git a/Framework/Algorithms/inc/MantidAlgorithms/ParallaxCorrection.h b/Framework/Algorithms/inc/MantidAlgorithms/ParallaxCorrection.h
new file mode 100644
index 0000000000000000000000000000000000000000..0145cfa5bcfcad01c2e1a5b5034d8e6bc6bed8be
--- /dev/null
+++ b/Framework/Algorithms/inc/MantidAlgorithms/ParallaxCorrection.h
@@ -0,0 +1,36 @@
+// Mantid Repository : https://github.com/mantidproject/mantid
+//
+// Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
+//     NScD Oak Ridge National Laboratory, European Spallation Source
+//     & Institut Laue - Langevin
+// SPDX - License - Identifier: GPL - 3.0 +
+#ifndef MANTID_ALGORITHMS_PARALLAXCORRECTION_H_
+#define MANTID_ALGORITHMS_PARALLAXCORRECTION_H_
+
+#include "MantidAPI/Algorithm.h"
+#include "MantidAlgorithms/DllConfig.h"
+
+namespace Mantid {
+namespace Algorithms {
+
+/** ParallaxCorrection : Performs geometrical correction for parallax effect in
+ * tube based SANS instruments.
+ */
+class MANTID_ALGORITHMS_DLL ParallaxCorrection : public API::Algorithm {
+public:
+  const std::string name() const override;
+  int version() const override;
+  const std::string category() const override;
+  const std::string summary() const override;
+
+private:
+  void init() override;
+  void exec() override;
+  void performCorrection(API::MatrixWorkspace_sptr, const std::vector<size_t> &,
+                         const std::string &, const std::string &);
+};
+
+} // namespace Algorithms
+} // namespace Mantid
+
+#endif /* MANTID_ALGORITHMS_PARALLAXCORRECTION_H_ */
diff --git a/Framework/Algorithms/inc/MantidAlgorithms/RunCombinationHelpers/RunCombinationHelper.h b/Framework/Algorithms/inc/MantidAlgorithms/RunCombinationHelpers/RunCombinationHelper.h
index c90429be2f778ee870e30a9225bdddcd1df15b59..91c0b7b18251e30c9af6aca27127b3721bbe7910 100644
--- a/Framework/Algorithms/inc/MantidAlgorithms/RunCombinationHelpers/RunCombinationHelper.h
+++ b/Framework/Algorithms/inc/MantidAlgorithms/RunCombinationHelpers/RunCombinationHelper.h
@@ -19,7 +19,7 @@ namespace Algorithms {
 
 /** RunCombinationHelper : This holds some useful utilities for operations
  * involving transformations of lists of workspaces into single one.
- * E.g. this is used commonly between MergeRuns and JoinRuns
+ * E.g. this is used commonly between MergeRuns and ConjoinXRuns
  */
 
 namespace RunCombinationOptions {
diff --git a/Framework/Algorithms/inc/MantidAlgorithms/RunCombinationHelpers/SampleLogsBehaviour.h b/Framework/Algorithms/inc/MantidAlgorithms/RunCombinationHelpers/SampleLogsBehaviour.h
index 632522a69fc69dfb40005df0484888621dedbc4f..0f9029d21dbfe28945b5bb927ebe53f6b28f49d8 100644
--- a/Framework/Algorithms/inc/MantidAlgorithms/RunCombinationHelpers/SampleLogsBehaviour.h
+++ b/Framework/Algorithms/inc/MantidAlgorithms/RunCombinationHelpers/SampleLogsBehaviour.h
@@ -8,7 +8,9 @@
 #define MANTID_ALGORITHMS_MERGERUNS_SAMPLELOGSBEHAVIOUR_H_
 
 #include "MantidAlgorithms/DllConfig.h"
-#include <MantidAPI/MatrixWorkspace.h>
+#include <MantidAPI/MatrixWorkspace_fwd.h>
+#include <MantidKernel/Logger.h>
+#include <MantidKernel/Property.h>
 
 namespace Mantid {
 namespace Algorithms {
@@ -16,18 +18,24 @@ namespace Algorithms {
 /** SampleLogsBehaviour : This class holds information relating to the
   behaviour of the sample log merging. It holds a map of all the sample log
   parameters to merge, how to merge them, and the associated tolerances.
+  Algorithms which already define paramter names for the instrument parameter
+  file are ConjoinXRuns and MergeRuns. Please use different names for new
+  algorithms.
 */
 class MANTID_ALGORITHMS_DLL SampleLogsBehaviour {
 public:
   enum class MergeLogType { Sum, TimeSeries, List, Warn, Fail };
 
-  static const std::string SUM_MERGE;
-  static const std::string TIME_SERIES_MERGE;
-  static const std::string LIST_MERGE;
-  static const std::string WARN_MERGE;
-  static const std::string FAIL_MERGE;
-  static const std::string WARN_MERGE_TOLERANCES;
-  static const std::string FAIL_MERGE_TOLERANCES;
+  // names of parameters in IPF containing names of sample log entries as values
+  struct ParameterName {
+    std::string SUM_MERGE;
+    std::string TIME_SERIES_MERGE;
+    std::string LIST_MERGE;
+    std::string WARN_MERGE;
+    std::string WARN_MERGE_TOLERANCES;
+    std::string FAIL_MERGE;
+    std::string FAIL_MERGE_TOLERANCES;
+  } parameterNames;
 
   // the names and docs of the override properties
   static const std::string TIME_SERIES_PROP;
@@ -51,22 +59,28 @@ public:
     bool isNumeric;
   };
 
-  SampleLogsBehaviour(API::MatrixWorkspace &ws, Kernel::Logger &logger,
-                      const std::string &sampleLogsSum = "",
-                      const std::string &sampleLogsTimeSeries = "",
-                      const std::string &sampleLogsList = "",
-                      const std::string &sampleLogsWarn = "",
-                      const std::string &sampleLogsWarnTolerances = "",
-                      const std::string &sampleLogsFail = "",
-                      const std::string &sampleLogsFailTolerances = "");
+  // override sample log entries for specific merge type
+  struct SampleLogNames {
+    std::string sampleLogsSum;
+    std::string sampleLogsTimeSeries;
+    std::string sampleLogsList;
+    std::string sampleLogsWarn;
+    std::string sampleLogsWarnTolerances;
+    std::string sampleLogsFail;
+    std::string sampleLogsFailTolerances;
+  };
+
+  SampleLogsBehaviour(API::MatrixWorkspace_sptr ws, Kernel::Logger &logger,
+                      const SampleLogNames &logEntries = {},
+                      const ParameterName &parName = {});
 
   /// Create and update sample logs according to instrument parameters
-  void mergeSampleLogs(API::MatrixWorkspace &addeeWS,
-                       API::MatrixWorkspace &outWS);
-  void setUpdatedSampleLogs(API::MatrixWorkspace &outWS);
-  void removeSampleLogsFromWorkspace(API::MatrixWorkspace &addeeWS);
-  void readdSampleLogToWorkspace(API::MatrixWorkspace &addeeWS);
-  void resetSampleLogs(API::MatrixWorkspace &ws);
+  void mergeSampleLogs(API::MatrixWorkspace_sptr addeeWS,
+                       API::MatrixWorkspace_sptr outWS);
+  void setUpdatedSampleLogs(API::MatrixWorkspace_sptr outWS);
+  void removeSampleLogsFromWorkspace(API::MatrixWorkspace_sptr addeeWS);
+  void readdSampleLogToWorkspace(API::MatrixWorkspace_sptr addeeWS);
+  void resetSampleLogs(API::MatrixWorkspace_sptr ws);
 
 private:
   Kernel::Logger &m_logger;
diff --git a/Framework/Algorithms/src/AbsorptionCorrection.cpp b/Framework/Algorithms/src/AbsorptionCorrection.cpp
index c519f055d22e14000cd554581ae7cf895798691e..f74a8aa03e12675626433206ca857c168a8c8cd1 100644
--- a/Framework/Algorithms/src/AbsorptionCorrection.cpp
+++ b/Framework/Algorithms/src/AbsorptionCorrection.cpp
@@ -247,9 +247,7 @@ void AbsorptionCorrection::retrieveBaseProperties() {
       sigma_atten = sampleMaterial.absorbXSection(NeutronAtom::ReferenceLambda);
   } else // Save input in Sample with wrong atomic number and name
   {
-    NeutronAtom neutron(static_cast<uint16_t>(EMPTY_DBL()),
-                        static_cast<uint16_t>(0), 0.0, 0.0, sigma_s, 0.0,
-                        sigma_s, sigma_atten);
+    NeutronAtom neutron(0, 0, 0.0, 0.0, sigma_s, 0.0, sigma_s, sigma_atten);
 
     auto shape = boost::shared_ptr<IObject>(
         m_inputWS->sample().getShape().cloneWithMaterial(
diff --git a/Framework/Algorithms/src/CalculateCarpenterSampleCorrection.cpp b/Framework/Algorithms/src/CalculateCarpenterSampleCorrection.cpp
index 46beff2a50c6d4018b45081f964494ae20906922..f23a237bd14afb0dbbb4eb45d6fa8dd3b12d992a 100644
--- a/Framework/Algorithms/src/CalculateCarpenterSampleCorrection.cpp
+++ b/Framework/Algorithms/src/CalculateCarpenterSampleCorrection.cpp
@@ -149,9 +149,7 @@ void CalculateCarpenterSampleCorrection::exec() {
       coeff3 = sampleMaterial.totalScatterXSection(LAMBDA_REF);
   } else // Save input in Sample with wrong atomic number and name
   {
-    NeutronAtom neutron(static_cast<uint16_t>(EMPTY_DBL()),
-                        static_cast<uint16_t>(0), 0.0, 0.0, coeff3, 0.0, coeff3,
-                        coeff1);
+    NeutronAtom neutron(0, 0, 0.0, 0.0, coeff3, 0.0, coeff3, coeff1);
     auto shape = boost::shared_ptr<IObject>(
         inputWksp->sample().getShape().cloneWithMaterial(
             Material("SetInMultipleScattering", neutron, coeff2)));
diff --git a/Framework/Algorithms/src/ConjoinXRuns.cpp b/Framework/Algorithms/src/ConjoinXRuns.cpp
index b397f9b5075e36df523f6ce41f04a43bfb0523ae..a690c3904925d7906d2ec2e66b3f737fddbf65c8 100644
--- a/Framework/Algorithms/src/ConjoinXRuns.cpp
+++ b/Framework/Algorithms/src/ConjoinXRuns.cpp
@@ -46,6 +46,17 @@ static const std::string SAMPLE_LOG_X_AXIS_PROPERTY = "SampleLogAsXAxis";
 // Register the algorithm into the AlgorithmFactory
 DECLARE_ALGORITHM(ConjoinXRuns)
 
+const std::string ConjoinXRuns::SUM_MERGE = "conjoin_sample_logs_sum";
+const std::string ConjoinXRuns::TIME_SERIES_MERGE =
+    "conjoin_sample_logs_time_series";
+const std::string ConjoinXRuns::LIST_MERGE = "conjoin_sample_logs_list";
+const std::string ConjoinXRuns::WARN_MERGE = "conjoin_sample_logs_warn";
+const std::string ConjoinXRuns::WARN_MERGE_TOLERANCES =
+    "conjoin_sample_logs_warn_tolerances";
+const std::string ConjoinXRuns::FAIL_MERGE = "conjoin_sample_logs_fail";
+const std::string ConjoinXRuns::FAIL_MERGE_TOLERANCES =
+    "conjoin_sample_logs_fail_tolerances";
+
 //----------------------------------------------------------------------------------------------
 
 /// Algorithms name for identification. @see Algorithm::name
@@ -333,19 +344,17 @@ void ConjoinXRuns::exec() {
       getProperty(INPUT_WORKSPACE_PROPERTY);
   m_logEntry = getPropertyValue(SAMPLE_LOG_X_AXIS_PROPERTY);
 
-  const std::string sampleLogsSum = getProperty(SampleLogsBehaviour::SUM_PROP);
-  const std::string sampleLogsTimeSeries =
-      getProperty(SampleLogsBehaviour::TIME_SERIES_PROP);
-  const std::string sampleLogsList =
-      getProperty(SampleLogsBehaviour::LIST_PROP);
-  const std::string sampleLogsWarn =
-      getProperty(SampleLogsBehaviour::WARN_PROP);
-  const std::string sampleLogsWarnTolerances =
-      getProperty(SampleLogsBehaviour::WARN_TOL_PROP);
-  const std::string sampleLogsFail =
-      getProperty(SampleLogsBehaviour::FAIL_PROP);
-  const std::string sampleLogsFailTolerances =
-      getProperty(SampleLogsBehaviour::FAIL_TOL_PROP);
+  SampleLogsBehaviour::SampleLogNames logEntries = {};
+  logEntries.sampleLogsSum = getPropertyValue(SampleLogsBehaviour::SUM_PROP);
+  logEntries.sampleLogsTimeSeries =
+      getPropertyValue(SampleLogsBehaviour::TIME_SERIES_PROP);
+  logEntries.sampleLogsList = getPropertyValue(SampleLogsBehaviour::LIST_PROP);
+  logEntries.sampleLogsWarn = getPropertyValue(SampleLogsBehaviour::WARN_PROP);
+  logEntries.sampleLogsWarnTolerances =
+      getPropertyValue(SampleLogsBehaviour::WARN_TOL_PROP);
+  logEntries.sampleLogsFail = getPropertyValue(SampleLogsBehaviour::FAIL_PROP);
+  logEntries.sampleLogsFailTolerances =
+      getPropertyValue(SampleLogsBehaviour::FAIL_TOL_PROP);
   const std::string sampleLogsFailBehaviour = getProperty("FailBehaviour");
 
   m_inputWS.clear();
@@ -357,11 +366,18 @@ void ConjoinXRuns::exec() {
   }
 
   auto first = m_inputWS.front();
-  SampleLogsBehaviour sampleLogsBehaviour = SampleLogsBehaviour(
-      *first, g_log, sampleLogsSum, sampleLogsTimeSeries, sampleLogsList,
-      sampleLogsWarn, sampleLogsWarnTolerances, sampleLogsFail,
-      sampleLogsFailTolerances);
 
+  SampleLogsBehaviour::ParameterName parName = {
+      ConjoinXRuns::SUM_MERGE,
+      ConjoinXRuns::TIME_SERIES_MERGE,
+      ConjoinXRuns::LIST_MERGE,
+      ConjoinXRuns::WARN_MERGE,
+      ConjoinXRuns::WARN_MERGE_TOLERANCES,
+      ConjoinXRuns::FAIL_MERGE,
+      ConjoinXRuns::FAIL_MERGE_TOLERANCES};
+
+  SampleLogsBehaviour sampleLogsBehaviour =
+      SampleLogsBehaviour(first, g_log, logEntries, parName);
   auto it = m_inputWS.begin();
 
   // Temporary workspace to carry the merged sample logs
@@ -369,21 +385,21 @@ void ConjoinXRuns::exec() {
   // the correct size to be the output, since the size is unknown
   // at this point. We can check only later which ones are going
   // to be skipped, to compute the size of the output respectively.
-  MatrixWorkspace_uptr temp = first->clone();
+  MatrixWorkspace_sptr temp = first->clone();
 
   size_t outBlockSize = (*it)->blocksize();
   // First sequentially merge the sample logs
   for (++it; it != m_inputWS.end(); ++it) {
     // attempt to merge the sample logs
     try {
-      sampleLogsBehaviour.mergeSampleLogs(**it, *temp);
-      sampleLogsBehaviour.setUpdatedSampleLogs(*temp);
+      sampleLogsBehaviour.mergeSampleLogs(*it, temp);
+      sampleLogsBehaviour.setUpdatedSampleLogs(temp);
       outBlockSize += (*it)->blocksize();
     } catch (std::invalid_argument &e) {
       if (sampleLogsFailBehaviour == SKIP_BEHAVIOUR) {
         g_log.error() << "Could not join workspace: " << (*it)->getName()
                       << ". Reason: \"" << e.what() << "\". Skipping.\n";
-        sampleLogsBehaviour.resetSampleLogs(*temp);
+        sampleLogsBehaviour.resetSampleLogs(temp);
         // remove the skipped one from the list
         m_inputWS.erase(it);
         --it;
diff --git a/Framework/Algorithms/src/DeadTimeCorrection.cpp b/Framework/Algorithms/src/DeadTimeCorrection.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0cf516d4e419424eb297dc29c0e9dbce7ab5c3f5
--- /dev/null
+++ b/Framework/Algorithms/src/DeadTimeCorrection.cpp
@@ -0,0 +1,132 @@
+// Mantid Repository : https://github.com/mantidproject/mantid
+//
+// Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
+//     NScD Oak Ridge National Laboratory, European Spallation Source
+//     & Institut Laue - Langevin
+// SPDX - License - Identifier: GPL - 3.0 +
+#include "MantidAlgorithms/DeadTimeCorrection.h"
+#include "MantidAPI/MatrixWorkspace.h"
+#include "MantidAPI/Progress.h"
+#include "MantidKernel/BoundedValidator.h"
+#include "MantidKernel/PropertyWithValue.h"
+
+#include <limits>
+
+namespace Mantid {
+namespace Algorithms {
+
+using API::MatrixWorkspace_sptr;
+using API::Progress;
+using API::WorkspaceProperty;
+using Kernel::BoundedValidator;
+using Kernel::Direction;
+using Kernel::PropertyWithValue;
+
+// Register the algorithm into the AlgorithmFactory
+DECLARE_ALGORITHM(DeadTimeCorrection)
+
+//----------------------------------------------------------------------------------------------
+
+/// Algorithms name for identification. @see Algorithm::name
+const std::string DeadTimeCorrection::name() const {
+  return "DeadTimeCorrection";
+}
+
+/// Algorithm's version for identification. @see Algorithm::version
+int DeadTimeCorrection::version() const { return 1; }
+
+/// Algorithm's category for identification. @see Algorithm::category
+const std::string DeadTimeCorrection::category() const {
+  return "CorrectionFunctions";
+}
+
+/// Algorithm's summary for use in the GUI and help. @see Algorithm::summary
+const std::string DeadTimeCorrection::summary() const {
+  return "Performs a dead time correction based on count rate.";
+}
+
+//----------------------------------------------------------------------------------------------
+/** Initialize the algorithm's properties.
+ */
+void DeadTimeCorrection::init() {
+  declareProperty(Kernel::make_unique<WorkspaceProperty<>>("InputWorkspace", "",
+                                                           Direction::Input),
+                  "An input workspace.");
+
+  declareProperty(Kernel::make_unique<PropertyWithValue<std::string>>(
+                      "GroupingPattern", "", Direction::Input),
+                  "See the GroupingPattern documentation of GroupDetectors.");
+
+  auto positive = boost::make_shared<BoundedValidator<double>>();
+  positive->setLower(0.);
+  declareProperty("Tau", 0., positive, "The count rate coefficient.");
+
+  declareProperty(Kernel::make_unique<WorkspaceProperty<>>(
+                      "OutputWorkspace", "", Direction::Output),
+                  "An output workspace.");
+}
+
+//----------------------------------------------------------------------------------------------
+/** Execute the algorithm.
+ */
+void DeadTimeCorrection::exec() {
+  MatrixWorkspace_sptr inputWorkspace = getProperty("InputWorkspace");
+  MatrixWorkspace_sptr outputWorkspace = getProperty("OutputWorkspace");
+  if (inputWorkspace != outputWorkspace) {
+    outputWorkspace = inputWorkspace->clone();
+  }
+  const auto map = outputWorkspace->getDetectorIDToWorkspaceIndexMap();
+  const double tau = getProperty("Tau");
+  MatrixWorkspace_sptr integrated = inputWorkspace;
+  if (outputWorkspace->blocksize() != 1) {
+    auto integrator = createChildAlgorithm("Integration");
+    integrator->setProperty("InputWorkspace", inputWorkspace);
+    integrator->setPropertyValue("OutputWorkspace", "unused");
+    integrator->executeAsChildAlg();
+    integrated = integrator->getProperty("OutputWorkspace");
+  }
+  const std::string groupingPattern = getProperty("GroupingPattern");
+  MatrixWorkspace_sptr grouped = integrated;
+  if (!groupingPattern.empty()) {
+    auto grouper = createChildAlgorithm("GroupDetectors");
+    grouper->setProperty("InputWorkspace", integrated);
+    grouper->setPropertyValue("OutputWorkspace", "unused");
+    grouper->setPropertyValue("GroupingPattern", groupingPattern);
+    grouper->setPropertyValue("Behaviour", "Sum");
+    grouper->setProperty("KeepUngroupedSpectra", true);
+    grouper->executeAsChildAlg();
+    grouped = grouper->getProperty("OutputWorkspace");
+  }
+  Progress progress(this, 0.0, 1.0, grouped->getNumberHistograms());
+  PARALLEL_FOR_IF(Kernel::threadSafe(*outputWorkspace))
+  for (int index = 0; index < static_cast<int>(grouped->getNumberHistograms());
+       ++index) {
+    PARALLEL_START_INTERUPT_REGION
+    progress.report("Performing the correction for the group at index " +
+                    std::to_string(index));
+    const double y = grouped->y(index)[0];
+    double correction = 1. / (1. - tau * y);
+    if (y >= 1. / tau) {
+      g_log.warning()
+          << "Saturation count rate reached for grouped detector at index "
+          << index
+          << ". Correction will be infinity. Check your tau or input "
+             "workspace, make sure it is normalised by acquisition time.\n";
+      correction = std::numeric_limits<double>::infinity();
+    }
+    const auto detIDs = grouped->getSpectrum(index).getDetectorIDs();
+    for (const auto id : detIDs) {
+      const size_t originalIndex = map.at(id);
+      auto &spectrum = outputWorkspace->mutableY(originalIndex);
+      auto &errors = outputWorkspace->mutableE(originalIndex);
+      spectrum *= correction;
+      errors *= correction;
+    }
+    PARALLEL_END_INTERUPT_REGION
+  }
+  PARALLEL_CHECK_INTERUPT_REGION
+  setProperty("OutputWorkspace", outputWorkspace);
+}
+
+} // namespace Algorithms
+} // namespace Mantid
diff --git a/Framework/Algorithms/src/FitPeaks.cpp b/Framework/Algorithms/src/FitPeaks.cpp
index 6092e3c2380e3623b1dfdcf6576ccea920cea5c0..8cf6bd7bebae2de3d0e24e50de51c159d05596c4 100644
--- a/Framework/Algorithms/src/FitPeaks.cpp
+++ b/Framework/Algorithms/src/FitPeaks.cpp
@@ -58,9 +58,12 @@ PeakFitResult::PeakFitResult(size_t num_peaks, size_t num_params)
                                  std::numeric_limits<double>::quiet_NaN());
   m_costs.resize(num_peaks, DBL_MAX);
   m_function_parameters_vector.resize(num_peaks);
+  m_function_errors_vector.resize(num_peaks);
   for (size_t ipeak = 0; ipeak < num_peaks; ++ipeak) {
     m_function_parameters_vector[ipeak].resize(
         num_params, std::numeric_limits<double>::quiet_NaN());
+    m_function_errors_vector[ipeak].resize(
+        num_params, std::numeric_limits<double>::quiet_NaN());
   }
 
   return;
@@ -75,6 +78,24 @@ size_t PeakFitResult::getNumberPeaks() const {
   return m_function_parameters_vector.size();
 }
 
+//----------------------------------------------------------------------------------------------
+/** get the fitting error of a particular parameter
+ * @param ipeak :: index of the peak in given peak position vector
+ * @param iparam :: index of the parameter in its corresponding peak profile
+ * function
+ * @return :: fitting error/uncertain of the specified parameter
+ */
+double PeakFitResult::getParameterError(size_t ipeak, size_t iparam) const {
+  return m_function_errors_vector[ipeak][iparam];
+}
+
+//----------------------------------------------------------------------------------------------
+/** get the fitted value of a particular parameter
+ * @param ipeak :: index of the peak in given peak position vector
+ * @param iparam :: index of the parameter in its corresponding peak profile
+ * function
+ * @return :: fitted value of the specified parameter
+ */
 double PeakFitResult::getParameterValue(size_t ipeak, size_t iparam) const {
   return m_function_parameters_vector[ipeak][iparam];
 }
@@ -108,15 +129,23 @@ void PeakFitResult::setRecord(size_t ipeak, const double cost,
     // peak function
     m_function_parameters_vector[ipeak][ipar] =
         fit_functions.peakfunction->getParameter(ipar);
+    m_function_errors_vector[ipeak][ipar] =
+        fit_functions.peakfunction->getError(ipar);
   }
   for (size_t ipar = 0; ipar < fit_functions.bkgdfunction->nParams(); ++ipar) {
     // background function
     m_function_parameters_vector[ipeak][ipar + peak_num_params] =
         fit_functions.bkgdfunction->getParameter(ipar);
+    m_function_errors_vector[ipeak][ipar + peak_num_params] =
+        fit_functions.bkgdfunction->getError(ipar);
   }
 }
 
-/// The peak postition should be negative and indicates what went wrong
+//----------------------------------------------------------------------------------------------
+/** The peak postition should be negative and indicates what went wrong
+ * @param ipeak :: index of the peak in user-specified peak position vector
+ * @param peak_position :: bad peak position indicating reason of bad fit
+ */
 void PeakFitResult::setBadRecord(size_t ipeak, const double peak_position) {
   // check input
   if (ipeak >= m_costs.size())
@@ -133,6 +162,8 @@ void PeakFitResult::setBadRecord(size_t ipeak, const double peak_position) {
   // transfer from peak function to vector
   for (size_t ipar = 0; ipar < m_function_parameters_number; ++ipar) {
     m_function_parameters_vector[ipeak][ipar] = 0.;
+    m_function_errors_vector[ipeak][ipar] =
+        std::numeric_limits<double>::quiet_NaN();
   }
 }
 } // namespace FitPeaksAlgorithm
@@ -165,8 +196,8 @@ enum PeakFitResult { NOSIGNAL, LOWPEAK, OUTOFBOUND, GOOD };
 
 //----------------------------------------------------------------------------------------------
 FitPeaks::FitPeaks()
-    : m_fitPeaksFromRight(true), m_numPeaksToFit(0), m_minPeakHeight(20.),
-      m_bkgdSimga(1.), m_peakPosTolCase234(false) {}
+    : m_fitPeaksFromRight(true), m_fitIterations(50), m_numPeaksToFit(0),
+      m_minPeakHeight(20.), m_bkgdSimga(1.), m_peakPosTolCase234(false) {}
 
 //----------------------------------------------------------------------------------------------
 /** initialize the properties
@@ -288,6 +319,11 @@ void FitPeaks::init() {
                       new Kernel::ListValidator<std::string>(costFuncOptions)),
                   "Cost functions");
 
+  auto min_max_iter = boost::make_shared<BoundedValidator<int>>();
+  min_max_iter->setLower(49);
+  declareProperty("MaxFitIterations", 50, min_max_iter,
+                  "Maximum number of function fitting iterations.");
+
   std::string optimizergrp("Optimization Setup");
   setPropertyGroup("Minimizer", optimizergrp);
   setPropertyGroup("CostFunction", optimizergrp);
@@ -328,10 +364,20 @@ void FitPeaks::init() {
       "The Y values belonged to peaks to fit are replaced by fitted value. "
       "Values of estimated background are used if peak fails to be fit.");
 
-  declareProperty(Kernel::make_unique<WorkspaceProperty<API::ITableWorkspace>>(
-                      "OutputPeakParametersWorkspace", "", Direction::Output),
-                  "Name of workspace containing all fitted peak parameters.  "
-                  "X-values are spectra/workspace index.");
+  declareProperty(
+      Kernel::make_unique<WorkspaceProperty<API::ITableWorkspace>>(
+          "OutputPeakParametersWorkspace", "", Direction::Output),
+      "Name of table workspace containing all fitted peak parameters.");
+
+  // Optional output table workspace for each individual parameter's fitting
+  // error
+  declareProperty(
+      Kernel::make_unique<WorkspaceProperty<API::ITableWorkspace>>(
+          "OutputParameterFitErrorsWorkspace", "", Direction::Output,
+          PropertyMode::Optional),
+      "Name of workspace containing all fitted peak parameters' fitting error."
+      "It must be used along with FittedPeaksWorkspace and RawPeakParameters "
+      "(True)");
 
   declareProperty("RawPeakParameters", true,
                   "false generates table with effective centre/width/height "
@@ -341,11 +387,15 @@ void FitPeaks::init() {
   std::string addoutgrp("Analysis");
   setPropertyGroup("OutputPeakParametersWorkspace", addoutgrp);
   setPropertyGroup("FittedPeaksWorkspace", addoutgrp);
+  setPropertyGroup("OutputParameterFitErrorsWorkspace", addoutgrp);
   setPropertyGroup("RawPeakParameters", addoutgrp);
 
   return;
 }
 
+//----------------------------------------------------------------------------------------------
+/** Validate inputs
+ */
 std::map<std::string, std::string> FitPeaks::validateInputs() {
   map<std::string, std::string> issues;
 
@@ -410,6 +460,19 @@ std::map<std::string, std::string> FitPeaks::validateInputs() {
     }
   }
 
+  // check inputs for uncertainty (fitting error)
+  const std::string error_table_name =
+      getPropertyValue("OutputParameterFitErrorsWorkspace");
+  if (!error_table_name.empty()) {
+    const bool use_raw_params = getProperty("RawPeakParameters");
+    if (!use_raw_params) {
+      const std::string msg =
+          "FitPeaks must output RAW peak parameters if fitting error "
+          "is chosen to be output";
+      issues["RawPeakParameters"] = msg;
+    }
+  }
+
   return issues;
 }
 
@@ -420,7 +483,10 @@ void FitPeaks::exec() {
 
   // create output workspaces
   generateOutputPeakPositionWS();
-  generateFittedParametersValueWorkspace();
+
+  // generateFittedParametersValueWorkspace();
+  generateFittedParametersValueWorkspaces();
+
   generateCalculatedPeaksWS();
 
   // fit peaks
@@ -462,6 +528,7 @@ void FitPeaks::processInputs() {
   m_costFunction = getPropertyValue("CostFunction");
   m_fitPeaksFromRight = getProperty("FitFromRight");
   m_constrainPeaksPosition = getProperty("ConstrainPeakPositions");
+  m_fitIterations = getProperty("MaxFitIterations");
 
   // Peak centers, tolerance and fitting range
   processInputPeakCenters();
@@ -635,7 +702,7 @@ void FitPeaks::processInputFitRanges() {
         errss << "Peak window workspace index " << wi
               << " has incompatible number of fit windows (x2) "
               << m_peakWindowWorkspace->y(wi).size()
-              << "with the number of peaks " << m_numPeaksToFit << " to fit.";
+              << " with the number of peaks " << m_numPeaksToFit << " to fit.";
         throw std::invalid_argument(errss.str());
       }
 
@@ -706,16 +773,34 @@ void FitPeaks::processInputPeakCenters() {
     m_peakCenterWorkspace = getProperty("PeakCentersWorkspace");
     // number of peaks to fit!
     m_numPeaksToFit = m_peakCenterWorkspace->x(0).size();
+    g_log.warning() << "Input peak center workspace: "
+                    << m_peakCenterWorkspace->x(0).size() << ", "
+                    << m_peakCenterWorkspace->y(0).size() << "\n";
 
     // check matrix worksapce for peak positions
-    const size_t numhist = m_peakCenterWorkspace->getNumberHistograms();
-    if (numhist == m_inputMatrixWS->size())
+    const size_t peak_center_ws_spectra_number =
+        m_peakCenterWorkspace->getNumberHistograms();
+    if (peak_center_ws_spectra_number ==
+        m_inputMatrixWS->getNumberHistograms()) {
+      // full spectra
       m_partialSpectra = false;
-    else if (numhist == m_stopWorkspaceIndex - m_startWorkspaceIndex + 1)
+    } else if (peak_center_ws_spectra_number ==
+               m_stopWorkspaceIndex - m_startWorkspaceIndex + 1) {
+      // partial spectra
       m_partialSpectra = true;
-    else
-      throw std::invalid_argument(
-          "Input peak center workspace has wrong number of spectra.");
+    } else {
+      // a case indicating programming error
+      g_log.error() << "Peak center workspace has "
+                    << peak_center_ws_spectra_number << " spectra;"
+                    << "Input workspace has "
+                    << m_inputMatrixWS->getNumberHistograms() << " spectra;"
+                    << "User specifies to fit peaks from "
+                    << m_startWorkspaceIndex << " to " << m_stopWorkspaceIndex
+                    << ".  They are mismatched to each other.\n";
+      throw std::invalid_argument("Input peak center workspace has mismatched "
+                                  "number of spectra to selected spectra to "
+                                  "fit.");
+    }
 
   } else {
     std::stringstream errss;
@@ -870,6 +955,13 @@ double numberCounts(const Histogram &histogram) {
   return total;
 }
 
+//----------------------------------------------------------------------------------------------
+/** Get number of counts in a specified range of a histogram
+ * @param histogram :: histogram instance
+ * @param xmin :: left boundary
+ * @param xmax :: right boundary
+ * @return :: counts
+ */
 double numberCounts(const Histogram &histogram, const double xmin,
                     const double xmax) {
   const auto &vector_x = histogram.points();
@@ -1000,6 +1092,10 @@ void FitPeaks::fitSpectrumPeaks(
 //----------------------------------------------------------------------------------------------
 /** Decide whether to estimate peak width.  If not, then set the width related
  * peak parameters from user specified starting value
+ * @param firstPeakInSpectrum :: flag whether the given peak is the first peak
+ * in the spectrum
+ * @param peak_function :: peak function to set parameter values to
+ * @return :: flag whether the peak width shall be observed
  */
 bool FitPeaks::decideToEstimatePeakWidth(
     const bool firstPeakInSpectrum, API::IPeakFunction_sptr peak_function) {
@@ -1008,7 +1104,7 @@ bool FitPeaks::decideToEstimatePeakWidth(
   if (!m_initParamIndexes.empty()) {
     // user specifies starting value of peak parameters
     if (firstPeakInSpectrum) {
-      // TODO just set the parameter values in a vector and loop over it
+      // set the parameter values in a vector and loop over it
       // first peak.  using the user-specified value
       for (size_t i = 0; i < m_initParamIndexes.size(); ++i) {
         size_t param_index = m_initParamIndexes[i];
@@ -1020,7 +1116,7 @@ bool FitPeaks::decideToEstimatePeakWidth(
       // do noting
     }
   } else {
-    // by observation
+    // no previously defined peak parameters: observation is thus required
     observe_peak_width = true;
   }
 
@@ -1030,6 +1126,13 @@ bool FitPeaks::decideToEstimatePeakWidth(
 //----------------------------------------------------------------------------------------------
 /** retrieve the fitted peak information from functions and set to output
  * vectors
+ * @param wsindex :: workspace index
+ * @param peakindex :: index of peak in given peak position vector
+ * @param cost :: cost function value (i.e., chi^2)
+ * @param expected_peak_positions :: vector of the expected peak positions
+ * @param fitfunction :: pointer to function to retrieve information from
+ * @param fit_result :: (output) PeakFitResult instance to set the fitting
+ * result to
  */
 void FitPeaks::processSinglePeakFitResult(
     size_t wsindex, size_t peakindex, const double cost,
@@ -1288,9 +1391,18 @@ void FitPeaks::estimateBackground(const Histogram &histogram,
 }
 
 //----------------------------------------------------------------------------------------------
-/**  Estimate peak profile's parameters values via observation
+/** Estimate peak profile's parameters values via observation
  * including
  * (1) peak center (2) peak intensity  (3) peak width depending on peak type
+ * In order to make the estimation better, a pre-defined background function is
+ * used to remove
+ * the background from the obervation data
+ * @param histogram :: Histogram instance containing experimental data
+ * @param peak_window :: pair of X-value to specify the peak range
+ * @param peakfunction :: (in/out) peak function to set observed value to
+ * @param bkgdfunction :: background function previously defined
+ * @param observe_peak_width :: flag to estimate peak width from input data
+ * @return :: obervation success or not
  */
 int FitPeaks::estimatePeakParameters(
     const Histogram &histogram, const std::pair<double, double> &peak_window,
@@ -1353,7 +1465,7 @@ int FitPeaks::estimatePeakParameters(
 /** check whether a peak profile is allowed to observe peak width and set width
  * @brief isObservablePeakProfile
  * @param peakprofile : name of peak profile to check against
- * @return
+ * @return :: flag whether the specified peak profile observable
  */
 bool FitPeaks::isObservablePeakProfile(const std::string &peakprofile) {
   return (std::find(supported_peak_profiles.begin(),
@@ -1362,7 +1474,18 @@ bool FitPeaks::isObservablePeakProfile(const std::string &peakprofile) {
 }
 
 //----------------------------------------------------------------------------------------------
-/// Guess/estimate peak center and thus height by observation
+/** Guess/estimate peak center and thus height by observation
+ * @param histogram :: Histogram instance
+ * @param bkgd_values :: calculated background value to removed from observed
+ * data
+ * @param start_index :: X index of the left boundary of observation widow
+ * @param stop_index :: X index of the right boundary of the observation window
+ * @param peak_center :: estiamted peak center (output)
+ * @param peak_center_index :: estimated peak center's index in histogram
+ * (output)
+ * @param peak_height :: estimated peak height (output)
+ * @return :: state whether peak center can be found by obervation
+ */
 int FitPeaks::observePeakCenter(const Histogram &histogram,
                                 FunctionValues &bkgd_values, size_t start_index,
                                 size_t stop_index, double &peak_center,
@@ -1408,7 +1531,13 @@ int FitPeaks::observePeakCenter(const Histogram &histogram,
 }
 
 //----------------------------------------------------------------------------------------------
-/**
+/** estimate peak width from 'observation'
+ * @param histogram :: Histogram instance
+ * @param bkgd_values :: (output) background values calculated from X in given
+ * histogram
+ * @param ipeak :: array index for the peak center in histogram
+ * @param istart :: array index for the left boundary of the peak
+ * @param istop :: array index for the right boundary of the peak
  * @return peak width as double
  */
 double FitPeaks::observePeakWidth(const Histogram &histogram,
@@ -1430,8 +1559,6 @@ double FitPeaks::observePeakWidth(const Histogram &histogram,
     else
       peak_width = std::sqrt(moments[2]);
 
-    //    moments = calculateMomentsAboutMean(
-    //        histogram, histogram.points()[ipeak], bkgd_values, istart, istop);
   } else {
     // get from last peak or from input!
     throw std::runtime_error(
@@ -1573,7 +1700,7 @@ double FitPeaks::fitFunctionSD(IAlgorithm_sptr fit,
   fit->setProperty("Function", fitfunc);
   fit->setProperty("InputWorkspace", dataws);
   fit->setProperty("WorkspaceIndex", static_cast<int>(wsindex));
-  fit->setProperty("MaxIterations", 50); // magic number
+  fit->setProperty("MaxIterations", m_fitIterations); // magic number
   fit->setProperty("StartX", xmin);
   fit->setProperty("EndX", xmax);
 
@@ -1587,7 +1714,6 @@ double FitPeaks::fitFunctionSD(IAlgorithm_sptr fit,
                            << (peak_center + 0.5 * peak_width);
 
     // set up a constraint on peak height
-
     fit->setProperty("Constraints", peak_center_constraint.str());
   }
 
@@ -1614,10 +1740,9 @@ double FitPeaks::fitFunctionSD(IAlgorithm_sptr fit,
 
   // Retrieve result
   std::string fitStatus = fit->getProperty("OutputStatus");
-  double chi2 = DBL_MAX;
+  double chi2{std::numeric_limits<double>::max()};
   if (fitStatus == "success") {
     chi2 = fit->getProperty("OutputChi2overDoF");
-    fitfunc = fit->getProperty("Function");
   }
 
   return chi2;
@@ -1672,7 +1797,7 @@ double FitPeaks::fitFunctionMD(API::IFunction_sptr fit_function,
   fit->setProperty("WorkspaceIndex_1", static_cast<int>(wsindex));
   fit->setProperty("StartX_1", vec_xmin[1]);
   fit->setProperty("EndX_1", vec_xmax[1]);
-  fit->setProperty("MaxIterations", 50);
+  fit->setProperty("MaxIterations", m_fitIterations);
 
   // Execute
   fit->execute();
@@ -1763,6 +1888,8 @@ FitPeaks::createMatrixWorkspace(const std::vector<double> &vec_x,
 }
 
 //----------------------------------------------------------------------------------------------
+/** generate output workspace for peak positions
+ */
 void FitPeaks::generateOutputPeakPositionWS() {
   // create output workspace for peak positions: can be partial spectra to input
   // workspace
@@ -1771,8 +1898,6 @@ void FitPeaks::generateOutputPeakPositionWS() {
       "Workspace2D", num_hist, m_numPeaksToFit, m_numPeaksToFit);
   // set default
   for (size_t wi = 0; wi < num_hist; ++wi) {
-    // TODO - Parallization OpenMP
-
     // convert to workspace index of input data workspace
     size_t inp_wi = wi + m_startWorkspaceIndex;
     std::vector<double> expected_position = getExpectedPeakPositions(inp_wi);
@@ -1786,48 +1911,35 @@ void FitPeaks::generateOutputPeakPositionWS() {
 }
 
 //----------------------------------------------------------------------------------------------
-void FitPeaks::generateFittedParametersValueWorkspace() {
-  // peak parameter workspace
-  m_rawPeaksTable = getProperty("RawPeakParameters");
+/** Set up parameter table (parameter value or error)
+ * @brief FitPeaks::generateParameterTable
+ * @param table_ws:: an empty workspace
+ * @param param_names
+ * @param with_chi2:: flag to append chi^2 to the table
+ */
+void FitPeaks::setupParameterTableWorkspace(
+    API::ITableWorkspace_sptr table_ws,
+    const std::vector<std::string> &param_names, bool with_chi2) {
 
-  // create
-  m_fittedParamTable =
-      WorkspaceFactory::Instance().createTable("TableWorkspace");
   // add columns
-  m_fittedParamTable->addColumn("int", "wsindex");
-  m_fittedParamTable->addColumn("int", "peakindex");
-  // peaks
-  if (m_rawPeaksTable) {
-    // raw parameters for output table workspace
-    for (size_t iparam = 0; iparam < m_peakFunction->nParams(); ++iparam)
-      m_fittedParamTable->addColumn("double",
-                                    m_peakFunction->parameterName(iparam));
-  } else {
-    // effective parameters or output table worspace
-    m_fittedParamTable->addColumn("double", "centre");
-    m_fittedParamTable->addColumn("double", "width");
-    m_fittedParamTable->addColumn("double", "height");
-    m_fittedParamTable->addColumn("double", "intensity");
-  }
-  // background
-  for (size_t iparam = 0; iparam < m_bkgdFunction->nParams(); ++iparam)
-    m_fittedParamTable->addColumn("double",
-                                  m_bkgdFunction->parameterName(iparam));
-  // chi^2
-  m_fittedParamTable->addColumn("double", "chi2");
-
-  const size_t numParam = m_fittedParamTable->columnCount() - 3;
-  // replace: m_peakFunction->nParams() + m_bkgdFunction->nParams();
+  table_ws->addColumn("int", "wsindex");
+  table_ws->addColumn("int", "peakindex");
+  for (size_t iparam = 0; iparam < param_names.size(); ++iparam)
+    table_ws->addColumn("double", param_names[iparam]);
+  if (with_chi2)
+    table_ws->addColumn("double", "chi2");
 
   // add rows
+  const size_t numParam = m_fittedParamTable->columnCount() - 3;
   for (size_t iws = m_startWorkspaceIndex; iws <= m_stopWorkspaceIndex; ++iws) {
     for (size_t ipeak = 0; ipeak < m_numPeaksToFit; ++ipeak) {
-      API::TableRow newRow = m_fittedParamTable->appendRow();
+      API::TableRow newRow = table_ws->appendRow();
       newRow << static_cast<int>(iws);   // workspace index
       newRow << static_cast<int>(ipeak); // peak number
       for (size_t iparam = 0; iparam < numParam; ++iparam)
-        newRow << 0.;    // parameters for each peak
-      newRow << DBL_MAX; // chisq
+        newRow << 0.; // parameters for each peak
+      if (with_chi2)
+        newRow << DBL_MAX; // chisq
     }
   }
 
@@ -1835,7 +1947,57 @@ void FitPeaks::generateFittedParametersValueWorkspace() {
 }
 
 //----------------------------------------------------------------------------------------------
-/// Generate the output MatrixWorkspace for calculated peaks
+/** Generate table workspace for fitted parameters' value
+ * and optionally the table workspace for those parameters' fitting error
+ * @brief FitPeaks::generateFittedParametersValueWorkspace
+ */
+void FitPeaks::generateFittedParametersValueWorkspaces() {
+  // peak parameter workspace
+  m_rawPeaksTable = getProperty("RawPeakParameters");
+
+  // create parameters
+  // peak
+  std::vector<std::string> param_vec;
+  if (m_rawPeaksTable) {
+    std::vector<std::string> peak_params = m_peakFunction->getParameterNames();
+    for (size_t i = 0; i < peak_params.size(); ++i)
+      param_vec.push_back(peak_params[i]);
+  } else {
+    param_vec.emplace_back("centre");
+    param_vec.emplace_back("width");
+    param_vec.emplace_back("height");
+    param_vec.emplace_back("intensity");
+  }
+  // background
+  for (size_t iparam = 0; iparam < m_bkgdFunction->nParams(); ++iparam)
+    param_vec.emplace_back(m_bkgdFunction->parameterName(iparam));
+
+  // parameter value table
+  m_fittedParamTable =
+      WorkspaceFactory::Instance().createTable("TableWorkspace");
+  setupParameterTableWorkspace(m_fittedParamTable, param_vec, true);
+
+  // for error workspace
+  std::string fiterror_table_name =
+      getPropertyValue("OutputParameterFitErrorsWorkspace");
+  // do nothing if user does not specifiy
+  if (fiterror_table_name.empty()) {
+    // not specified
+    m_fitErrorTable = nullptr;
+  } else {
+    // create table and set up parameter table
+    m_fitErrorTable =
+        WorkspaceFactory::Instance().createTable("TableWorkspace");
+    setupParameterTableWorkspace(m_fitErrorTable, param_vec, false);
+  }
+
+  return;
+}
+
+//----------------------------------------------------------------------------------------------
+/** Generate the output MatrixWorkspace for calculated peaks (as an option)
+ * @brief FitPeaks::generateCalculatedPeaksWS
+ */
 void FitPeaks::generateCalculatedPeaksWS() {
   // matrix workspace contained calculated peaks from fitting
   std::string fit_ws_name = getPropertyValue("FittedPeaksWorkspace");
@@ -1845,7 +2007,7 @@ void FitPeaks::generateCalculatedPeaksWS() {
     return;
   }
 
-  // create
+  // create a wokspace with same number of input matrix workspace
   m_fittedPeakWS = API::WorkspaceFactory::Instance().create(m_inputMatrixWS);
   for (size_t iws = 0; iws < m_fittedPeakWS->getNumberHistograms(); ++iws) {
     auto out_vecx = m_fittedPeakWS->histogram(iws).x();
@@ -1864,10 +2026,14 @@ void FitPeaks::processOutputs(
     std::vector<boost::shared_ptr<FitPeaksAlgorithm::PeakFitResult>>
         fit_result_vec) {
   setProperty("OutputWorkspace", m_outputPeakPositionWorkspace);
+  setProperty("OutputPeakParametersWorkspace", m_fittedParamTable);
 
-  // optional
-  if (m_fittedParamTable)
-    setProperty("OutputPeakParametersWorkspace", m_fittedParamTable);
+  if (m_fitErrorTable) {
+    g_log.warning("Output error table workspace");
+    setProperty("OutputParameterFitErrorsWorkspace", m_fitErrorTable);
+  } else {
+    g_log.warning("No error table output");
+  }
 
   // optional
   if (m_fittedPeakWS && m_fittedParamTable) {
@@ -2019,8 +2185,11 @@ void FitPeaks::getRangeData(size_t iws,
 }
 
 //----------------------------------------------------------------------------------------------
-// find 2 local minima: draw a line as background to reduce
-// find 1 local minima: a flat background
+/** Reduce Y value with given background function
+ * @param bkgd_func :: bacground function pointer
+ * @param vec_x :: vector of X valye
+ * @param vec_y :: (input/output) vector Y to be reduced by background function
+ */
 void FitPeaks::reduceByBackground(API::IBackgroundFunction_sptr bkgd_func,
                                   const std::vector<double> &vec_x,
                                   std::vector<double> &vec_y) {
@@ -2038,8 +2207,17 @@ void FitPeaks::reduceByBackground(API::IBackgroundFunction_sptr bkgd_func,
   return;
 }
 
-/// This assumes that there are significantly more background bins than peak
-/// bins
+//----------------------------------------------------------------------------------------------
+/** Estimate linear background from observation in a given fit window
+ *  This assumes that there are significantly more background bins than peak
+ *  bins
+ * @param histogram :: Histogram instance
+ * @param left_window_boundary :: x value as the left boundary of the fit window
+ * @param right_window_boundary :: x value as the right boundary of the fit
+ * window
+ * @param bkgd_a0 :: constant factor (output)
+ * @param bkgd_a1 :: linear factor (output)
+ */
 void FitPeaks::estimateLinearBackground(const Histogram &histogram,
                                         double left_window_boundary,
                                         double right_window_boundary,
@@ -2065,7 +2243,14 @@ void FitPeaks::estimateLinearBackground(const Histogram &histogram,
 }
 
 //----------------------------------------------------------------------------------------------
-///  Write result of peak fit per spectrum to output analysis workspaces
+/** Write result of peak fit per spectrum to output analysis workspaces
+ * including (1) output peak position workspace (2) parameter table workspace
+ * and optionally (3) fitting error/uncertainty workspace
+ * @brief FitPeaks::writeFitResult
+ * @param wi
+ * @param expected_positions :: vector for expected peak positions
+ * @param fit_result :: PeakFitResult instance
+ */
 void FitPeaks::writeFitResult(
     size_t wi, const std::vector<double> &expected_positions,
     boost::shared_ptr<FitPeaksAlgorithm::PeakFitResult> fit_result) {
@@ -2093,10 +2278,6 @@ void FitPeaks::writeFitResult(
     m_outputPeakPositionWorkspace->mutableE(out_wi)[ipeak] = peak_chi2;
   }
 
-  // return if it is not asked to write fitted peak parameters
-  if (!m_fittedParamTable)
-    return;
-
   // Output the peak parameters to the table workspace
   // check vector size
 
@@ -2149,13 +2330,15 @@ void FitPeaks::writeFitResult(
       for (size_t iparam = 0; iparam < num_peakfunc_params + num_bkgd_params;
            ++iparam) {
         size_t col_index = iparam + 2;
-        // check column index against table columns
-        if (col_index >= m_fittedParamTable->columnCount()) {
-          // TODO FIXME Remove this check after testing!
-          throw std::runtime_error("This is not possible!");
-        }
+        // fitted parameter's value
         m_fittedParamTable->cell<double>(row_index, col_index) =
             fit_result->getParameterValue(ipeak, iparam);
+        // fitted parameter's fitting error
+        if (m_fitErrorTable) {
+          m_fitErrorTable->cell<double>(row_index, col_index) =
+              fit_result->getParameterError(ipeak, iparam);
+        }
+
       } // end for (iparam)
     } else {
       // effective peak profile parameter
diff --git a/Framework/Algorithms/src/HRPDSlabCanAbsorption.cpp b/Framework/Algorithms/src/HRPDSlabCanAbsorption.cpp
index d6e544087d27f969f41912b1085b95d3d04bcad5..842e7aa50142eb2cf52acb4abcc9068789d7c685 100644
--- a/Framework/Algorithms/src/HRPDSlabCanAbsorption.cpp
+++ b/Framework/Algorithms/src/HRPDSlabCanAbsorption.cpp
@@ -169,9 +169,7 @@ API::MatrixWorkspace_sptr HRPDSlabCanAbsorption::runFlatPlateAbsorption() {
       sigma_atten = sampleMaterial.absorbXSection(NeutronAtom::ReferenceLambda);
   } else // Save input in Sample with wrong atomic number and name
   {
-    NeutronAtom neutron(static_cast<uint16_t>(EMPTY_DBL()),
-                        static_cast<uint16_t>(0), 0.0, 0.0, sigma_s, 0.0,
-                        sigma_s, sigma_atten);
+    NeutronAtom neutron(0, 0, 0.0, 0.0, sigma_s, 0.0, sigma_s, sigma_atten);
     auto shape = boost::shared_ptr<IObject>(
         m_inputWS->sample().getShape().cloneWithMaterial(
             Material("SetInSphericalAbsorption", neutron, rho)));
diff --git a/Framework/Algorithms/src/MergeRuns.cpp b/Framework/Algorithms/src/MergeRuns.cpp
index 497459e2c43d9ad9fa2153444116d2d870e7f9fa..f85859c49131787b2aac6c6957716a086be80611 100644
--- a/Framework/Algorithms/src/MergeRuns.cpp
+++ b/Framework/Algorithms/src/MergeRuns.cpp
@@ -344,19 +344,17 @@ void MergeRuns::execEvent() {
 }
 
 void MergeRuns::execHistogram(const std::vector<std::string> &inputs) {
-  const std::string sampleLogsSum = getProperty(SampleLogsBehaviour::SUM_PROP);
-  const std::string sampleLogsTimeSeries =
-      getProperty(SampleLogsBehaviour::TIME_SERIES_PROP);
-  const std::string sampleLogsList =
-      getProperty(SampleLogsBehaviour::LIST_PROP);
-  const std::string sampleLogsWarn =
-      getProperty(SampleLogsBehaviour::WARN_PROP);
-  const std::string sampleLogsWarnTolerances =
-      getProperty(SampleLogsBehaviour::WARN_TOL_PROP);
-  const std::string sampleLogsFail =
-      getProperty(SampleLogsBehaviour::FAIL_PROP);
-  const std::string sampleLogsFailTolerances =
-      getProperty(SampleLogsBehaviour::FAIL_TOL_PROP);
+  SampleLogsBehaviour::SampleLogNames logEntries = {};
+  logEntries.sampleLogsSum = getPropertyValue(SampleLogsBehaviour::SUM_PROP);
+  logEntries.sampleLogsTimeSeries =
+      getPropertyValue(SampleLogsBehaviour::TIME_SERIES_PROP);
+  logEntries.sampleLogsList = getPropertyValue(SampleLogsBehaviour::LIST_PROP);
+  logEntries.sampleLogsWarn = getPropertyValue(SampleLogsBehaviour::WARN_PROP);
+  logEntries.sampleLogsWarnTolerances =
+      getPropertyValue(SampleLogsBehaviour::WARN_TOL_PROP);
+  logEntries.sampleLogsFail = getPropertyValue(SampleLogsBehaviour::FAIL_PROP);
+  logEntries.sampleLogsFailTolerances =
+      getPropertyValue(SampleLogsBehaviour::FAIL_TOL_PROP);
 
   const std::string sampleLogsFailBehaviour = getProperty("FailBehaviour");
 
@@ -370,10 +368,16 @@ void MergeRuns::execHistogram(const std::vector<std::string> &inputs) {
   if (rebinParams) {
     outWS = this->rebinInput(outWS, *rebinParams);
   }
-  Algorithms::SampleLogsBehaviour sampleLogsBehaviour = SampleLogsBehaviour(
-      *outWS, g_log, sampleLogsSum, sampleLogsTimeSeries, sampleLogsList,
-      sampleLogsWarn, sampleLogsWarnTolerances, sampleLogsFail,
-      sampleLogsFailTolerances);
+  SampleLogsBehaviour::ParameterName parName = {
+      MergeRunsParameter::SUM_MERGE,
+      MergeRunsParameter::TIME_SERIES_MERGE,
+      MergeRunsParameter::LIST_MERGE,
+      MergeRunsParameter::WARN_MERGE,
+      MergeRunsParameter::WARN_MERGE_TOLERANCES,
+      MergeRunsParameter::FAIL_MERGE,
+      MergeRunsParameter::FAIL_MERGE_TOLERANCES};
+  Algorithms::SampleLogsBehaviour sampleLogsBehaviour =
+      SampleLogsBehaviour(outWS, g_log, logEntries, parName);
 
   auto isScanning = outWS->detectorInfo().isScanning();
 
@@ -393,21 +397,21 @@ void MergeRuns::execHistogram(const std::vector<std::string> &inputs) {
     // Add the current workspace to the total
     // Update the sample logs
     try {
-      sampleLogsBehaviour.mergeSampleLogs(**it, *outWS);
-      sampleLogsBehaviour.removeSampleLogsFromWorkspace(*addee);
+      sampleLogsBehaviour.mergeSampleLogs(*it, outWS);
+      sampleLogsBehaviour.removeSampleLogsFromWorkspace(addee);
       if (isScanning)
         outWS = buildScanningOutputWorkspace(outWS, addee);
       else
         outWS = outWS + addee;
-      sampleLogsBehaviour.setUpdatedSampleLogs(*outWS);
-      sampleLogsBehaviour.readdSampleLogToWorkspace(*addee);
+      sampleLogsBehaviour.setUpdatedSampleLogs(outWS);
+      sampleLogsBehaviour.readdSampleLogToWorkspace(addee);
     } catch (std::invalid_argument &e) {
       if (sampleLogsFailBehaviour == SKIP_BEHAVIOUR) {
         g_log.error()
             << "Could not merge run: " << it->get()->getName() << ". Reason: \""
             << e.what()
             << "\". MergeRuns will continue but this run will be skipped.\n";
-        sampleLogsBehaviour.resetSampleLogs(*outWS);
+        sampleLogsBehaviour.resetSampleLogs(outWS);
       } else {
         throw std::invalid_argument(e);
       }
diff --git a/Framework/Algorithms/src/MostLikelyMean.cpp b/Framework/Algorithms/src/MostLikelyMean.cpp
index c45383577322f460410247aa268ca04680138532..c78f4d3138ec8beb2c43e1fa0c294c5b55f5f14a 100644
--- a/Framework/Algorithms/src/MostLikelyMean.cpp
+++ b/Framework/Algorithms/src/MostLikelyMean.cpp
@@ -5,8 +5,9 @@
 //     & Institut Laue - Langevin
 // SPDX - License - Identifier: GPL - 3.0 +
 #include "MantidAlgorithms/MostLikelyMean.h"
+#include "MantidKernel/ArrayLengthValidator.h"
 #include "MantidKernel/ArrayProperty.h"
-#include "MantidKernel/NullValidator.h"
+#include "MantidKernel/MultiThreaded.h"
 #include "MantidKernel/PropertyWithValue.h"
 
 #include "boost/multi_array.hpp"
@@ -14,9 +15,9 @@
 namespace Mantid {
 namespace Algorithms {
 
+using Mantid::Kernel::ArrayLengthValidator;
 using Mantid::Kernel::ArrayProperty;
 using Mantid::Kernel::Direction;
-using Mantid::Kernel::NullValidator;
 using Mantid::Kernel::PropertyWithValue;
 
 // Register the algorithm into the AlgorithmFactory
@@ -43,9 +44,10 @@ const std::string MostLikelyMean::summary() const {
 /** Initialize the algorithm's properties.
  */
 void MostLikelyMean::init() {
-  auto nullValidator = boost::make_shared<NullValidator>();
+  auto lengthValidator = boost::make_shared<ArrayLengthValidator<double>>();
+  lengthValidator->setLengthMin(1);
   declareProperty(Kernel::make_unique<ArrayProperty<double>>(
-                      "InputArray", nullValidator, Direction::Input),
+                      "InputArray", lengthValidator, Direction::Input),
                   "An input array.");
   declareProperty(Kernel::make_unique<PropertyWithValue<double>>(
                       "Output", 0., Direction::Output),
@@ -59,7 +61,7 @@ void MostLikelyMean::exec() {
   const std::vector<double> input = getProperty("InputArray");
   const int size = static_cast<int>(input.size());
   boost::multi_array<double, 2> cov(boost::extents[size][size]);
-  PARALLEL_FOR_IF(true)
+  PARALLEL_FOR_NO_WSP_CHECK()
   for (int i = 0; i < size; ++i) {
     for (int j = 0; j <= i; ++j) {
       double diff = sqrt(fabs(input[i] - input[j]));
diff --git a/Framework/Algorithms/src/ParallaxCorrection.cpp b/Framework/Algorithms/src/ParallaxCorrection.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5b9ea00b5614960287526ea16d98e98d6bedf35d
--- /dev/null
+++ b/Framework/Algorithms/src/ParallaxCorrection.cpp
@@ -0,0 +1,201 @@
+// Mantid Repository : https://github.com/mantidproject/mantid
+//
+// Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
+//     NScD Oak Ridge National Laboratory, European Spallation Source
+//     & Institut Laue - Langevin
+// SPDX - License - Identifier: GPL - 3.0 +
+#include "MantidAlgorithms/ParallaxCorrection.h"
+#include "MantidAPI/InstrumentValidator.h"
+#include "MantidAPI/MatrixWorkspace.h"
+#include "MantidAPI/Progress.h"
+#include "MantidAPI/WorkspaceUnitValidator.h"
+#include "MantidGeometry/Instrument.h"
+#include "MantidGeometry/Instrument/ComponentInfo.h"
+#include "MantidGeometry/Instrument/DetectorInfo.h"
+#include "MantidHistogramData/HistogramMath.h"
+#include "MantidKernel/ArrayLengthValidator.h"
+#include "MantidKernel/ArrayProperty.h"
+#include "MantidKernel/CompositeValidator.h"
+
+#include <math.h>
+#include <muParser.h>
+
+namespace {
+static const std::string PARALLAX_PARAMETER = "parallax";
+static const std::string DIRECTION_PARAMETER = "direction";
+
+/**
+ * @brief validateFormula Checks if the formula is evaluable
+ * @param parallax : formula
+ * @param direction : x or y
+ * @return empty string if valid, error message otherwise
+ */
+std::string validateFormula(const std::string &parallax,
+                            const std::string &direction) {
+  if (direction != "x" && direction != "y") {
+    return "Direction must be x or y";
+  }
+  mu::Parser muParser;
+  double t = 0.;
+  muParser.DefineVar("t", &t);
+  muParser.SetExpr(parallax);
+  try {
+    muParser.Eval();
+  } catch (mu::Parser::exception_type &e) {
+    return e.GetMsg();
+  }
+  return "";
+}
+} // namespace
+
+namespace Mantid {
+namespace Algorithms {
+
+// Register the algorithm into the AlgorithmFactory
+DECLARE_ALGORITHM(ParallaxCorrection)
+
+//----------------------------------------------------------------------------------------------
+
+/// Algorithms name for identification. @see Algorithm::name
+const std::string ParallaxCorrection::name() const {
+  return "ParallaxCorrection";
+}
+
+/// Algorithm's version for identification. @see Algorithm::version
+int ParallaxCorrection::version() const { return 1; }
+
+/// Algorithm's category for identification. @see Algorithm::category
+const std::string ParallaxCorrection::category() const { return "SANS"; }
+
+/// Algorithm's summary for use in the GUI and help. @see Algorithm::summary
+const std::string ParallaxCorrection::summary() const {
+  return "Performs parallax correction for tube based SANS instruments.";
+}
+
+//----------------------------------------------------------------------------------------------
+/** Initialize the algorithm's properties.
+ */
+void ParallaxCorrection::init() {
+  auto validator = boost::make_shared<Kernel::CompositeValidator>();
+  validator->add(Kernel::make_unique<API::InstrumentValidator>());
+  validator->add(
+      Kernel::make_unique<API::WorkspaceUnitValidator>("Wavelength"));
+  auto lengthValidator =
+      boost::make_shared<Kernel::ArrayLengthValidator<std::string>>();
+  lengthValidator->setLengthMin(1);
+  declareProperty(
+      Kernel::make_unique<API::WorkspaceProperty<API::MatrixWorkspace>>(
+          "InputWorkspace", "", Kernel::Direction::Input, validator),
+      "An input workspace.");
+  declareProperty(
+      Kernel::make_unique<Kernel::ArrayProperty<std::string>>("ComponentNames",
+                                                              lengthValidator),
+      "List of instrument components to perform the corrections for.");
+  declareProperty(
+      Kernel::make_unique<API::WorkspaceProperty<API::MatrixWorkspace>>(
+          "OutputWorkspace", "", Kernel::Direction::Output),
+      "An output workspace.");
+}
+
+//----------------------------------------------------------------------------------------------
+/**
+ * @brief ParallaxCorrection::performCorrection for the given bank
+ * @param outWS : the corrected workspace
+ * @param indices : the workspaces indices corresponding to the bank
+ * @param parallax : the correction formula for the bank
+ * @param direction : the tube direction in the bank
+ */
+void ParallaxCorrection::performCorrection(API::MatrixWorkspace_sptr outWS,
+                                           const std::vector<size_t> &indices,
+                                           const std::string &parallax,
+                                           const std::string &direction) {
+  double t;
+  mu::Parser muParser;
+  muParser.DefineVar("t", &t);
+  muParser.SetExpr(parallax);
+  const auto &detectorInfo = outWS->detectorInfo();
+  // note that this is intenionally serial
+  for (const auto wsIndex : indices) {
+    const Kernel::V3D pos = detectorInfo.position(wsIndex);
+    if (direction == "y") {
+      t = std::fabs(std::atan2(pos.X(), pos.Z()));
+    } else {
+      t = std::fabs(std::atan2(pos.Y(), pos.Z()));
+    }
+    const double correction = muParser.Eval();
+    if (correction > 0.) {
+      auto &spectrum = outWS->mutableY(wsIndex);
+      auto &errors = outWS->mutableE(wsIndex);
+      spectrum /= correction;
+      errors /= correction;
+    } else {
+      g_log.warning() << "Correction is <=0 for workspace index " << wsIndex
+                      << ". Skipping the correction.\n";
+    }
+  }
+}
+
+//----------------------------------------------------------------------------------------------
+/** Execute the algorithm.
+ */
+void ParallaxCorrection::exec() {
+  API::MatrixWorkspace_const_sptr inputWorkspace =
+      getProperty("InputWorkspace");
+  API::MatrixWorkspace_sptr outputWorkspace = getProperty("OutputWorkspace");
+  if (inputWorkspace != outputWorkspace) {
+    outputWorkspace = inputWorkspace->clone();
+  }
+  const std::vector<std::string> componentNames = getProperty("ComponentNames");
+  const auto &instrument = inputWorkspace->getInstrument();
+  const auto &detectorInfo = outputWorkspace->detectorInfo();
+  const auto &allDetIDs = detectorInfo.detectorIDs();
+  const auto &componentInfo = outputWorkspace->componentInfo();
+  auto progress =
+      Kernel::make_unique<API::Progress>(this, 0., 1., componentNames.size());
+  for (const auto &componentName : componentNames) {
+    progress->report("Performing parallax correction for component " +
+                     componentName);
+    const auto component = instrument->getComponentByName(componentName);
+    if (!component) {
+      g_log.error() << "No component defined with name " << componentName
+                    << "\n";
+      continue;
+    }
+    if (!component->hasParameter(PARALLAX_PARAMETER) ||
+        !component->hasParameter(DIRECTION_PARAMETER)) {
+      g_log.error() << "No parallax correction defined in IPF for component "
+                    << componentName << "\n";
+      continue;
+    }
+    const std::string parallax =
+        component->getStringParameter(PARALLAX_PARAMETER)[0];
+    const std::string direction =
+        component->getStringParameter(DIRECTION_PARAMETER)[0];
+    const auto valid = validateFormula(parallax, direction);
+    if (!valid.empty()) {
+      g_log.error() << "Unable to parse the parallax formula and direction "
+                       "for component "
+                    << componentName << ". Reason: " << valid << "\n";
+      continue;
+    }
+    const auto componentIndex = componentInfo.indexOfAny(componentName);
+    const auto &detectorIndices =
+        componentInfo.detectorsInSubtree(componentIndex);
+    if (detectorIndices.empty()) {
+      g_log.error() << "No detectors found in component " << componentName
+                    << "\n";
+      continue;
+    }
+    std::vector<detid_t> detIDs;
+    detIDs.reserve(detectorIndices.size());
+    std::transform(detectorIndices.cbegin(), detectorIndices.cend(),
+                   std::back_inserter(detIDs),
+                   [&allDetIDs](size_t i) { return allDetIDs[i]; });
+    const auto indices = outputWorkspace->getIndicesFromDetectorIDs(detIDs);
+    performCorrection(outputWorkspace, indices, parallax, direction);
+  }
+  setProperty("OutputWorkspace", outputWorkspace);
+}
+
+} // namespace Algorithms
+} // namespace Mantid
diff --git a/Framework/Algorithms/src/ReflectometryReductionOneAuto2.cpp b/Framework/Algorithms/src/ReflectometryReductionOneAuto2.cpp
index 5105eb76f6809f3ec5bd5d6cff416521aa794e4b..4a6ddcd9135ead281b38df1c827fb3499397d61f 100644
--- a/Framework/Algorithms/src/ReflectometryReductionOneAuto2.cpp
+++ b/Framework/Algorithms/src/ReflectometryReductionOneAuto2.cpp
@@ -275,30 +275,30 @@ void ReflectometryReductionOneAuto2::init() {
                   boost::make_shared<StringListValidator>(propOptions),
                   "Polarization analysis mode.");
   declareProperty(
-      Kernel::make_unique<ArrayProperty<double>>("Pp", Direction::Input),
+      Kernel::make_unique<ArrayProperty<double>>("CPp", Direction::Input),
       "Effective polarizing power of the polarizing system. "
       "Expressed as a ratio 0 &lt; Pp &lt; 1");
   declareProperty(
-      Kernel::make_unique<ArrayProperty<double>>("Ap", Direction::Input),
+      Kernel::make_unique<ArrayProperty<double>>("CAp", Direction::Input),
       "Effective polarizing power of the analyzing system. "
       "Expressed as a ratio 0 &lt; Ap &lt; 1");
   declareProperty(
-      Kernel::make_unique<ArrayProperty<double>>("Rho", Direction::Input),
+      Kernel::make_unique<ArrayProperty<double>>("CRho", Direction::Input),
       "Ratio of efficiencies of polarizer spin-down to polarizer "
       "spin-up. This is characteristic of the polarizer flipper. "
       "Values are constants for each term in a polynomial "
       "expression.");
   declareProperty(
-      Kernel::make_unique<ArrayProperty<double>>("Alpha", Direction::Input),
+      Kernel::make_unique<ArrayProperty<double>>("CAlpha", Direction::Input),
       "Ratio of efficiencies of analyzer spin-down to analyzer "
       "spin-up. This is characteristic of the analyzer flipper. "
       "Values are factors for each term in a polynomial "
       "expression.");
   setPropertyGroup("PolarizationAnalysis", "Polarization Corrections");
-  setPropertyGroup("Pp", "Polarization Corrections");
-  setPropertyGroup("Ap", "Polarization Corrections");
-  setPropertyGroup("Rho", "Polarization Corrections");
-  setPropertyGroup("Alpha", "Polarization Corrections");
+  setPropertyGroup("CPp", "Polarization Corrections");
+  setPropertyGroup("CAp", "Polarization Corrections");
+  setPropertyGroup("CRho", "Polarization Corrections");
+  setPropertyGroup("CAlpha", "Polarization Corrections");
 
   // Flood correction
   propOptions = {"Workspace", "ParameterFile"};
@@ -944,17 +944,17 @@ ReflectometryReductionOneAuto2::getPolarizationEfficiencies() {
   } else {
     auto effAlg = createChildAlgorithm("CreatePolarizationEfficiencies");
     effAlg->setProperty("InputWorkspace", workspace);
-    if (!isDefault("Pp")) {
-      effAlg->setProperty("Pp", getPropertyValue("Pp"));
+    if (!isDefault("CPp")) {
+      effAlg->setProperty("Pp", getPropertyValue("CPp"));
     }
-    if (!isDefault("Rho")) {
-      effAlg->setProperty("Rho", getPropertyValue("Rho"));
+    if (!isDefault("CRho")) {
+      effAlg->setProperty("Rho", getPropertyValue("CRho"));
     }
-    if (!isDefault("Ap")) {
-      effAlg->setProperty("Ap", getPropertyValue("Ap"));
+    if (!isDefault("CAp")) {
+      effAlg->setProperty("Ap", getPropertyValue("CAp"));
     }
-    if (!isDefault("Alpha")) {
-      effAlg->setProperty("Alpha", getPropertyValue("Alpha"));
+    if (!isDefault("CAlpha")) {
+      effAlg->setProperty("Alpha", getPropertyValue("CAlpha"));
     }
     effAlg->execute();
     efficiencies = effAlg->getProperty("OutputWorkspace");
diff --git a/Framework/Algorithms/src/RunCombinationHelpers/SampleLogsBehaviour.cpp b/Framework/Algorithms/src/RunCombinationHelpers/SampleLogsBehaviour.cpp
index d3f83008795648c93484ed3942d34be4ff99d3d7..acebba1e1df9457fce75660d6bb4081e9d3b6ac0 100644
--- a/Framework/Algorithms/src/RunCombinationHelpers/SampleLogsBehaviour.cpp
+++ b/Framework/Algorithms/src/RunCombinationHelpers/SampleLogsBehaviour.cpp
@@ -5,6 +5,7 @@
 //     & Institut Laue - Langevin
 // SPDX - License - Identifier: GPL - 3.0 +
 #include "MantidAlgorithms/RunCombinationHelpers/SampleLogsBehaviour.h"
+#include "MantidAPI/MatrixWorkspace.h"
 #include "MantidAPI/Run.h"
 #include "MantidGeometry/Instrument.h"
 #include "MantidKernel/StringTokenizer.h"
@@ -31,20 +32,10 @@ std::string generateDifferenceMessage(const std::string &item,
   return stringstream.str();
 }
 } // namespace
-const std::string SampleLogsBehaviour::SUM_MERGE = "sample_logs_sum";
-const std::string SampleLogsBehaviour::TIME_SERIES_MERGE =
-    "sample_logs_time_series";
-const std::string SampleLogsBehaviour::LIST_MERGE = "sample_logs_list";
-const std::string SampleLogsBehaviour::WARN_MERGE = "sample_logs_warn";
-const std::string SampleLogsBehaviour::FAIL_MERGE = "sample_logs_fail";
-const std::string SampleLogsBehaviour::WARN_MERGE_TOLERANCES =
-    "sample_logs_warn_tolerances";
-const std::string SampleLogsBehaviour::FAIL_MERGE_TOLERANCES =
-    "sample_logs_fail_tolerances";
 
 // Names and docs from the properties allowing to override the default (IPF
 // controlled) merging behaviour.
-// These are common between e.g. MergeRuns and JoinWorkspaces.
+// These are common between e.g. MergeRuns and ConjoinXRuns.
 const std::string SampleLogsBehaviour::TIME_SERIES_PROP =
     "SampleLogsTimeSeries";
 const std::string SampleLogsBehaviour::TIME_SERIES_DOC =
@@ -94,42 +85,27 @@ const std::string SampleLogsBehaviour::SUM_DOC =
  *
  * @param ws the base workspace that the other workspaces are merged into
  * @param logger the logger from the parent algorithm
- * @param sampleLogsSum a string with a comma separated list of the logs to be
- *summed
- * @param sampleLogsTimeSeries a string with a comma separated list of the logs
- * for the time series merge
- * @param sampleLogsList a string with a comma separated list of the logs for a
- * list merge
- * @param sampleLogsWarn a string with a comma separated list of the logs for
- * warning when different on merging
- * @param sampleLogsWarnTolerances a string with a single value or comma
- * separated list of values for the warning tolerances
- * @param sampleLogsFail a string with a comma separated list of the logs for
- * throwing an error when different on merging
- * @param sampleLogsFailTolerances a string with a single value or comma
- * separated list of values for the error tolerances
- * @return An instance of SampleLogsBehaviour initialised with the merge types
- * from the IPF and parent algorithm
+ * @param logEntries the sample log names to merge given by the user which
+ * override names given by IPF parameters
+ * @param parName the parameter names which specify the sample log sames to
+ * merge given be the IPF
  */
-SampleLogsBehaviour::SampleLogsBehaviour(
-    MatrixWorkspace &ws, Logger &logger, const std::string &sampleLogsSum,
-    const std::string &sampleLogsTimeSeries, const std::string &sampleLogsList,
-    const std::string &sampleLogsWarn,
-    const std::string &sampleLogsWarnTolerances,
-    const std::string &sampleLogsFail,
-    const std::string &sampleLogsFailTolerances)
-    : m_logger(logger) {
-  setSampleMap(m_logMap, MergeLogType::Sum, sampleLogsSum, ws, "");
-  setSampleMap(m_logMap, MergeLogType::TimeSeries, sampleLogsTimeSeries, ws,
-               "");
-  setSampleMap(m_logMap, MergeLogType::List, sampleLogsList, ws, "");
-  setSampleMap(m_logMap, MergeLogType::Warn, sampleLogsWarn, ws,
-               sampleLogsWarnTolerances);
-  setSampleMap(m_logMap, MergeLogType::Fail, sampleLogsFail, ws,
-               sampleLogsFailTolerances);
+SampleLogsBehaviour::SampleLogsBehaviour(MatrixWorkspace_sptr ws,
+                                         Logger &logger,
+                                         const SampleLogNames &logEntries,
+                                         const ParameterName &parName)
+    : parameterNames(parName), m_logger(logger) {
+  setSampleMap(m_logMap, MergeLogType::Sum, logEntries.sampleLogsSum, *ws);
+  setSampleMap(m_logMap, MergeLogType::TimeSeries,
+               logEntries.sampleLogsTimeSeries, *ws);
+  setSampleMap(m_logMap, MergeLogType::List, logEntries.sampleLogsList, *ws);
+  setSampleMap(m_logMap, MergeLogType::Warn, logEntries.sampleLogsWarn, *ws,
+               logEntries.sampleLogsWarnTolerances);
+  setSampleMap(m_logMap, MergeLogType::Fail, logEntries.sampleLogsFail, *ws,
+               logEntries.sampleLogsFailTolerances);
 
   SampleLogsMap instrumentMap;
-  this->createSampleLogsMapsFromInstrumentParams(instrumentMap, ws);
+  this->createSampleLogsMapsFromInstrumentParams(instrumentMap, *ws);
 
   // This adds the parameters from the instrument to the main map, with any
   // duplicates left as the versions in the MergeRuns arguments.
@@ -146,24 +122,28 @@ SampleLogsBehaviour::SampleLogsBehaviour(
 void SampleLogsBehaviour::createSampleLogsMapsFromInstrumentParams(
     SampleLogsMap &map, MatrixWorkspace &ws) {
   std::string params =
-      ws.getInstrument()->getParameterAsString(SUM_MERGE, false);
+      ws.getInstrument()->getParameterAsString(parameterNames.SUM_MERGE, false);
   setSampleMap(map, MergeLogType::Sum, params, ws, "", true);
 
-  params = ws.getInstrument()->getParameterAsString(TIME_SERIES_MERGE, false);
+  params = ws.getInstrument()->getParameterAsString(
+      parameterNames.TIME_SERIES_MERGE, false);
   setSampleMap(map, MergeLogType::TimeSeries, params, ws, "", true);
 
-  params = ws.getInstrument()->getParameterAsString(LIST_MERGE, false);
+  params = ws.getInstrument()->getParameterAsString(parameterNames.LIST_MERGE,
+                                                    false);
   setSampleMap(map, MergeLogType::List, params, ws, "", true);
 
-  params = ws.getInstrument()->getParameterAsString(WARN_MERGE, false);
+  params = ws.getInstrument()->getParameterAsString(parameterNames.WARN_MERGE,
+                                                    false);
   std::string paramsTolerances;
-  paramsTolerances =
-      ws.getInstrument()->getParameterAsString(WARN_MERGE_TOLERANCES, false);
+  paramsTolerances = ws.getInstrument()->getParameterAsString(
+      parameterNames.WARN_MERGE_TOLERANCES, false);
   setSampleMap(map, MergeLogType::Warn, params, ws, paramsTolerances, true);
 
-  params = ws.getInstrument()->getParameterAsString(FAIL_MERGE, false);
-  paramsTolerances =
-      ws.getInstrument()->getParameterAsString(FAIL_MERGE_TOLERANCES, false);
+  params = ws.getInstrument()->getParameterAsString(parameterNames.FAIL_MERGE,
+                                                    false);
+  paramsTolerances = ws.getInstrument()->getParameterAsString(
+      parameterNames.FAIL_MERGE_TOLERANCES, false);
   setSampleMap(map, MergeLogType::Fail, params, ws, paramsTolerances, true);
 }
 
@@ -394,7 +374,7 @@ std::shared_ptr<Property> SampleLogsBehaviour::addPropertyForList(
     const std::string &item, const std::string &value, MatrixWorkspace &ws) {
   std::shared_ptr<Property> returnProp;
 
-  // See if property exists already - merging an output of MergeRuns
+  // See if property exists already - merging an output of the calling algorithm
   returnProp.reset(ws.getLog(item)->clone());
 
   if (returnProp->type() != "string") {
@@ -435,19 +415,19 @@ bool SampleLogsBehaviour::setNumericValue(const std::string &item,
  * @param addeeWS the workspace being merged
  * @param outWS the workspace the others are merged into
  */
-void SampleLogsBehaviour::mergeSampleLogs(MatrixWorkspace &addeeWS,
-                                          MatrixWorkspace &outWS) {
-  for (auto item : m_logMap) {
+void SampleLogsBehaviour::mergeSampleLogs(MatrixWorkspace_sptr addeeWS,
+                                          MatrixWorkspace_sptr outWS) {
+  for (const auto &item : m_logMap) {
     std::string logName = item.first.first;
 
-    Property *addeeWSProperty = addeeWS.getLog(logName);
+    Property *addeeWSProperty = addeeWS->getLog(logName);
 
-    double addeeWSNumericValue = 0;
-    double outWSNumericValue = 0;
+    double addeeWSNumericValue = 0.;
+    double outWSNumericValue = 0.;
 
     try {
-      addeeWSNumericValue = addeeWS.getLogAsSingleValue(logName);
-      outWSNumericValue = outWS.getLogAsSingleValue(logName);
+      addeeWSNumericValue = addeeWS->getLogAsSingleValue(logName);
+      outWSNumericValue = outWS->getLogAsSingleValue(logName);
     } catch (std::invalid_argument &) {
       if (item.second.isNumeric) {
         throw std::invalid_argument(
@@ -457,24 +437,24 @@ void SampleLogsBehaviour::mergeSampleLogs(MatrixWorkspace &addeeWS,
 
     switch (item.first.second) {
     case MergeLogType::Sum: {
-      this->updateSumProperty(addeeWSNumericValue, outWSNumericValue, outWS,
+      this->updateSumProperty(addeeWSNumericValue, outWSNumericValue, *outWS,
                               logName);
       break;
     }
     case MergeLogType::TimeSeries: {
-      this->updateTimeSeriesProperty(addeeWS, outWS, logName);
+      this->updateTimeSeriesProperty(*addeeWS, *outWS, logName);
       break;
     }
     case MergeLogType::List: {
-      this->updateListProperty(addeeWS, outWS, logName);
+      this->updateListProperty(*addeeWS, *outWS, logName);
       break;
     }
     case MergeLogType::Warn:
-      this->checkWarnProperty(addeeWS, addeeWSProperty, item.second,
+      this->checkWarnProperty(*addeeWS, addeeWSProperty, item.second,
                               addeeWSNumericValue, outWSNumericValue, logName);
       break;
     case MergeLogType::Fail:
-      this->checkErrorProperty(addeeWS, addeeWSProperty, item.second,
+      this->checkErrorProperty(*addeeWS, addeeWSProperty, item.second,
                                addeeWSNumericValue, outWSNumericValue, logName);
       break;
     }
@@ -516,14 +496,13 @@ void SampleLogsBehaviour::updateTimeSeriesProperty(MatrixWorkspace &addeeWS,
     // are combined when adding workspaces.
     addeeWS.run().getTimeSeriesProperty<double>(name);
   } catch (std::invalid_argument &) {
-    auto timeSeriesProp =
-        outWS.mutableRun().getTimeSeriesProperty<double>(name);
-    Types::Core::DateAndTime startTime = addeeWS.mutableRun().startTime();
-    double value = addeeWS.mutableRun().getLogAsSingleValue(name);
+    auto timeSeriesProp = outWS.run().getTimeSeriesProperty<double>(name);
+    Types::Core::DateAndTime startTime = addeeWS.run().startTime();
+    double value = addeeWS.run().getLogAsSingleValue(name);
     timeSeriesProp->addValue(startTime, value);
     // Remove this to supress a warning, we will put it back after adding the
     // workspaces in MergeRuns
-    const Property *addeeWSProperty = addeeWS.mutableRun().getProperty(name);
+    const Property *addeeWSProperty = addeeWS.run().getProperty(name);
     m_addeeLogMap.push_back(
         std::shared_ptr<Property>(addeeWSProperty->clone()));
   }
@@ -541,11 +520,9 @@ void SampleLogsBehaviour::updateTimeSeriesProperty(MatrixWorkspace &addeeWS,
 void SampleLogsBehaviour::updateListProperty(MatrixWorkspace &addeeWS,
                                              MatrixWorkspace &outWS,
                                              const std::string &name) {
-  auto propertyAddeeWS = addeeWS.getLog(name);
-  auto propertyOutWS = outWS.mutableRun().getProperty(name);
-
-  propertyOutWS->setValue(propertyOutWS->value() + ", " +
-                          propertyAddeeWS->value());
+  const std::string addeeWSVal = addeeWS.getLog(name)->value();
+  const std::string outWSVal = outWS.run().getProperty(name)->value();
+  outWS.mutableRun().addProperty(name, outWSVal + ", " + addeeWSVal, true);
 }
 
 /**
@@ -645,7 +622,7 @@ bool SampleLogsBehaviour::stringPropertiesMatch(
  *
  * @param outWS the merged workspace
  */
-void SampleLogsBehaviour::setUpdatedSampleLogs(MatrixWorkspace &outWS) {
+void SampleLogsBehaviour::setUpdatedSampleLogs(MatrixWorkspace_sptr outWS) {
   for (auto &item : m_logMap) {
     std::string propertyToReset = item.first.first;
 
@@ -654,8 +631,7 @@ void SampleLogsBehaviour::setUpdatedSampleLogs(MatrixWorkspace &outWS) {
       continue;
     }
 
-    const Property *outWSProperty =
-        outWS.mutableRun().getProperty(propertyToReset);
+    const Property *outWSProperty = outWS->run().getProperty(propertyToReset);
     item.second.property = std::shared_ptr<Property>(outWSProperty->clone());
   }
 }
@@ -668,10 +644,10 @@ void SampleLogsBehaviour::setUpdatedSampleLogs(MatrixWorkspace &outWS) {
  * @param addeeWS the workspace being merged
  */
 void SampleLogsBehaviour::removeSampleLogsFromWorkspace(
-    MatrixWorkspace &addeeWS) {
+    MatrixWorkspace_sptr addeeWS) {
   for (const auto &prop : m_addeeLogMap) {
     const auto &propName = prop->name();
-    addeeWS.mutableRun().removeProperty(propName);
+    addeeWS->mutableRun().removeProperty(propName);
   }
 }
 
@@ -683,10 +659,11 @@ void SampleLogsBehaviour::removeSampleLogsFromWorkspace(
  *
  * @param addeeWS the workspace being merged
  */
-void SampleLogsBehaviour::readdSampleLogToWorkspace(MatrixWorkspace &addeeWS) {
+void SampleLogsBehaviour::readdSampleLogToWorkspace(
+    MatrixWorkspace_sptr addeeWS) {
   for (const auto &item : m_addeeLogMap) {
     auto property = std::unique_ptr<Kernel::Property>(item->clone());
-    addeeWS.mutableRun().addProperty(std::move(property));
+    addeeWS->mutableRun().addProperty(std::move(property));
   }
   m_addeeLogMap.clear();
 }
@@ -696,17 +673,17 @@ void SampleLogsBehaviour::readdSampleLogToWorkspace(MatrixWorkspace &addeeWS) {
  *
  * @param ws the merged workspace to reset the sample logs for
  */
-void SampleLogsBehaviour::resetSampleLogs(MatrixWorkspace &ws) {
+void SampleLogsBehaviour::resetSampleLogs(MatrixWorkspace_sptr ws) {
   for (auto const &item : m_logMap) {
     std::string const &propertyToReset = item.first.first;
 
     if (item.first.second == MergeLogType::TimeSeries) {
       auto property =
           std::unique_ptr<Kernel::Property>(item.second.property->clone());
-      ws.mutableRun().addProperty(std::move(property), true);
+      ws->mutableRun().addProperty(std::move(property), true);
     } else if (item.first.second == MergeLogType::Sum ||
                item.first.second == MergeLogType::List) {
-      ws.mutableRun()
+      ws->mutableRun()
           .getProperty(propertyToReset)
           ->setValue(item.second.property->value());
     }
diff --git a/Framework/Algorithms/src/SphericalAbsorption.cpp b/Framework/Algorithms/src/SphericalAbsorption.cpp
index dc4833390d3356dc003c1cb047baf76c342e69c1..9b5c160c37ac2ceeb30e5b11fde4498d1dbf5c70 100644
--- a/Framework/Algorithms/src/SphericalAbsorption.cpp
+++ b/Framework/Algorithms/src/SphericalAbsorption.cpp
@@ -127,9 +127,7 @@ void SphericalAbsorption::retrieveBaseProperties() {
       sigma_atten = sampleMaterial.absorbXSection(NeutronAtom::ReferenceLambda);
   } else // Save input in Sample with wrong atomic number and name
   {
-    NeutronAtom neutron(static_cast<uint16_t>(EMPTY_DBL()),
-                        static_cast<uint16_t>(0), 0.0, 0.0, sigma_s, 0.0,
-                        sigma_s, sigma_atten);
+    NeutronAtom neutron(0, 0, 0.0, 0.0, sigma_s, 0.0, sigma_s, sigma_atten);
     auto shape = boost::shared_ptr<IObject>(
         m_inputWS->sample().getShape().cloneWithMaterial(
             Material("SetInSphericalAbsorption", neutron, rho)));
diff --git a/Framework/Algorithms/test/DeadTimeCorrectionTest.h b/Framework/Algorithms/test/DeadTimeCorrectionTest.h
new file mode 100644
index 0000000000000000000000000000000000000000..b377228c9aff1109ee2cd84285cf37e39ff929f1
--- /dev/null
+++ b/Framework/Algorithms/test/DeadTimeCorrectionTest.h
@@ -0,0 +1,132 @@
+// Mantid Repository : https://github.com/mantidproject/mantid
+//
+// Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
+//     NScD Oak Ridge National Laboratory, European Spallation Source
+//     & Institut Laue - Langevin
+// SPDX - License - Identifier: GPL - 3.0 +
+#ifndef MANTID_ALGORITHMS_DEADTIMECORRECTIONTEST_H_
+#define MANTID_ALGORITHMS_DEADTIMECORRECTIONTEST_H_
+
+#include <cxxtest/TestSuite.h>
+
+#include "MantidAPI/FrameworkManager.h"
+#include "MantidAlgorithms/CreateSampleWorkspace.h"
+#include "MantidAlgorithms/DeadTimeCorrection.h"
+
+using Mantid::API::FrameworkManager;
+using Mantid::API::MatrixWorkspace_sptr;
+using Mantid::Algorithms::CreateSampleWorkspace;
+using Mantid::Algorithms::DeadTimeCorrection;
+
+namespace {
+MatrixWorkspace_sptr createWorkspace(const int nPixelsPerBank = 3,
+                                     const int nBins = 2,
+                                     const int numBanks = 2) {
+  CreateSampleWorkspace creator;
+  creator.initialize();
+  creator.setChild(true);
+  creator.setAlwaysStoreInADS(false);
+  creator.setProperty("NumBanks", numBanks);
+  creator.setProperty("XMin", 1.);
+  creator.setProperty("XMax", 2.);
+  creator.setProperty("BinWidth", 1. / nBins);
+  creator.setProperty("BankPixelWidth", nPixelsPerBank);
+  creator.setPropertyValue("OutputWorkspace", "__unused");
+  creator.execute();
+  MatrixWorkspace_sptr in = creator.getProperty("OutputWorkspace");
+  return in;
+}
+} // namespace
+
+class DeadTimeCorrectionTest : 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 DeadTimeCorrectionTest *createSuite() {
+    return new DeadTimeCorrectionTest();
+  }
+  static void destroySuite(DeadTimeCorrectionTest *suite) { delete suite; }
+
+  DeadTimeCorrectionTest() { FrameworkManager::Instance(); }
+
+  void test_init() {
+    DeadTimeCorrection alg;
+    TS_ASSERT_THROWS_NOTHING(alg.initialize())
+    TS_ASSERT(alg.isInitialized())
+  }
+
+  void test_exec() {
+    const double tau = 0.001;
+    MatrixWorkspace_sptr in = createWorkspace();
+    // we have 2 TOF bins, and will be grouping 9 pixels
+    const double countrate = 9 * (in->readY(0)[0] + in->readY(0)[1]);
+    const double expectation = 1. / (1. - tau * countrate);
+    DeadTimeCorrection alg;
+    alg.setChild(true);
+    alg.setRethrows(true);
+    TS_ASSERT_THROWS_NOTHING(alg.initialize())
+    TS_ASSERT(alg.isInitialized())
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("InputWorkspace", in))
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("Tau", tau))
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("GroupingPattern", "0-8,9-17"))
+    TS_ASSERT_THROWS_NOTHING(
+        alg.setPropertyValue("OutputWorkspace", "_unused_for_child"))
+    TS_ASSERT_THROWS_NOTHING(alg.execute())
+    TS_ASSERT(alg.isExecuted())
+    MatrixWorkspace_sptr out = alg.getProperty("OutputWorkspace");
+    TS_ASSERT(out)
+    TS_ASSERT_EQUALS(out->getNumberHistograms(), in->getNumberHistograms())
+    for (size_t index = 0; index < in->getNumberHistograms(); ++index) {
+      const auto &yIn = in->readY(index);
+      const auto &eIn = in->readE(index);
+      const auto &yOut = out->readY(index);
+      const auto &eOut = out->readE(index);
+      TS_ASSERT_EQUALS(yIn.size(), yOut.size())
+      TS_ASSERT_EQUALS(eIn.size(), eOut.size())
+      for (size_t bin = 0; bin < yIn.size(); ++bin) {
+        const double corrY = yOut[bin] / yIn[bin];
+        const double corrE = eOut[bin] / eIn[bin];
+        TS_ASSERT_DELTA(corrY, expectation, 1E-10)
+        TS_ASSERT_DELTA(corrE, expectation, 1E-10)
+      }
+    }
+  }
+};
+
+class DeadTimeCorrectionTestPerformance : public CxxTest::TestSuite {
+public:
+  static DeadTimeCorrectionTestPerformance *createSuite() {
+    return new DeadTimeCorrectionTestPerformance();
+  }
+  static void destroySuite(DeadTimeCorrectionTestPerformance *suite) {
+    delete suite;
+  }
+
+  DeadTimeCorrectionTestPerformance() { FrameworkManager::Instance(); }
+
+  void setUp() override {
+    MatrixWorkspace_sptr in = createWorkspace(100, 1000, 10);
+    m_alg.initialize();
+    m_alg.setChild(true);
+    m_alg.setRethrows(true);
+    m_alg.setProperty("InputWorkspace", in);
+    m_alg.setPropertyValue("OutputWorkspace", "__unused");
+    m_alg.setProperty("Tau", 0.0000001);
+    m_alg.setPropertyValue("GroupingPattern",
+                           "0-9999,10000-19999,20000-29999,30000-39999,40000-"
+                           "49999,50000-59999,60000-"
+                           "69999,70000-79999,80000-89999,90000-99999");
+  }
+
+  void test_performance() {
+    for (size_t i = 0; i < 5; ++i) {
+      TS_ASSERT_THROWS_NOTHING(m_alg.execute())
+    }
+  }
+
+private:
+  DeadTimeCorrection m_alg;
+  MatrixWorkspace_sptr in;
+};
+
+#endif /* MANTID_ALGORITHMS_DEADTIMECORRECTIONTEST_H_ */
diff --git a/Framework/Algorithms/test/FitPeaksTest.h b/Framework/Algorithms/test/FitPeaksTest.h
index 25c1408730498fe7508e6408f9aafb1af3a0afef..ce83ef160c0710dad7ae335ca3bc92d4f245906d 100644
--- a/Framework/Algorithms/test/FitPeaksTest.h
+++ b/Framework/Algorithms/test/FitPeaksTest.h
@@ -54,6 +54,87 @@ public:
     TS_ASSERT(fitpeaks.isInitialized());
   }
 
+  //----------------------------------------------------------------------------------------------
+  /** test fit a single peak in partial spectra from a multiple spectra
+   * workspace
+   * the peak positions are given by the peak position workspace and thus the
+   * peak fit windows
+   * @brief test_singlePeaksPartialSpectra
+   */
+  void test_singlePeaksPartialSpectra() {
+    // Generate input workspace
+    const std::string data_ws_name("Test1Data");
+    createTestData(data_ws_name);
+
+    // Generate peak and background parameters
+    std::vector<string> peakparnames;
+    std::vector<double> peakparvalues;
+    gen_PeakParameters(peakparnames, peakparvalues);
+
+    // create a 1-value peak index vector for peak (0) at X=5
+    std::vector<int> peak_index_vec;
+    peak_index_vec.push_back(0);
+    const std::string ws_name("peakcenter1");
+    const std::string peak_center_ws_name =
+        genPeakCenterWorkspace(peak_index_vec, ws_name);
+    const std::string fit_window_ws_name =
+        genFitWindowWorkspace(peak_index_vec, "peakwindow1");
+
+    // Initialize FitPeak
+    FitPeaks fitpeaks;
+
+    fitpeaks.initialize();
+    TS_ASSERT(fitpeaks.isInitialized());
+
+    TS_ASSERT_THROWS_NOTHING(
+        fitpeaks.setProperty("InputWorkspace", data_ws_name));
+    TS_ASSERT_THROWS_NOTHING(fitpeaks.setProperty("StartWorkspaceIndex", 0));
+    TS_ASSERT_THROWS_NOTHING(fitpeaks.setProperty("StopWorkspaceIndex", 1));
+    TS_ASSERT_THROWS_NOTHING(fitpeaks.setProperty("PeakFunction", "Gaussian"));
+    TS_ASSERT_THROWS_NOTHING(
+        fitpeaks.setProperty("PeakCentersWorkspace", peak_center_ws_name));
+    TS_ASSERT_THROWS_NOTHING(
+        fitpeaks.setProperty("FitPeakWindowWorkspace", fit_window_ws_name))
+
+    fitpeaks.setProperty("OutputWorkspace", "PeakPositionsWS3");
+    fitpeaks.setProperty("OutputPeakParametersWorkspace", "PeakParametersWS3");
+    fitpeaks.setProperty("FittedPeaksWorkspace", "FittedPeaksWS3");
+    fitpeaks.setProperty("MaxFitIterations", 200);
+
+    fitpeaks.execute();
+    TS_ASSERT(fitpeaks.isExecuted());
+    if (fitpeaks.isExecuted()) {
+      // check output workspaces
+      TS_ASSERT(
+          API::AnalysisDataService::Instance().doesExist("PeakPositionsWS3"));
+      TS_ASSERT(
+          API::AnalysisDataService::Instance().doesExist("PeakParametersWS3"));
+      TS_ASSERT(
+          API::AnalysisDataService::Instance().doesExist("FittedPeaksWS3"));
+
+      // about the parameters
+      API::MatrixWorkspace_sptr peak_params_ws =
+          boost::dynamic_pointer_cast<API::MatrixWorkspace>(
+              AnalysisDataService::Instance().retrieve("PeakPositionsWS3"));
+      TS_ASSERT(peak_params_ws);
+      // 2 spectra
+      TS_ASSERT_EQUALS(peak_params_ws->getNumberHistograms(), 2);
+      // 1 peak
+      TS_ASSERT_EQUALS(peak_params_ws->histogram(0).x().size(), 1);
+
+      // clean algorithm-generated workspaces
+      API::AnalysisDataService::Instance().remove("PeakPositionsWS3");
+      API::AnalysisDataService::Instance().remove("PeakParametersWS3");
+      API::AnalysisDataService::Instance().remove("FittedPeaksWS3");
+    }
+
+    // clean
+    API::AnalysisDataService::Instance().remove(fit_window_ws_name);
+    API::AnalysisDataService::Instance().remove(peak_center_ws_name);
+
+    return;
+  }
+
   //----------------------------------------------------------------------------------------------
   /**
    * @brief test_multiPeaksMultiSpectra
@@ -654,6 +735,98 @@ public:
     return;
   }
 
+  //----------------------------------------------------------------------------------------------
+  /** Test the optional output for fit error of each peak parameters
+   * It is modified from test_multiple_peak_profiles
+   * @brief test_outputFitError
+   */
+  void test_outputFitError() {
+    // set up parameters with starting value
+    std::vector<string> peakparnames;
+    std::vector<double> peakparvalues;
+    createGuassParameters(peakparnames, peakparvalues);
+
+    // Generate input workspace
+    createTestData(m_inputWorkspaceName);
+
+    // initialize algorithm to test
+    FitPeaks fitpeaks;
+
+    fitpeaks.initialize();
+    TS_ASSERT(fitpeaks.isInitialized());
+
+    TS_ASSERT_THROWS_NOTHING(
+        fitpeaks.setProperty("InputWorkspace", m_inputWorkspaceName));
+    TS_ASSERT_THROWS_NOTHING(fitpeaks.setProperty("StartWorkspaceIndex", 0));
+    TS_ASSERT_THROWS_NOTHING(fitpeaks.setProperty("StopWorkspaceIndex", 2));
+    TS_ASSERT_THROWS_NOTHING(fitpeaks.setProperty("PeakCenters", "5.0, 10.0"));
+    TS_ASSERT_THROWS_NOTHING(
+        fitpeaks.setProperty("FitWindowBoundaryList", "2.5, 6.5, 8.0, 12.0"));
+    TS_ASSERT_THROWS_NOTHING(fitpeaks.setProperty("FitFromRight", true));
+    TS_ASSERT_THROWS_NOTHING(
+        fitpeaks.setProperty("PeakParameterNames", peakparnames));
+    TS_ASSERT_THROWS_NOTHING(
+        fitpeaks.setProperty("PeakParameterValues", peakparvalues));
+    TS_ASSERT_THROWS_NOTHING(fitpeaks.setProperty("HighBackground", false));
+    TS_ASSERT_THROWS_NOTHING(
+        fitpeaks.setProperty("ConstrainPeakPositions", true));
+
+    fitpeaks.setProperty("OutputWorkspace", "PeakPositionsWS");
+    fitpeaks.setProperty("FittedPeaksWorkspace", "FittedPeaksWS");
+
+    fitpeaks.setProperty("RawPeakParameters", true);
+    fitpeaks.setProperty("OutputPeakParametersWorkspace", "PeakParametersWS");
+    TS_ASSERT_THROWS_NOTHING(fitpeaks.setPropertyValue(
+        "OutputParameterFitErrorsWorkspace", "FitErrorsWS"));
+
+    fitpeaks.execute();
+
+    // check result
+    TS_ASSERT(fitpeaks.isExecuted());
+    if (!fitpeaks.isExecuted())
+      return;
+
+    // get fitted peak data
+    API::MatrixWorkspace_sptr main_out_ws =
+        boost::dynamic_pointer_cast<API::MatrixWorkspace>(
+            AnalysisDataService::Instance().retrieve("PeakPositionsWS"));
+    TS_ASSERT(main_out_ws);
+    TS_ASSERT_EQUALS(main_out_ws->getNumberHistograms(), 3);
+
+    API::MatrixWorkspace_sptr plot_ws =
+        boost::dynamic_pointer_cast<API::MatrixWorkspace>(
+            AnalysisDataService::Instance().retrieve("FittedPeaksWS"));
+    TS_ASSERT(plot_ws);
+    TS_ASSERT_EQUALS(plot_ws->getNumberHistograms(), 3);
+
+    API::ITableWorkspace_sptr param_ws =
+        boost::dynamic_pointer_cast<API::ITableWorkspace>(
+            AnalysisDataService::Instance().retrieve("PeakParametersWS"));
+    TS_ASSERT(param_ws);
+    TS_ASSERT_EQUALS(param_ws->rowCount(), 6);
+    API::ITableWorkspace_sptr error_table =
+        boost::dynamic_pointer_cast<API::ITableWorkspace>(
+            AnalysisDataService::Instance().retrieve("FitErrorsWS"));
+    TS_ASSERT(error_table);
+    // shall be same number of rows to OutputPeakParametersWorkspace
+    // (PeakParametersWS)
+    TS_ASSERT_EQUALS(error_table->rowCount(), param_ws->rowCount());
+    // there is no Chi2 column in error table
+    TS_ASSERT_EQUALS(error_table->columnCount(), param_ws->columnCount() - 1);
+
+    // check fit error
+    for (size_t irow = 0; irow < param_ws->rowCount(); ++irow) {
+      continue;
+    }
+
+    // clean up
+    AnalysisDataService::Instance().remove(m_inputWorkspaceName);
+    AnalysisDataService::Instance().remove("PeakPositionsWS");
+    AnalysisDataService::Instance().remove("FittedPeaksWS");
+    AnalysisDataService::Instance().remove("PeakParametersWS");
+    AnalysisDataService::Instance().remove("FitErrorsWS");
+  }
+
   //----------------------------------------------------------------------------------------------
   /** Generate peak parameters for Back-to-back exponential convoluted by
    * Gaussian
@@ -691,7 +864,83 @@ public:
   }
 
   //--------------------------------------------------------------------------------------------------------------
-  /** create basic testing data set containing some Gaussian peaks
+  /** generate a peak center workspace compatible to the workspace created by
+   * createTestData(), which will 3 spectra and at most 2 elements for each
+   * sepctrum
+   * @brief genPeakCenterWorkspace
+   * @param peak_index_vec :: a vector of integer as 0 or 1.  0 for peak center
+   * @param workspace_name
+   * @return
+   */
+  std::string genPeakCenterWorkspace(const std::vector<int> &peak_index_vec,
+                                     const std::string &workspace_name) {
+    // create the empty workspace containing N X values for N peaks (N <=2)
+    size_t num_peaks = peak_index_vec.size();
+    int64_t nbins = num_peaks;
+    // fixed to 2 spectrum
+    int64_t nhist = 3;
+    // point data
+    bool ishist = false;
+    double xval(0), yval(0), eval(0), dxval(1);
+    std::set<int64_t> maskedws;
+    MatrixWorkspace_sptr center_ws =
+        boost::dynamic_pointer_cast<MatrixWorkspace>(
+            WorkspaceCreationHelper::create2DWorkspaceWithValuesAndXerror(
+                nhist, nbins, ishist, xval, yval, eval, dxval, maskedws));
+
+    std::cout << "Center workspace has " << center_ws->readX(0).size()
+              << " bins"
+              << "\n";
+    for (size_t i = 0; i < center_ws->getNumberHistograms(); ++i) {
+      for (size_t j = 0; j < peak_index_vec.size(); ++j) {
+        const int peak_index = peak_index_vec[j];
+        const double peak_center = peak_index == 0 ? 5.0 : 10.0;
+        center_ws->dataX(i)[j] = peak_center;
+      }
+    }
+
+    AnalysisDataService::Instance().addOrReplace(workspace_name, center_ws);
+
+    return workspace_name;
+  }
+
+  //--------------------------------------------------------------------------------------------------------------
+  /** create a fit window workspace compatibel to the workspace created by
+   * createTestData()
+   * @brief genFitWindowWorkspace :: generate a matrix work for peak fitting
+   * window
+   * @param peak_index_vec :: vector for peak indexes
+   * @param workspace_name :: name of the output workspace registered to ADS
+   */
+  std::string genFitWindowWorkspace(std::vector<int> &peak_index_vec,
+                                    const std::string &workspace_name) {
+    // create the empty workspace containing 3 spectrum
+    const size_t num_peaks = peak_index_vec.size();
+    MatrixWorkspace_sptr center_ws =
+        boost::dynamic_pointer_cast<MatrixWorkspace>(
+            WorkspaceCreationHelper::create2DWorkspace(
+                3, static_cast<int>(num_peaks) * 2));
+    for (size_t i = 0; i < center_ws->getNumberHistograms(); ++i) {
+      for (size_t j = 0; j < peak_index_vec.size(); ++j) {
+        const int peak_index = peak_index_vec[j];
+        const double peak_center = peak_index == 0 ? 5.0 : 10.0;
+        center_ws->dataX(i)[j * 2] = peak_center - 2.0;
+        center_ws->dataX(i)[j * 2 + 1] = peak_center + 2.0;
+      }
+    }
+
+    AnalysisDataService::Instance().addOrReplace(workspace_name, center_ws);
+
+    return workspace_name;
+  }
+
+  //--------------------------------------------------------------------------------------------------------------
+  /** create basic testing data set having 3 spectra, each containing 2 Gaussian
+   * peaks
+   * The exact location of the peaks are at
+   * ws-index = 0: peak 0 @ 5.00; peak 1  @ 10.00
+   * ws-index = 1: peak 0 @ 5.01; peak 1  @  9.98
+   * ws-index = 2: peak 0 @ 5.03; peak 1  @ 10.02
    * @brief createTestData
    * @param workspacename
    */
diff --git a/Framework/Algorithms/test/MergeRunsTest.h b/Framework/Algorithms/test/MergeRunsTest.h
index 8977e0beb170f5f7579f464b952a6f93303fe7f4..4c855637c2a16ec1d92f7c36956d6853d627a475 100644
--- a/Framework/Algorithms/test/MergeRunsTest.h
+++ b/Framework/Algorithms/test/MergeRunsTest.h
@@ -4,8 +4,8 @@
 //     NScD Oak Ridge National Laboratory, European Spallation Source
 //     & Institut Laue - Langevin
 // SPDX - License - Identifier: GPL - 3.0 +
-#ifndef MERGERUNSTEST_H_
-#define MERGERUNSTEST_H_
+#ifndef MANTID_ALGORITHMS_MERGERUNSTEST_H_
+#define MANTID_ALGORITHMS_MERGERUNSTEST_H_
 
 #include "MantidTestHelpers/WorkspaceCreationHelper.h"
 #include <cxxtest/TestSuite.h>
@@ -186,10 +186,10 @@ private:
     // add tolerances
     a->instrumentParameters().addString(
         a->getInstrument()->getComponentID(),
-        SampleLogsBehaviour::FAIL_MERGE_TOLERANCES, tolerances);
+        MergeRunsParameter::FAIL_MERGE_TOLERANCES, tolerances);
     b->instrumentParameters().addString(
         b->getInstrument()->getComponentID(),
-        SampleLogsBehaviour::FAIL_MERGE_TOLERANCES, tolerances);
+        MergeRunsParameter::FAIL_MERGE_TOLERANCES, tolerances);
 
     WorkspaceGroup_sptr group = boost::make_shared<WorkspaceGroup>();
     group->addWorkspace(a);
@@ -262,7 +262,7 @@ private:
 
     c->instrumentParameters().addString(
         c->getInstrument()->getComponentID(),
-        SampleLogsBehaviour::FAIL_MERGE_TOLERANCES, tolerances);
+        MergeRunsParameter::FAIL_MERGE_TOLERANCES, tolerances);
 
     AnalysisDataService::Instance().addOrReplace("c1", c);
 
@@ -287,9 +287,9 @@ private:
     TS_ASSERT(wsOut != nullptr);
     for (size_t j = 0; j < wsOut->getNumberHistograms(); ++j) {
       using Mantid::MantidVec;
-      auto &xValues = wsOut->x(j);
-      auto &yValues = wsOut->y(j);
-      auto &eValues = wsOut->e(j);
+      const auto &xValues = wsOut->x(j);
+      const auto &yValues = wsOut->y(j);
+      const auto &eValues = wsOut->e(j);
       TS_ASSERT_EQUALS(nXValues, xValues.size());
       // Loop through each y-value in the histogram
       for (size_t k = 0; k < yValues.size(); ++k) {
@@ -831,7 +831,7 @@ public:
         output = AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(
             "outer"));
 
-    auto &X = output->x(0);
+    const auto &X = output->x(0);
     TS_ASSERT_EQUALS(X.size(), 8);
     int i;
     for (i = 0; i < 3; ++i) {
@@ -859,7 +859,7 @@ public:
         output = AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(
             "outer"));
 
-    auto &X = output->x(0);
+    const auto &X = output->x(0);
     TS_ASSERT_EQUALS(X.size(), 8);
     int i;
     for (i = 0; i < 2; ++i) {
@@ -944,9 +944,9 @@ public:
       // Loop through each histogram in each workspace
       for (size_t j = 0; j < ws->getNumberHistograms(); ++j) {
         using Mantid::MantidVec;
-        auto &xValues = ws->x(j);
-        auto &yValues = ws->y(j);
-        auto &eValues = ws->e(j);
+        const auto &xValues = ws->x(j);
+        const auto &yValues = ws->y(j);
+        const auto &eValues = ws->e(j);
         TS_ASSERT_EQUALS(nXValues, xValues.size());
         // Loop through each y-value in the histogram
         for (size_t k = 0; k < yValues.size(); ++k) {
@@ -1025,11 +1025,11 @@ public:
 
     TS_ASSERT_EQUALS(output->y(0).front(), 2.0 * filesMerged);
 
-    if (mergeType.compare(SampleLogsBehaviour::TIME_SERIES_MERGE) == 0) {
-      prop = output->mutableRun().getTimeSeriesProperty<double>(propertyName);
+    if (mergeType.compare(MergeRunsParameter::TIME_SERIES_MERGE) == 0) {
+      prop = output->run().getTimeSeriesProperty<double>(propertyName);
       TS_ASSERT_EQUALS(prop->value(), result);
     } else {
-      prop = output->mutableRun().getLogData(propertyName);
+      prop = output->run().getLogData(propertyName);
       TS_ASSERT_EQUALS(prop->value(), result);
     }
 
@@ -1053,14 +1053,14 @@ public:
   }
 
   void test_mergeSampleLogs_sum() {
-    std::string mergeType = SampleLogsBehaviour::SUM_MERGE;
+    const std::string mergeType = MergeRunsParameter::SUM_MERGE;
     auto ws = create_group_workspace_with_sample_logs<double>(
         mergeType, "prop1", 1.2, 2.3, 0.0, 0.0);
     do_test_mergeSampleLogs(ws, "prop1", mergeType, "3.5", 2);
   }
 
   void test_mergeSampleLogs_time_series() {
-    std::string mergeType = SampleLogsBehaviour::TIME_SERIES_MERGE;
+    const std::string mergeType = MergeRunsParameter::TIME_SERIES_MERGE;
     auto ws = create_group_workspace_with_sample_logs<double>(
         mergeType, "prop1", 1.0, 2.0, 0.0, 0.0);
     do_test_mergeSampleLogs(
@@ -1069,7 +1069,7 @@ public:
   }
 
   void test_mergeSampleLogs_time_series_multiple() {
-    std::string mergeType = SampleLogsBehaviour::TIME_SERIES_MERGE;
+    const std::string mergeType = MergeRunsParameter::TIME_SERIES_MERGE;
     auto ws = create_group_workspace_with_sample_logs<double>(
         mergeType, "prop1, prop2", 1.0, 2.0, 3.0, 4.0);
     do_test_mergeSampleLogs(
@@ -1078,21 +1078,21 @@ public:
   }
 
   void test_mergeSampleLogs_list() {
-    std::string mergeType = SampleLogsBehaviour::LIST_MERGE;
+    const std::string mergeType = MergeRunsParameter::LIST_MERGE;
     auto ws = create_group_workspace_with_sample_logs<double>(
         mergeType, "prop1", 1.0, 2.0, 0.0, 0.0);
     do_test_mergeSampleLogs(ws, "prop1", mergeType, "1, 2", 2);
   }
 
   void test_mergeSampleLogs_warn() {
-    std::string mergeType = SampleLogsBehaviour::WARN_MERGE;
+    const std::string mergeType = MergeRunsParameter::WARN_MERGE;
     auto ws = create_group_workspace_with_sample_logs<double>(
         mergeType, "prop1", 1.0, 2.0, 0.0, 0.0);
     do_test_mergeSampleLogs(ws, "prop1", mergeType, "1", 2);
   }
 
   void test_mergeSampleLogs_fail_where_params_are_equal_succeeds() {
-    std::string mergeType = SampleLogsBehaviour::FAIL_MERGE;
+    const std::string mergeType = MergeRunsParameter::FAIL_MERGE;
     auto ws = create_group_workspace_with_sample_logs<double>(
         mergeType, "prop1", 1.0, 1.0, 0.0, 0.0);
     do_test_mergeSampleLogs(ws, "prop1", mergeType, "1", 2);
@@ -1102,7 +1102,7 @@ public:
   }
 
   void test_mergeSampleLogs_fail_where_params_are_different_fails() {
-    std::string mergeType = SampleLogsBehaviour::FAIL_MERGE;
+    const std::string mergeType = MergeRunsParameter::FAIL_MERGE;
     auto ws = create_group_workspace_with_sample_logs<double>(
         mergeType, "prop1", 1.0, 2.0, 0.0, 0.0);
     do_test_mergeSampleLogs(ws, "prop1", mergeType, "1", 1);
@@ -1110,7 +1110,7 @@ public:
 
   void
   test_mergeSampleLogs_fail_where_params_are_different_but_inside_tolerance_succeeds() {
-    std::string mergeType = SampleLogsBehaviour::FAIL_MERGE;
+    const std::string mergeType = MergeRunsParameter::FAIL_MERGE;
     auto ws = create_group_workspace_with_sample_logs<double>(
         mergeType, "prop1", 1.0, 2.0, 0.0, 0.0, "2.0");
     do_test_mergeSampleLogs(ws, "prop1", mergeType, "1", 2);
@@ -1118,7 +1118,7 @@ public:
 
   void
   test_mergeSampleLogs_fail_where_params_are_different_but_outside_tolerance_fails() {
-    std::string mergeType = SampleLogsBehaviour::FAIL_MERGE;
+    const std::string mergeType = MergeRunsParameter::FAIL_MERGE;
     auto ws = create_group_workspace_with_sample_logs<double>(
         mergeType, "prop1", 1.0, 2.0, 0.0, 0.0, "0.5");
     do_test_mergeSampleLogs(ws, "prop1", mergeType, "1", 1);
@@ -1126,7 +1126,7 @@ public:
 
   void
   test_mergeSampleLogs_fail_where_params_with_one_outside_tolerance_fails_multiple_tolerances() {
-    std::string mergeType = SampleLogsBehaviour::FAIL_MERGE;
+    const std::string mergeType = MergeRunsParameter::FAIL_MERGE;
     auto ws = create_group_workspace_with_sample_logs<double>(
         mergeType, "prop1, prop2", 1.0, 2.0, 3.0, 4.0, "0.5, 1.5");
     do_test_mergeSampleLogs(ws, "prop1", mergeType, "1", 1);
@@ -1134,14 +1134,14 @@ public:
 
   void
   test_mergeSampleLogs_fail_where_params_with_both_tolerances_outside_fails() {
-    std::string mergeType = SampleLogsBehaviour::FAIL_MERGE;
+    const std::string mergeType = MergeRunsParameter::FAIL_MERGE;
     auto ws = create_group_workspace_with_sample_logs<double>(
         mergeType, "prop1, prop2", 1.0, 2.0, 3.0, 4.0, "0.5");
     do_test_mergeSampleLogs(ws, "prop1", mergeType, "1", 1);
   }
 
   void test_mergeSampleLogs_fail() {
-    std::string mergeType = SampleLogsBehaviour::FAIL_MERGE;
+    const std::string mergeType = MergeRunsParameter::FAIL_MERGE;
     auto ws = create_group_workspace_with_sample_logs<double>(
         mergeType, "prop1", 1.0, 2.0, 0.0, 0.0);
     do_test_mergeSampleLogs(ws, "prop1", mergeType, "1", 1);
@@ -1151,7 +1151,7 @@ public:
   }
 
   void test_mergeSampleLogs_non_existent_log_is_ignored() {
-    std::string mergeType = SampleLogsBehaviour::TIME_SERIES_MERGE;
+    const std::string mergeType = MergeRunsParameter::TIME_SERIES_MERGE;
     WorkspaceGroup_sptr gws = create_group_workspace_with_sample_logs<double>(
         mergeType, "prop1", 1.0, 2.0, 0.0, 0.0);
     MatrixWorkspace_sptr a =
@@ -1164,7 +1164,8 @@ public:
   }
 
   void test_mergeSampleLogs_log_used_twice_with_same_merge_type_throws_error() {
-    std::string mergeTypeTimeSeries = SampleLogsBehaviour::TIME_SERIES_MERGE;
+    const std::string mergeTypeTimeSeries =
+        MergeRunsParameter::TIME_SERIES_MERGE;
     WorkspaceGroup_sptr gws = create_group_workspace_with_sample_logs<double>(
         mergeTypeTimeSeries, "prop1", 1.0, 2.0, 0.0, 0.0);
 
@@ -1179,8 +1180,9 @@ public:
 
   void
   test_mergeSampleLogs_log_used_twice_with_incompatible_merge_types_fails() {
-    std::string mergeTypeTimeSeries = SampleLogsBehaviour::TIME_SERIES_MERGE;
-    std::string mergeTypeList = SampleLogsBehaviour::LIST_MERGE;
+    const std::string mergeTypeTimeSeries =
+        MergeRunsParameter::TIME_SERIES_MERGE;
+    const std::string mergeTypeList = MergeRunsParameter::LIST_MERGE;
     WorkspaceGroup_sptr gws = create_group_workspace_with_sample_logs<double>(
         mergeTypeTimeSeries, "prop1", 1.0, 2.0, 0.0, 0.0);
     MatrixWorkspace_sptr a =
@@ -1196,8 +1198,9 @@ public:
 
   void
   test_mergeSampleLogs_log_used_twice_with_compatible_merge_types_suceeds() {
-    std::string mergeTypeTimeSeries = SampleLogsBehaviour::TIME_SERIES_MERGE;
-    std::string mergeTypeWarn = SampleLogsBehaviour::WARN_MERGE;
+    const std::string mergeTypeTimeSeries =
+        MergeRunsParameter::TIME_SERIES_MERGE;
+    const std::string mergeTypeWarn = MergeRunsParameter::WARN_MERGE;
     WorkspaceGroup_sptr gws = create_group_workspace_with_sample_logs<double>(
         mergeTypeTimeSeries, "prop1", 1.0, 2.0, 0.0, 0.0);
     MatrixWorkspace_sptr a =
@@ -1212,7 +1215,7 @@ public:
   }
 
   void test_mergeSampleLogs_non_numeric_property_fails_to_merge() {
-    std::string mergeType = SampleLogsBehaviour::SUM_MERGE;
+    const std::string mergeType = MergeRunsParameter::SUM_MERGE;
     do_test_mergeSampleLogs(
         create_group_workspace_with_sample_logs<std::string>(
             mergeType, "prop1", "1", "two", "", ""),
@@ -1221,7 +1224,7 @@ public:
 
   void
   test_mergeSampleLogs_non_numeric_property_in_first_ws_skips_merging_parameter() {
-    std::string mergeType = SampleLogsBehaviour::TIME_SERIES_MERGE;
+    const std::string mergeType = MergeRunsParameter::TIME_SERIES_MERGE;
     auto ws = create_group_workspace_with_sample_logs<std::string>(
         mergeType, "prop1", "one", "two", "", "");
     // should get stuck when trying to get "prop1" as a time series
@@ -1232,64 +1235,64 @@ public:
 
   void test_mergeSampleLogs_with_additional_time_series_property() {
     WorkspaceGroup_sptr ws = create_group_workspace_with_sample_logs<double>(
-        SampleLogsBehaviour::TIME_SERIES_MERGE, "prop1", 1.0, 2.0, 3.0, 4.0);
+        MergeRunsParameter::TIME_SERIES_MERGE, "prop1", 1.0, 2.0, 3.0, 4.0);
 
     MergeRuns alg;
     alg.initialize();
     alg.setPropertyValue("SampleLogsTimeSeries", "prop2");
     do_test_mergeSampleLogs_modified_alg(
-        alg, ws, "prop2", SampleLogsBehaviour::TIME_SERIES_MERGE,
+        alg, ws, "prop2", MergeRunsParameter::TIME_SERIES_MERGE,
         "2013-Jun-25 10:59:15  3\n2013-Jun-25 11:59:15  4\n", 2);
   }
 
   void test_mergeSampleLogs_with_additional_list_property() {
     WorkspaceGroup_sptr ws = create_group_workspace_with_sample_logs<double>(
-        SampleLogsBehaviour::TIME_SERIES_MERGE, "prop1", 1.0, 2.0, 3.0, 4.0);
+        MergeRunsParameter::TIME_SERIES_MERGE, "prop1", 1.0, 2.0, 3.0, 4.0);
 
     MergeRuns alg;
     alg.initialize();
     alg.setPropertyValue("SampleLogsList", "prop2");
     do_test_mergeSampleLogs_modified_alg(
-        alg, ws, "prop2", SampleLogsBehaviour::LIST_MERGE, "3, 4", 2);
+        alg, ws, "prop2", MergeRunsParameter::LIST_MERGE, "3, 4", 2);
   }
 
   void test_mergeSampleLogs_with_additional_warn_property() {
     WorkspaceGroup_sptr ws = create_group_workspace_with_sample_logs<double>(
-        SampleLogsBehaviour::TIME_SERIES_MERGE, "prop1", 1.0, 2.0, 3.0, 4.0);
+        MergeRunsParameter::TIME_SERIES_MERGE, "prop1", 1.0, 2.0, 3.0, 4.0);
 
     MergeRuns alg;
     alg.initialize();
     alg.setPropertyValue("SampleLogsWarn", "prop2");
     do_test_mergeSampleLogs_modified_alg(
-        alg, ws, "prop2", SampleLogsBehaviour::WARN_MERGE, "3", 2);
+        alg, ws, "prop2", MergeRunsParameter::WARN_MERGE, "3", 2);
   }
 
   void test_mergeSampleLogs_with_additional_fail_property() {
     WorkspaceGroup_sptr ws = create_group_workspace_with_sample_logs<double>(
-        SampleLogsBehaviour::TIME_SERIES_MERGE, "prop1", 1.0, 2.0, 3.0, 4.0);
+        MergeRunsParameter::TIME_SERIES_MERGE, "prop1", 1.0, 2.0, 3.0, 4.0);
 
     MergeRuns alg;
     alg.initialize();
     alg.setPropertyValue("SampleLogsFail", "prop2");
     alg.setPropertyValue("SampleLogsFailTolerances", "0.5");
     do_test_mergeSampleLogs_modified_alg(
-        alg, ws, "prop2", SampleLogsBehaviour::FAIL_MERGE, "3", 1);
+        alg, ws, "prop2", MergeRunsParameter::FAIL_MERGE, "3", 1);
   }
 
   void
   test_mergeSampleLogs_time_series_overwriting_in_merge_behaviour_in_algorithm() {
-    std::string mergeType = SampleLogsBehaviour::TIME_SERIES_MERGE;
+    const std::string mergeType = MergeRunsParameter::TIME_SERIES_MERGE;
     auto ws = create_group_workspace_with_sample_logs<double>(
         mergeType, "prop1", 1.0, 2.0, 0.0, 0.0);
     MergeRuns alg;
     alg.initialize();
     alg.setPropertyValue("SampleLogsList", "prop1");
     do_test_mergeSampleLogs_modified_alg(
-        alg, ws, "prop1", SampleLogsBehaviour::LIST_MERGE, "1, 2", 2);
+        alg, ws, "prop1", MergeRunsParameter::LIST_MERGE, "1, 2", 2);
   }
 
   void test_mergeSampleLogs_time_series_overwriting_tolerance_in_algorithm() {
-    std::string mergeType = SampleLogsBehaviour::FAIL_MERGE;
+    const std::string mergeType = MergeRunsParameter::FAIL_MERGE;
     auto ws = create_group_workspace_with_sample_logs<double>(
         mergeType, "prop1", 1.0, 2.0, 0.0, 0.0, "0.5");
 
@@ -1302,19 +1305,19 @@ public:
 
   void test_mergeSampleLogs_sum_and_error_skips_merging_second_file() {
     auto ws = create_group_workspace_with_sample_logs<double>(
-        SampleLogsBehaviour::SUM_MERGE, "prop1", 1.0, 2.0, 3.0, 4.0, "0.5");
+        MergeRunsParameter::SUM_MERGE, "prop1", 1.0, 2.0, 3.0, 4.0, "0.5");
 
     MergeRuns alg;
     alg.initialize();
     alg.setPropertyValue("SampleLogsFail", "prop2");
     alg.setPropertyValue("SampleLogsFailTolerances", "0.5");
-    do_test_mergeSampleLogs_modified_alg(
-        alg, ws, "prop1", SampleLogsBehaviour::SUM_MERGE, "1", 1);
+    do_test_mergeSampleLogs_modified_alg(alg, ws, "prop1",
+                                         MergeRunsParameter::SUM_MERGE, "1", 1);
   }
 
   void test_mergeSampleLogs_time_series_and_error_skips_merging_second_file() {
     auto ws = create_group_workspace_with_sample_logs<double>(
-        SampleLogsBehaviour::TIME_SERIES_MERGE, "prop1", 1.0, 2.0, 3.0, 4.0,
+        MergeRunsParameter::TIME_SERIES_MERGE, "prop1", 1.0, 2.0, 3.0, 4.0,
         "0.5");
 
     MergeRuns alg;
@@ -1322,24 +1325,24 @@ public:
     alg.setPropertyValue("SampleLogsFail", "prop2");
     alg.setPropertyValue("SampleLogsFailTolerances", "0.5");
     do_test_mergeSampleLogs_modified_alg(alg, ws, "prop1",
-                                         SampleLogsBehaviour::TIME_SERIES_MERGE,
+                                         MergeRunsParameter::TIME_SERIES_MERGE,
                                          "2013-Jun-25 10:59:15  1\n", 1);
   }
 
   void test_mergeSampleLogs_list_and_error_skips_merging_second_file() {
     auto ws = create_group_workspace_with_sample_logs<double>(
-        SampleLogsBehaviour::LIST_MERGE, "prop1", 1.0, 2.0, 3.0, 4.0, "0.5");
+        MergeRunsParameter::LIST_MERGE, "prop1", 1.0, 2.0, 3.0, 4.0, "0.5");
 
     MergeRuns alg;
     alg.initialize();
     alg.setPropertyValue("SampleLogsFail", "prop2");
     alg.setPropertyValue("SampleLogsFailTolerances", "0.5");
     do_test_mergeSampleLogs_modified_alg(
-        alg, ws, "prop1", SampleLogsBehaviour::LIST_MERGE, "1", 1);
+        alg, ws, "prop1", MergeRunsParameter::LIST_MERGE, "1", 1);
   }
 
   void test_merging_three_workspace_with_time_series() {
-    std::string mergeType = SampleLogsBehaviour::TIME_SERIES_MERGE;
+    const std::string mergeType = MergeRunsParameter::TIME_SERIES_MERGE;
 
     auto ws = create_group_workspace_with_sample_logs<double>(
         mergeType, "prop1", 1.0, 2.0, 3.0, 4.0);
@@ -1379,13 +1382,13 @@ public:
 
   void test_merging_two_workspace_then_third_with_time_series() {
     do_test_merge_two_workspaces_then_third(
-        SampleLogsBehaviour::TIME_SERIES_MERGE, "2013-Jun-25 10:59:15  "
-                                                "1\n2013-Jun-25 11:59:15  "
-                                                "2\n2013-Jun-25 12:59:15  5\n");
+        MergeRunsParameter::TIME_SERIES_MERGE, "2013-Jun-25 10:59:15  "
+                                               "1\n2013-Jun-25 11:59:15  "
+                                               "2\n2013-Jun-25 12:59:15  5\n");
   }
 
   void test_merging_two_workspace_then_third_with_list() {
-    do_test_merge_two_workspaces_then_third(SampleLogsBehaviour::LIST_MERGE,
+    do_test_merge_two_workspaces_then_third(MergeRunsParameter::LIST_MERGE,
                                             "1, 2, 5");
   }
 
@@ -1424,18 +1427,18 @@ public:
 
   void test_merging_two_workspace_then_two_already_merged_with_time_series() {
     do_test_merging_two_workspaces_both_already_merged(
-        SampleLogsBehaviour::TIME_SERIES_MERGE,
+        MergeRunsParameter::TIME_SERIES_MERGE,
         "2013-Jun-25 10:59:15  1\n2013-Jun-25 10:59:15  6\n2013-Jun-25 "
         "11:59:15  2\n2013-Jun-25 11:59:15  7\n");
   }
 
   void test_merging_two_workspace_then_two_already_merged_with_list() {
     do_test_merging_two_workspaces_both_already_merged(
-        SampleLogsBehaviour::LIST_MERGE, "1, 2, 6, 7");
+        MergeRunsParameter::LIST_MERGE, "1, 2, 6, 7");
   }
 
   void test_merging_single_workspace() {
-    std::string mergeType = SampleLogsBehaviour::TIME_SERIES_MERGE;
+    const std::string mergeType = MergeRunsParameter::TIME_SERIES_MERGE;
 
     auto ws = create_group_workspace_with_sample_logs<double>(
         mergeType, "prop1", 1.0, 2.0, 3.0, 4.0);
@@ -1454,7 +1457,7 @@ public:
 
   void test_mergeSampleLogs_fail_throwing_error() {
     WorkspaceGroup_sptr ws = create_group_workspace_with_sample_logs<double>(
-        SampleLogsBehaviour::TIME_SERIES_MERGE, "prop1", 1.0, 2.0, 3.0, 4.0);
+        MergeRunsParameter::TIME_SERIES_MERGE, "prop1", 1.0, 2.0, 3.0, 4.0);
 
     MergeRuns alg;
     alg.initialize();
@@ -1463,7 +1466,7 @@ public:
     alg.setPropertyValue("FailBehaviour",
                          RunCombinationOptions::STOP_BEHAVIOUR);
     do_test_mergeSampleLogs_modified_alg(
-        alg, ws, "prop2", SampleLogsBehaviour::FAIL_MERGE, "3", 1, true);
+        alg, ws, "prop2", MergeRunsParameter::FAIL_MERGE, "3", 1, true);
   }
 
   void rebin_one_workspace() {
@@ -1477,7 +1480,7 @@ public:
 
   void test_mergeSampleLogs_with_different_binning_skips_merging() {
     WorkspaceGroup_sptr ws = create_group_workspace_with_sample_logs<double>(
-        SampleLogsBehaviour::SUM_MERGE, "prop1", 1.0, 2.0, 3.0, 4.0);
+        MergeRunsParameter::SUM_MERGE, "prop1", 1.0, 2.0, 3.0, 4.0);
 
     rebin_one_workspace();
 
@@ -1485,14 +1488,14 @@ public:
     alg.initialize();
     alg.setPropertyValue("RebinBehaviour",
                          RunCombinationOptions::FAIL_BEHAVIOUR);
-    do_test_mergeSampleLogs_modified_alg(
-        alg, ws, "prop1", SampleLogsBehaviour::SUM_MERGE, "1", 1);
+    do_test_mergeSampleLogs_modified_alg(alg, ws, "prop1",
+                                         MergeRunsParameter::SUM_MERGE, "1", 1);
   }
 
   void
   test_mergeSampleLogs_with_different_binning_skips_merging_and_throws_error() {
     WorkspaceGroup_sptr ws = create_group_workspace_with_sample_logs<double>(
-        SampleLogsBehaviour::SUM_MERGE, "prop1", 1.0, 2.0, 3.0, 4.0);
+        MergeRunsParameter::SUM_MERGE, "prop1", 1.0, 2.0, 3.0, 4.0);
 
     rebin_one_workspace();
 
@@ -1503,18 +1506,18 @@ public:
     alg.setPropertyValue("FailBehaviour",
                          RunCombinationOptions::STOP_BEHAVIOUR);
     do_test_mergeSampleLogs_modified_alg(
-        alg, ws, "prop1", SampleLogsBehaviour::SUM_MERGE, "1", 1, true);
+        alg, ws, "prop1", MergeRunsParameter::SUM_MERGE, "1", 1, true);
   }
 
   void test_mergeSampleLogs_fail_with_single_negative_tolerance() {
-    std::string mergeType = SampleLogsBehaviour::FAIL_MERGE;
+    const std::string mergeType = MergeRunsParameter::FAIL_MERGE;
     auto ws = create_group_workspace_with_sample_logs<double>(
         mergeType, "prop1", 0.0, 0.0, 0.0, 0.0, "-1.0");
     do_test_mergeSampleLogs(ws, "prop1", mergeType, "1", 2, true);
   }
 
   void test_mergeSampleLogs_fail_with_single_negative_tolerance_in_a_list() {
-    std::string mergeType = SampleLogsBehaviour::FAIL_MERGE;
+    const std::string mergeType = MergeRunsParameter::FAIL_MERGE;
     auto ws = create_group_workspace_with_sample_logs<double>(
         mergeType, "prop1, prop2", 0.0, 0.0, 0.0, 0.0, "-0.5, 1.5");
     do_test_mergeSampleLogs(ws, "prop1", mergeType, "1", 1, true);
@@ -1859,7 +1862,7 @@ public:
       const auto &ws = WorkspaceCreationHelper::
           create2DDetectorScanWorkspaceWithFullInstrument(5000, 1, 25,
                                                           i * 1000);
-      std::string wsName = "a" + std::to_string(i);
+      const std::string wsName = "a" + std::to_string(i);
       AnalysisDataService::Instance().addOrReplace(wsName, ws);
     }
 
@@ -1873,7 +1876,7 @@ public:
 
   void tearDown() override {
     for (size_t i = 0; i < 10; ++i) {
-      std::string wsName = "a" + std::to_string(i);
+      const std::string wsName = "a" + std::to_string(i);
       AnalysisDataService::Instance().remove(wsName);
     }
     AnalysisDataService::Instance().remove("outputWS");
@@ -1883,4 +1886,4 @@ private:
   Mantid::Algorithms::MergeRuns m_mergeRuns;
 };
 
-#endif /*MERGERUNSTEST_H_*/
+#endif /*MANTID_ALGORITHMS_MERGERUNSTEST_H_*/
diff --git a/Framework/Algorithms/test/ParallaxCorrectionTest.h b/Framework/Algorithms/test/ParallaxCorrectionTest.h
new file mode 100644
index 0000000000000000000000000000000000000000..3c90d1e228cd68d3fbf7e005be0161b5fe613840
--- /dev/null
+++ b/Framework/Algorithms/test/ParallaxCorrectionTest.h
@@ -0,0 +1,255 @@
+// Mantid Repository : https://github.com/mantidproject/mantid
+//
+// Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
+//     NScD Oak Ridge National Laboratory, European Spallation Source
+//     & Institut Laue - Langevin
+// SPDX - License - Identifier: GPL - 3.0 +
+#ifndef MANTID_ALGORITHMS_PARALLAXCORRECTIONTEST_H_
+#define MANTID_ALGORITHMS_PARALLAXCORRECTIONTEST_H_
+
+#include <cxxtest/TestSuite.h>
+
+#include "MantidAPI/FrameworkManager.h"
+#include "MantidAPI/MatrixWorkspace_fwd.h"
+#include "MantidAlgorithms/CompareWorkspaces.h"
+#include "MantidAlgorithms/CreateSampleWorkspace.h"
+#include "MantidAlgorithms/ParallaxCorrection.h"
+#include "MantidAlgorithms/SetInstrumentParameter.h"
+#include "MantidGeometry/Instrument/DetectorInfo.h"
+#include "MantidKernel/V3D.h"
+
+using Mantid::API::FrameworkManager;
+using Mantid::API::MatrixWorkspace_sptr;
+using Mantid::Algorithms::CompareWorkspaces;
+using Mantid::Algorithms::CreateSampleWorkspace;
+using Mantid::Algorithms::ParallaxCorrection;
+using Mantid::Algorithms::SetInstrumentParameter;
+using Mantid::Geometry::DetectorInfo;
+using Mantid::Kernel::V3D;
+
+namespace {
+bool compare(MatrixWorkspace_sptr w1, MatrixWorkspace_sptr w2) {
+  CompareWorkspaces comparator;
+  comparator.initialize();
+  comparator.setChild(true);
+  comparator.setAlwaysStoreInADS(false);
+  comparator.setProperty("Workspace1", w1);
+  comparator.setProperty("Workspace2", w2);
+  comparator.execute();
+  bool result = comparator.getProperty("Result");
+  return result;
+}
+
+MatrixWorkspace_sptr createWorkspace(const int nPixelsPerBank = 3,
+                                     const int nBins = 2,
+                                     const bool withParameter = true) {
+  CreateSampleWorkspace creator;
+  creator.initialize();
+  creator.setChild(true);
+  creator.setAlwaysStoreInADS(false);
+  creator.setProperty("NumBanks", 1);
+  creator.setProperty("XMin", 1.);
+  creator.setProperty("XMax", 2.);
+  creator.setProperty("BinWidth", 1. / nBins);
+  creator.setProperty("BankPixelWidth", nPixelsPerBank);
+  creator.setProperty("Function", "One Peak");
+  creator.setProperty("XUnit", "Wavelength");
+  creator.setPropertyValue("OutputWorkspace", "__unused");
+  creator.execute();
+
+  MatrixWorkspace_sptr in = creator.getProperty("OutputWorkspace");
+
+  if (withParameter) {
+    SetInstrumentParameter setter;
+    setter.initialize();
+    setter.setChild(true);
+    setter.setAlwaysStoreInADS(false);
+
+    setter.setProperty("Workspace", in);
+    setter.setProperty("ParameterName", "direction");
+    setter.setProperty("ParameterType", "String");
+    setter.setProperty("ComponentName", "bank1");
+    setter.setProperty("Value", "y");
+    setter.execute();
+
+    setter.setProperty("Workspace", in);
+    setter.setProperty("ParameterName", "parallax");
+    setter.setProperty("ParameterType", "String");
+    setter.setProperty("ComponentName", "bank1");
+    setter.setProperty("Value", "1 + 0.1 * t");
+    setter.execute();
+  }
+
+  return in;
+}
+} // namespace
+
+class ParallaxCorrectionTest : 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 ParallaxCorrectionTest *createSuite() {
+    return new ParallaxCorrectionTest();
+  }
+  static void destroySuite(ParallaxCorrectionTest *suite) { delete suite; }
+
+  ParallaxCorrectionTest() { FrameworkManager::Instance(); }
+
+  void test_init() {
+    ParallaxCorrection alg;
+    TS_ASSERT_THROWS_NOTHING(alg.initialize())
+    TS_ASSERT(alg.isInitialized())
+  }
+
+  void test_wrong_component() {
+    MatrixWorkspace_sptr in = createWorkspace();
+    const std::vector<std::string> components = {"bank-of-america"};
+
+    ParallaxCorrection alg;
+    alg.setChild(true);
+    alg.setAlwaysStoreInADS(false);
+    alg.initialize();
+    alg.isInitialized();
+    alg.setRethrows(true);
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("InputWorkspace", in));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("ComponentNames", components));
+    TS_ASSERT_THROWS_NOTHING(
+        alg.setPropertyValue("OutputWorkspace", "__unused"));
+    TS_ASSERT_THROWS_NOTHING(alg.execute());
+    TS_ASSERT(alg.isExecuted());
+
+    MatrixWorkspace_sptr out = alg.getProperty("OutputWorkspace");
+    TS_ASSERT(out);
+    // No correction has been done, output is just a clone of the input
+    TS_ASSERT(compare(in, out))
+  }
+
+  void test_no_parameter() {
+    MatrixWorkspace_sptr in = createWorkspace(3, 2, false);
+    const std::vector<std::string> components = {"bank1"};
+
+    ParallaxCorrection alg;
+    alg.setChild(true);
+    alg.setAlwaysStoreInADS(false);
+    alg.initialize();
+    alg.isInitialized();
+    alg.setRethrows(true);
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("InputWorkspace", in));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("ComponentNames", components));
+    TS_ASSERT_THROWS_NOTHING(
+        alg.setPropertyValue("OutputWorkspace", "__unused"));
+    TS_ASSERT_THROWS_NOTHING(alg.execute());
+    TS_ASSERT(alg.isExecuted());
+
+    MatrixWorkspace_sptr out = alg.getProperty("OutputWorkspace");
+    TS_ASSERT(out);
+    // No correction has been done, output is just a clone of the input
+    TS_ASSERT(compare(in, out))
+  }
+
+  void test_wrong_formula_parameter() {
+    MatrixWorkspace_sptr in = createWorkspace(3, 2);
+    const std::vector<std::string> components = {"bank1"};
+
+    SetInstrumentParameter setter;
+    setter.initialize();
+    setter.setChild(true);
+    setter.setAlwaysStoreInADS(false);
+
+    setter.setProperty("Workspace", in);
+    setter.setProperty("ParameterName", "direction");
+    setter.setProperty("ParameterType", "String");
+    setter.setProperty("ComponentName", "bank1");
+    setter.setProperty("Value", "y");
+    setter.execute();
+
+    setter.setProperty("Workspace", in);
+    setter.setProperty("ParameterName", "parallax");
+    setter.setProperty("ParameterType", "String");
+    setter.setProperty("ComponentName", "bank1");
+    // this is a wrong formula
+    setter.setProperty("Value", "1 + 0.1 * t + x");
+    setter.execute();
+
+    ParallaxCorrection alg;
+    alg.setChild(true);
+    alg.setAlwaysStoreInADS(false);
+    alg.initialize();
+    alg.isInitialized();
+    alg.setRethrows(true);
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("InputWorkspace", in));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("ComponentNames", components));
+    TS_ASSERT_THROWS_NOTHING(
+        alg.setPropertyValue("OutputWorkspace", "__unused"));
+    TS_ASSERT_THROWS_NOTHING(alg.execute());
+    TS_ASSERT(alg.isExecuted());
+
+    MatrixWorkspace_sptr out = alg.getProperty("OutputWorkspace");
+    TS_ASSERT(out);
+    // No correction has been done, output is just a clone of the input
+    TS_ASSERT(compare(in, out))
+  }
+
+  void test_exec() {
+
+    MatrixWorkspace_sptr in = createWorkspace();
+    const std::vector<std::string> components = {"bank1"};
+
+    ParallaxCorrection alg;
+    alg.setChild(true);
+    alg.setAlwaysStoreInADS(false);
+    alg.initialize();
+    alg.isInitialized();
+    alg.setRethrows(true);
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("InputWorkspace", in));
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty("ComponentNames", components));
+    TS_ASSERT_THROWS_NOTHING(
+        alg.setPropertyValue("OutputWorkspace", "__unused"));
+    TS_ASSERT_THROWS_NOTHING(alg.execute());
+    TS_ASSERT(alg.isExecuted());
+
+    MatrixWorkspace_sptr out = alg.getProperty("OutputWorkspace");
+    TS_ASSERT(out);
+    // Divide the input with the output to get just the correction
+    in /= out;
+
+    auto &detectorInfo = in->detectorInfo();
+    for (size_t index = 0; index < in->getNumberHistograms(); ++index) {
+      const V3D pos = detectorInfo.position(index);
+      const double expectation =
+          1. + 0.1 * std::abs(std::atan2(pos.X(), pos.Z()));
+      const double reality = in->y(index)[0];
+      TS_ASSERT_DELTA(expectation, reality, 1E-10);
+    }
+  }
+};
+
+class ParallaxCorrectionTestPerformance : public CxxTest::TestSuite {
+public:
+  static ParallaxCorrectionTestPerformance *createSuite() {
+    return new ParallaxCorrectionTestPerformance();
+  }
+  static void destroySuite(ParallaxCorrectionTestPerformance *suite) {
+    delete suite;
+  }
+
+  void setUp() override {
+    FrameworkManager::Instance();
+    m_alg.initialize();
+    m_alg.setChild(true);
+    m_alg.setAlwaysStoreInADS(false);
+    m_alg.setRethrows(true);
+    MatrixWorkspace_sptr in = createWorkspace(1000, 100);
+    const std::vector<std::string> components = {"bank1"};
+    m_alg.setProperty("InputWorkspace", in);
+    m_alg.setProperty("ComponentNames", components);
+    m_alg.setProperty("OutputWorkspace", "__out");
+  }
+
+  void test_performance() { TS_ASSERT_THROWS_NOTHING(m_alg.execute()) }
+
+private:
+  ParallaxCorrection m_alg;
+};
+
+#endif /* MANTID_ALGORITHMS_PARALLAXCORRECTIONTEST_H_ */
diff --git a/Framework/Algorithms/test/ReflectometryReductionOneAuto2Test.h b/Framework/Algorithms/test/ReflectometryReductionOneAuto2Test.h
index df6eb8e43654e6d6a90a318e935f7aab8d6e541b..0bbc3a825b77bef0bd7a9140cf7e139077d10186 100644
--- a/Framework/Algorithms/test/ReflectometryReductionOneAuto2Test.h
+++ b/Framework/Algorithms/test/ReflectometryReductionOneAuto2Test.h
@@ -728,10 +728,10 @@ public:
     alg.setProperty("ProcessingInstructions", "2");
     alg.setProperty("MomentumTransferStep", 0.04);
     alg.setProperty("PolarizationAnalysis", "PA");
-    alg.setProperty("Pp", "0.9,0,0");
-    alg.setProperty("Ap", "0.8,0,0");
-    alg.setProperty("Rho", "0.7778,0,0");
-    alg.setProperty("Alpha", "0.75,0");
+    alg.setProperty("CPp", "0.9,0,0");
+    alg.setProperty("CAp", "0.8,0,0");
+    alg.setProperty("CRho", "0.7778,0,0");
+    alg.setProperty("CAlpha", "0.75,0");
     alg.setPropertyValue("OutputWorkspace", "IvsQ");
     alg.setPropertyValue("OutputWorkspaceBinned", "IvsQ_binned");
     alg.setPropertyValue("OutputWorkspaceWavelength", "IvsLam");
@@ -776,10 +776,10 @@ public:
     alg.setProperty("ProcessingInstructions", "2");
     alg.setProperty("MomentumTransferStep", 0.04);
     alg.setProperty("PolarizationAnalysis", "PNR");
-    alg.setProperty("Pp", "1,1,2");
-    alg.setProperty("Ap", "1,1,2");
-    alg.setProperty("Rho", "1,1");
-    alg.setProperty("Alpha", "1");
+    alg.setProperty("CPp", "1,1,2");
+    alg.setProperty("CAp", "1,1,2");
+    alg.setProperty("CRho", "1,1");
+    alg.setProperty("CAlpha", "1");
     alg.setPropertyValue("OutputWorkspace", "IvsQ");
     alg.setPropertyValue("OutputWorkspaceBinned", "IvsQ_binned");
     alg.setPropertyValue("OutputWorkspaceWavelength", "IvsLam");
@@ -801,8 +801,8 @@ public:
     alg.setProperty("ProcessingInstructions", "2");
     alg.setProperty("MomentumTransferStep", 0.04);
     alg.setProperty("PolarizationAnalysis", "PNR");
-    alg.setProperty("Pp", "1,1,2");
-    alg.setProperty("Rho", "1,1");
+    alg.setProperty("CPp", "1,1,2");
+    alg.setProperty("CRho", "1,1");
     alg.setPropertyValue("OutputWorkspace", "IvsQ");
     alg.setPropertyValue("OutputWorkspaceBinned", "IvsQ_binned");
     alg.setPropertyValue("OutputWorkspaceWavelength", "IvsLam");
@@ -976,8 +976,8 @@ public:
     alg.setProperty("ProcessingInstructions", "2");
     alg.setProperty("MomentumTransferStep", 0.04);
     alg.setProperty("PolarizationAnalysis", "PNR");
-    alg.setProperty("Pp", "1");
-    alg.setProperty("Rho", "1");
+    alg.setProperty("CPp", "1");
+    alg.setProperty("CRho", "1");
     alg.setPropertyValue("OutputWorkspace", "IvsQ");
     alg.setPropertyValue("OutputWorkspaceBinned", "IvsQ_binned");
     alg.setPropertyValue("OutputWorkspaceWavelength", "IvsLam");
diff --git a/Framework/Algorithms/test/SampleLogsBehaviourTest.h b/Framework/Algorithms/test/SampleLogsBehaviourTest.h
new file mode 100644
index 0000000000000000000000000000000000000000..64f69432dc511697bebddca0b3ea0d3c528b6911
--- /dev/null
+++ b/Framework/Algorithms/test/SampleLogsBehaviourTest.h
@@ -0,0 +1,184 @@
+// Mantid Repository : https://github.com/mantidproject/mantid
+//
+// Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
+//     NScD Oak Ridge National Laboratory, European Spallation Source
+//     & Institut Laue - Langevin
+// SPDX - License - Identifier: GPL - 3.0 +
+#ifndef MANTID_ALGORITHMS_SAMPLELOGSBEHAVIOURTEST_H_
+#define MANTID_ALGORITHMS_SAMPLELOGSBEHAVIOURTEST_H_
+
+#include "MantidAPI/MatrixWorkspace.h"
+#include "MantidAlgorithms/RunCombinationHelpers/SampleLogsBehaviour.h"
+#include "MantidDataHandling/LoadParameterFile.h"
+#include "MantidTestHelpers/WorkspaceCreationHelper.h"
+#include <cxxtest/TestSuite.h>
+
+#include "MantidKernel/TimeSeriesProperty.h"
+
+using Mantid::Algorithms::SampleLogsBehaviour;
+using namespace Mantid::API;
+using namespace Mantid::DataHandling;
+using namespace Mantid::Kernel;
+using namespace WorkspaceCreationHelper;
+
+class SampleLogsBehaviourTest : 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 SampleLogsBehaviourTest *createSuite() {
+    return new SampleLogsBehaviourTest();
+  }
+  static void destroySuite(SampleLogsBehaviourTest *suite) { delete suite; }
+
+  // Please note that many tests are currently present in MergeRunsTest.
+
+  void testConstructorDefaults() {
+    Logger log("testLog");
+    auto ws = createTestingWorkspace();
+    SampleLogsBehaviour sbh = SampleLogsBehaviour(ws, log);
+    TS_ASSERT_THROWS_NOTHING(sbh.mergeSampleLogs(ws, ws))
+    const std::string A = ws->run().getLogData("A")->value();
+    const std::string B = ws->run().getLogData("B")->value();
+    const std::string C = ws->run().getLogData("C")->value();
+    // A, B, C original values
+    TS_ASSERT_EQUALS(A, "2.6499999999999999")
+    TS_ASSERT_EQUALS(B, "1.5600000000000001")
+    TS_ASSERT_EQUALS(C, "8.5500000000000007")
+  }
+
+  void testSomeAlgorithmIPFNames() {
+    Logger log("testLog");
+    auto ws = createTestingWorkspace();
+    SampleLogsBehaviour::ParameterName parameterNames;
+    parameterNames.SUM_MERGE = "logs_sum";
+    parameterNames.LIST_MERGE = "logs_list";
+    SampleLogsBehaviour sbh = SampleLogsBehaviour(ws, log, {}, parameterNames);
+    TS_ASSERT_THROWS_NOTHING(sbh.mergeSampleLogs(ws, ws))
+    const std::string A = ws->run().getLogData("A")->value();
+    const std::string B = ws->run().getLogData("B")->value();
+    const std::string C = ws->run().getLogData("C")->value();
+    // A listed and B summed according to IPF
+    TS_ASSERT_EQUALS(A, "2.6499999999999999, 2.6499999999999999")
+    TS_ASSERT_EQUALS(B, "3.1200000000000001")
+    TS_ASSERT_EQUALS(C, "8.5500000000000007")
+  }
+
+  void testSomeAlgorithmUserNames() {
+    // Using default values of the constructor
+    Logger log("testLog");
+    auto ws = createTestingWorkspace();
+    SampleLogsBehaviour::ParameterName parameterNames;
+    parameterNames.SUM_MERGE = "logs_sum";
+    SampleLogsBehaviour::SampleLogNames sampleLogNames;
+    sampleLogNames.sampleLogsSum = "A";
+    SampleLogsBehaviour sbh =
+        SampleLogsBehaviour(ws, log, sampleLogNames, parameterNames);
+    TS_ASSERT_THROWS_NOTHING(sbh.mergeSampleLogs(ws, ws))
+    const std::string A = ws->run().getLogData("A")->value();
+    const std::string B = ws->run().getLogData("B")->value();
+    const std::string C = ws->run().getLogData("C")->value();
+    // A summed according to user name and B summed according to IPF
+    TS_ASSERT_EQUALS(A, "5.2999999999999998")
+    TS_ASSERT_EQUALS(B, "3.1200000000000001")
+    TS_ASSERT_EQUALS(C, "8.5500000000000007")
+  }
+
+  void testOtherAlgorithmIPFNames() {
+    Logger log("testLog");
+    auto ws = createTestingWorkspace();
+    SampleLogsBehaviour::SampleLogNames sampleLogNames;
+    SampleLogsBehaviour::ParameterName parameterNames;
+    parameterNames.SUM_MERGE = "other_logs_sum";
+    SampleLogsBehaviour sbh =
+        SampleLogsBehaviour(ws, log, sampleLogNames, parameterNames);
+    sbh.mergeSampleLogs(ws, ws);
+    const std::string A = ws->run().getLogData("A")->value();
+    const std::string B = ws->run().getLogData("B")->value();
+    const std::string C = ws->run().getLogData("C")->value();
+    // A and C summed according to IPF
+    TS_ASSERT_EQUALS(A, "5.2999999999999998")
+    TS_ASSERT_EQUALS(B, "1.5600000000000001")
+    TS_ASSERT_EQUALS(C, "17.100000000000001")
+  }
+
+  void testOtherAlgorithmUserNames() {
+    Logger log("testLog");
+    auto ws = createTestingWorkspace();
+    SampleLogsBehaviour::SampleLogNames sampleLogNames;
+    sampleLogNames.sampleLogsSum = "B";
+    SampleLogsBehaviour::ParameterName parameterNames;
+    parameterNames.SUM_MERGE = "other_logs_sum";
+    SampleLogsBehaviour sbh =
+        SampleLogsBehaviour(ws, log, sampleLogNames, parameterNames);
+    sbh.mergeSampleLogs(ws, ws);
+    const std::string A = ws->run().getLogData("A")->value();
+    const std::string B = ws->run().getLogData("B")->value();
+    const std::string C = ws->run().getLogData("C")->value();
+    // B summed according to user name and A and C summed according to IPF
+    TS_ASSERT_EQUALS(A, "5.2999999999999998")
+    TS_ASSERT_EQUALS(B, "3.1200000000000001")
+    TS_ASSERT_EQUALS(C, "17.100000000000001")
+  }
+
+  MatrixWorkspace_sptr createTestingWorkspace() {
+    MatrixWorkspace_sptr ws = create2DWorkspaceWithFullInstrument(
+        3, 3, true, false, true, m_instrName);
+    // Add sample logs
+    TS_ASSERT_THROWS_NOTHING(
+        ws->mutableRun().addLogData(new PropertyWithValue<double>("A", 2.65)))
+    TS_ASSERT_THROWS_NOTHING(
+        ws->mutableRun().addLogData(new PropertyWithValue<double>("B", 1.56)))
+    TS_ASSERT_THROWS_NOTHING(
+        ws->mutableRun().addLogData(new PropertyWithValue<double>("C", 8.55)))
+    TimeSeriesProperty<double> *time_series_log =
+        new TimeSeriesProperty<double>("D");
+    TS_ASSERT_THROWS_NOTHING(
+        time_series_log->addValue("2018-11-30T16:17:01", 5.5))
+    TS_ASSERT_THROWS_NOTHING(
+        time_series_log->addValue("2018-11-30T16:17:02", 6.6))
+    TS_ASSERT_THROWS_NOTHING(
+        time_series_log->addValue("2018-11-30T16:17:03", 7.7))
+    TS_ASSERT_THROWS_NOTHING(ws->mutableRun().addProperty(time_series_log))
+    // Add units to the sample logs
+    TS_ASSERT_THROWS_NOTHING(ws->getLog("A")->setUnits("A_unit"))
+    TS_ASSERT_THROWS_NOTHING(ws->getLog("B")->setUnits("B_unit"))
+    TS_ASSERT_THROWS_NOTHING(ws->getLog("C")->setUnits("C_unit"))
+    TS_ASSERT_THROWS_NOTHING(ws->getLog("D")->setUnits("D_unit"))
+    // Load test parameter file
+    LoadParameterFile addIPF;
+    TS_ASSERT_THROWS_NOTHING(addIPF.initialize());
+    TS_ASSERT_THROWS_NOTHING(addIPF.setProperty("ParameterXML", m_parameterXML))
+    TS_ASSERT_THROWS_NOTHING(addIPF.setProperty("Workspace", ws))
+    TS_ASSERT_THROWS_NOTHING(addIPF.execute())
+    TS_ASSERT(addIPF.isExecuted())
+    return ws;
+  }
+
+private:
+  // Test instrument name
+  const std::string m_instrName = "INSTR";
+  // Define parameter XML string
+  std::string m_parameterXML =
+      "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>"
+      "<parameter-file instrument=\"INSTR\" valid-from=\"2018-11-07 "
+      "12:00:00\">"
+      "  <component-link name=\"INSTR\">"
+      "    <!-- Some algorithm.-->"
+      "    <parameter name=\"logs_sum\" type=\"string\">"
+      "	      <value val=\"B\" />"
+      "    </parameter>"
+      "    <parameter name=\"logs_list\" type=\"string\">"
+      "	      <value val=\"A\" />"
+      "    </parameter>"
+      "    <parameter name=\"logs_time_series\" type=\"string\">"
+      "	      <value val=\"D\" />"
+      "    </parameter>"
+      "    <!-- Some other algorithm. -->"
+      "    <parameter name=\"other_logs_sum\" type=\"string\">"
+      "       <value val=\"A, C\" />"
+      "    </parameter>"
+      "  </component-link>"
+      "</parameter-file>";
+};
+
+#endif /* MANTID_ALGORITHMS_SAMPLELOGSBEHAVIOURTEST_H_ */
diff --git a/Framework/Crystal/src/AnvredCorrection.cpp b/Framework/Crystal/src/AnvredCorrection.cpp
index ccbba79ad2e3ab9b3552a9ca7a8c7d02b402def7..67ecd2e2bef13c06eda8c1af33a4f11116bd3411 100644
--- a/Framework/Crystal/src/AnvredCorrection.cpp
+++ b/Framework/Crystal/src/AnvredCorrection.cpp
@@ -362,9 +362,7 @@ void AnvredCorrection::retrieveBaseProperties() {
       m_amu = sampleMaterial.absorbXSection(NeutronAtom::ReferenceLambda) * rho;
   } else // Save input in Sample with wrong atomic number and name
   {
-    NeutronAtom neutron(static_cast<uint16_t>(EMPTY_DBL()),
-                        static_cast<uint16_t>(0), 0.0, 0.0, m_smu, 0.0, m_smu,
-                        m_amu);
+    NeutronAtom neutron(0, 0, 0.0, 0.0, m_smu, 0.0, m_smu, m_amu);
     auto shape = boost::shared_ptr<IObject>(
         m_inputWS->sample().getShape().cloneWithMaterial(
             Material("SetInAnvredCorrection", neutron, 1.0)));
diff --git a/Framework/Crystal/src/LoadHKL.cpp b/Framework/Crystal/src/LoadHKL.cpp
index 04f9357f1f9ddea092bdd5e33a07902f4084cffd..9532b718a273aabc083dd94d288bdd1fe4ebe0d8 100644
--- a/Framework/Crystal/src/LoadHKL.cpp
+++ b/Framework/Crystal/src/LoadHKL.cpp
@@ -175,8 +175,7 @@ void LoadHKL::exec() {
                  << " calculated from tbar and transmission of 2 peaks\n";
   API::Run &mrun = ws->mutableRun();
   mrun.addProperty<double>("Radius", radius, true);
-  NeutronAtom neutron(static_cast<uint16_t>(EMPTY_DBL()),
-                      static_cast<uint16_t>(0), 0.0, 0.0, smu, 0.0, smu, amu);
+  NeutronAtom neutron(0, 0, 0.0, 0.0, smu, 0.0, smu, amu);
   auto shape =
       boost::shared_ptr<IObject>(ws->sample().getShape().cloneWithMaterial(
           Material("SetInLoadHKL", neutron, 1.0)));
diff --git a/Framework/Crystal/src/SaveHKL.cpp b/Framework/Crystal/src/SaveHKL.cpp
index 03b3815291a1e19f16b50c6cace4e8e2ac383481..f78244d7c784ea278d9cf686c16d8d47d77c8235 100644
--- a/Framework/Crystal/src/SaveHKL.cpp
+++ b/Framework/Crystal/src/SaveHKL.cpp
@@ -261,9 +261,7 @@ void SaveHKL::exec() {
                                    // with wrong atomic
                                    // number and name
   {
-    NeutronAtom neutron(static_cast<uint16_t>(EMPTY_DBL()),
-                        static_cast<uint16_t>(0), 0.0, 0.0, m_smu, 0.0, m_smu,
-                        m_amu);
+    NeutronAtom neutron(0, 0, 0.0, 0.0, m_smu, 0.0, m_smu, m_amu);
     auto shape = boost::shared_ptr<IObject>(
         peaksW->sample().getShape().cloneWithMaterial(
             Material("SetInSaveHKL", neutron, 1.0)));
diff --git a/Framework/Crystal/test/LoadHKLTest.h b/Framework/Crystal/test/LoadHKLTest.h
index 015313effd76bc46930d27a31d0e7aaf415e750b..08473af75d2832bff3f9653bb12bab995db58429 100644
--- a/Framework/Crystal/test/LoadHKLTest.h
+++ b/Framework/Crystal/test/LoadHKLTest.h
@@ -46,8 +46,7 @@ public:
     ws->setInstrument(inst);
     double smu = 0.357;
     double amu = 0.011;
-    NeutronAtom neutron(static_cast<uint16_t>(EMPTY_DBL()),
-                        static_cast<uint16_t>(0), 0.0, 0.0, smu, 0.0, smu, amu);
+    NeutronAtom neutron(0, 0, 0.0, 0.0, smu, 0.0, smu, amu);
     auto sampleShape = boost::make_shared<CSGObject>();
     sampleShape->setMaterial(Material("SetInAnvredCorrection", neutron, 1.0));
     ws->mutableSample().setShape(sampleShape);
diff --git a/Framework/Crystal/test/SaveHKLTest.h b/Framework/Crystal/test/SaveHKLTest.h
index 93fceb3f2107d48b54d0b55ce0f5192c96c90f52..3d389f1293819064449a0da726c7ef57ed70fc4f 100644
--- a/Framework/Crystal/test/SaveHKLTest.h
+++ b/Framework/Crystal/test/SaveHKLTest.h
@@ -44,8 +44,7 @@ public:
     ws->setInstrument(inst);
     double smu = 0.357;
     double amu = 0.011;
-    NeutronAtom neutron(static_cast<uint16_t>(EMPTY_DBL()),
-                        static_cast<uint16_t>(0), 0.0, 0.0, smu, 0.0, smu, amu);
+    NeutronAtom neutron(0, 0, 0.0, 0.0, smu, 0.0, smu, amu);
 
     auto sampleShape = boost::make_shared<CSGObject>();
     sampleShape->setMaterial(Material("SetInSaveHKLTest", neutron, 1.0));
diff --git a/Framework/Crystal/test/SortHKLTest.h b/Framework/Crystal/test/SortHKLTest.h
index c5bd0794c9ba87ac23e51bcf96b6f6c66aa28467..e4d71cca919db8e2898efd350f08f75f686e01c6 100644
--- a/Framework/Crystal/test/SortHKLTest.h
+++ b/Framework/Crystal/test/SortHKLTest.h
@@ -54,8 +54,7 @@ public:
 
     double smu = 0.357;
     double amu = 0.011;
-    NeutronAtom neutron(static_cast<uint16_t>(EMPTY_DBL()),
-                        static_cast<uint16_t>(0), 0.0, 0.0, smu, 0.0, smu, amu);
+    NeutronAtom neutron(0, 0, 0.0, 0.0, smu, 0.0, smu, amu);
     auto sampleShape = boost::make_shared<CSGObject>();
     sampleShape->setMaterial(Material("SetInSaveHKLTest", neutron, 1.0));
     ws->mutableSample().setShape(sampleShape);
diff --git a/Framework/CurveFitting/inc/MantidCurveFitting/Algorithms/QENSFitSequential.h b/Framework/CurveFitting/inc/MantidCurveFitting/Algorithms/QENSFitSequential.h
index 6feb6c897f029b309402514f69af60ac5e2c36f7..9b14cb0459158bfbf081e251696f4a22952d131a 100644
--- a/Framework/CurveFitting/inc/MantidCurveFitting/Algorithms/QENSFitSequential.h
+++ b/Framework/CurveFitting/inc/MantidCurveFitting/Algorithms/QENSFitSequential.h
@@ -45,6 +45,7 @@ private:
   API::ITableWorkspace_sptr performFit(const std::string &input,
                                        const std::string &output);
   void deleteTemporaryWorkspaces(const std::string &outputBaseName);
+  void addAdditionalLogs(API::WorkspaceGroup_sptr resultWorkspace);
   void addAdditionalLogs(API::Workspace_sptr result);
 
   virtual bool throwIfElasticQConversionFails() const;
@@ -75,10 +76,14 @@ private:
                             std::vector<std::string> const &spectra,
                             std::string const &outputBaseName,
                             std::string const &endOfSuffix);
-  void copyLogs(API::WorkspaceGroup_sptr resultWorkspace,
-                const std::vector<API::MatrixWorkspace_sptr> &workspaces);
+  void copyLogs(API::WorkspaceGroup_sptr resultWorkspaces,
+                std::vector<API::MatrixWorkspace_sptr> const &workspaces);
+  void copyLogs(API::Workspace_sptr resultWorkspace,
+                std::vector<API::MatrixWorkspace_sptr> const &workspaces);
   void copyLogs(API::MatrixWorkspace_sptr resultWorkspace,
                 API::WorkspaceGroup_sptr resultGroup);
+  void copyLogs(API::MatrixWorkspace_sptr resultWorkspace,
+                API::Workspace_sptr resultGroup);
   void extractMembers(API::WorkspaceGroup_sptr resultGroupWs,
                       const std::vector<API::MatrixWorkspace_sptr> &workspaces,
                       const std::string &outputWsName);
diff --git a/Framework/CurveFitting/src/Algorithms/PlotPeakByLogValue.cpp b/Framework/CurveFitting/src/Algorithms/PlotPeakByLogValue.cpp
index 7ae364b380dcbac91d31a2ab8455841b5767c400..a45856b9eb0834ed88c2a76e380dd048b0987b79 100644
--- a/Framework/CurveFitting/src/Algorithms/PlotPeakByLogValue.cpp
+++ b/Framework/CurveFitting/src/Algorithms/PlotPeakByLogValue.cpp
@@ -220,9 +220,9 @@ void PlotPeakByLogValue::exec() {
 
   setProperty("OutputWorkspace", result);
 
-  std::vector<std::string> covariance_workspaces;
-  std::vector<std::string> fit_workspaces;
-  std::vector<std::string> parameter_workspaces;
+  std::vector<MatrixWorkspace_sptr> fitWorkspaces;
+  std::vector<ITableWorkspace_sptr> parameterWorkspaces;
+  std::vector<ITableWorkspace_sptr> covarianceWorkspaces;
 
   double dProg = 1. / static_cast<double>(wsNames.size());
   double Prog = 0.;
@@ -250,9 +250,9 @@ void PlotPeakByLogValue::exec() {
     }
 
     if (createFitOutput) {
-      covariance_workspaces.reserve(covariance_workspaces.size() + jend);
-      fit_workspaces.reserve(fit_workspaces.size() + jend);
-      parameter_workspaces.reserve(parameter_workspaces.size() + jend);
+      covarianceWorkspaces.reserve(covarianceWorkspaces.size() + jend);
+      fitWorkspaces.reserve(fitWorkspaces.size() + jend);
+      parameterWorkspaces.reserve(parameterWorkspaces.size() + jend);
     }
 
     dProg /= abs(jend - j);
@@ -305,8 +305,7 @@ void PlotPeakByLogValue::exec() {
         bool ignoreInvalidData = getProperty("IgnoreInvalidData");
 
         // Fit the function
-        API::IAlgorithm_sptr fit =
-            AlgorithmManager::Instance().createUnmanaged("Fit");
+        auto fit = this->createChildAlgorithm("Fit");
         fit->initialize();
         fit->setPropertyValue("EvaluationType",
                               getPropertyValue("EvaluationType"));
@@ -341,15 +340,18 @@ void PlotPeakByLogValue::exec() {
         chi2 = fit->getProperty("OutputChi2overDoF");
 
         if (createFitOutput) {
-          covariance_workspaces.push_back(wsBaseName +
-                                          "_NormalisedCovarianceMatrix");
-          parameter_workspaces.push_back(wsBaseName + "_Parameters");
-          fit_workspaces.push_back(wsBaseName + "_Workspace");
+          MatrixWorkspace_sptr outputFitWorkspace =
+              fit->getProperty("OutputWorkspace");
+          ITableWorkspace_sptr outputParamWorkspace =
+              fit->getProperty("OutputParameters");
+          ITableWorkspace_sptr outputCovarianceWorkspace =
+              fit->getProperty("OutputNormalisedCovarianceMatrix");
+          fitWorkspaces.emplace_back(outputFitWorkspace);
+          parameterWorkspaces.emplace_back(outputParamWorkspace);
+          covarianceWorkspaces.emplace_back(outputCovarianceWorkspace);
         }
-
         g_log.debug() << "Fit result " << fit->getPropertyValue("OutputStatus")
                       << ' ' << chi2 << '\n';
-
       } catch (...) {
         g_log.error("Error in Fit ChildAlgorithm");
         throw;
@@ -378,37 +380,33 @@ void PlotPeakByLogValue::exec() {
           ifun->setParameter(i, initialParams[i]);
         }
       }
-
     } // for(;j < jend;++j)
   }
 
   if (createFitOutput) {
     // collect output of fit for each spectrum into workspace groups
-    API::IAlgorithm_sptr groupAlg =
-        AlgorithmManager::Instance().createUnmanaged("GroupWorkspaces");
-    groupAlg->initialize();
-    groupAlg->setProperty("InputWorkspaces", covariance_workspaces);
-    groupAlg->setProperty("OutputWorkspace",
-                          m_baseName + "_NormalisedCovarianceMatrices");
-    groupAlg->execute();
-
-    groupAlg = AlgorithmManager::Instance().createUnmanaged("GroupWorkspaces");
-    groupAlg->initialize();
-    groupAlg->setProperty("InputWorkspaces", parameter_workspaces);
-    groupAlg->setProperty("OutputWorkspace", m_baseName + "_Parameters");
-    groupAlg->execute();
-
-    groupAlg = AlgorithmManager::Instance().createUnmanaged("GroupWorkspaces");
-    groupAlg->initialize();
-    groupAlg->setProperty("InputWorkspaces", fit_workspaces);
-    groupAlg->setProperty("OutputWorkspace", m_baseName + "_Workspaces");
-    groupAlg->execute();
+    WorkspaceGroup_sptr covarianceGroup = boost::make_shared<WorkspaceGroup>();
+    for (auto const &workspace : covarianceWorkspaces)
+      covarianceGroup->addWorkspace(workspace);
+    AnalysisDataService::Instance().addOrReplace(
+        m_baseName + "_NormalisedCovarianceMatrices", covarianceGroup);
+
+    WorkspaceGroup_sptr parameterGroup = boost::make_shared<WorkspaceGroup>();
+    for (auto const &workspace : parameterWorkspaces)
+      parameterGroup->addWorkspace(workspace);
+    AnalysisDataService::Instance().addOrReplace(m_baseName + "_Parameters",
+                                                 parameterGroup);
+
+    WorkspaceGroup_sptr fitGroup = boost::make_shared<WorkspaceGroup>();
+    for (auto const &workspace : fitWorkspaces)
+      fitGroup->addWorkspace(workspace);
+    AnalysisDataService::Instance().addOrReplace(m_baseName + "_Workspaces",
+                                                 fitGroup);
   }
 
   for (auto &minimizerWorkspace : m_minimizerWorkspaces) {
     const std::string paramName = minimizerWorkspace.first;
-    API::IAlgorithm_sptr groupAlg =
-        AlgorithmManager::Instance().createUnmanaged("GroupWorkspaces");
+    auto groupAlg = this->createChildAlgorithm("GroupWorkspaces");
     groupAlg->initialize();
     groupAlg->setProperty("InputWorkspaces", minimizerWorkspace.second);
     groupAlg->setProperty("OutputWorkspace", m_baseName + "_" + paramName);
diff --git a/Framework/CurveFitting/src/Algorithms/QENSFitSequential.cpp b/Framework/CurveFitting/src/Algorithms/QENSFitSequential.cpp
index 73bc15b98fe2be28bf1163bf05b07d5c0f5aa486..90165ffd3af82be1294b61294b1f32e7c5caf0af 100644
--- a/Framework/CurveFitting/src/Algorithms/QENSFitSequential.cpp
+++ b/Framework/CurveFitting/src/Algorithms/QENSFitSequential.cpp
@@ -563,6 +563,11 @@ QENSFitSequential::getAdditionalLogNumbers() const {
   return logs;
 }
 
+void QENSFitSequential::addAdditionalLogs(WorkspaceGroup_sptr resultWorkspace) {
+  for (const auto &workspace : *resultWorkspace)
+    addAdditionalLogs(workspace);
+}
+
 void QENSFitSequential::addAdditionalLogs(Workspace_sptr resultWorkspace) {
   auto logAdder = createChildAlgorithm("AddSampleLog", -1.0, -1.0, false);
   logAdder->setProperty("Workspace", resultWorkspace);
@@ -759,12 +764,19 @@ void QENSFitSequential::extractMembers(
 }
 
 void QENSFitSequential::copyLogs(
-    WorkspaceGroup_sptr resultWorkspace,
-    const std::vector<MatrixWorkspace_sptr> &workspaces) {
+    WorkspaceGroup_sptr resultWorkspaces,
+    std::vector<MatrixWorkspace_sptr> const &workspaces) {
+  for (auto const &resultWorkspace : *resultWorkspaces)
+    copyLogs(resultWorkspace, workspaces);
+}
+
+void QENSFitSequential::copyLogs(
+    Workspace_sptr resultWorkspace,
+    std::vector<MatrixWorkspace_sptr> const &workspaces) {
   auto logCopier = createChildAlgorithm("CopyLogs", -1.0, -1.0, false);
   logCopier->setProperty("OutputWorkspace", resultWorkspace->getName());
 
-  for (const auto &workspace : workspaces) {
+  for (auto const &workspace : workspaces) {
     logCopier->setProperty("InputWorkspace", workspace);
     logCopier->executeAsChildAlg();
   }
@@ -772,6 +784,12 @@ void QENSFitSequential::copyLogs(
 
 void QENSFitSequential::copyLogs(MatrixWorkspace_sptr resultWorkspace,
                                  WorkspaceGroup_sptr resultGroup) {
+  for (auto const &workspace : *resultGroup)
+    copyLogs(resultWorkspace, workspace);
+}
+
+void QENSFitSequential::copyLogs(MatrixWorkspace_sptr resultWorkspace,
+                                 Workspace_sptr resultGroup) {
   auto logCopier = createChildAlgorithm("CopyLogs", -1.0, -1.0, false);
   logCopier->setProperty("InputWorkspace", resultWorkspace);
   logCopier->setProperty("OutputWorkspace", resultGroup->getName());
diff --git a/Framework/CurveFitting/src/CostFunctions/CostFuncFitting.cpp b/Framework/CurveFitting/src/CostFunctions/CostFuncFitting.cpp
index ca72f15a0de5b7c72cb7cc242355230f5a5e0c3b..0ffb0570e1bd49bef8688843226a9ff59e6a0f84 100644
--- a/Framework/CurveFitting/src/CostFunctions/CostFuncFitting.cpp
+++ b/Framework/CurveFitting/src/CostFunctions/CostFuncFitting.cpp
@@ -178,6 +178,7 @@ void CostFuncFitting::calFittingErrors(const GSLMatrix &covar, double chi2) {
   size_t np = m_function->nParams();
   auto covarMatrix = boost::shared_ptr<Kernel::Matrix<double>>(
       new Kernel::Matrix<double>(np, np));
+  m_function->setCovarianceMatrix(covarMatrix);
   size_t ia = 0;
   for (size_t i = 0; i < np; ++i) {
     if (!m_function->isActive(i)) {
diff --git a/Framework/CurveFitting/test/Algorithms/PlotPeakByLogValueTest.h b/Framework/CurveFitting/test/Algorithms/PlotPeakByLogValueTest.h
index 87afe3c32e1d582f8c6708b44aac9880bd6ff9d6..2cf1a3d7415a0b03d087244c67dcba4aa19d3602 100644
--- a/Framework/CurveFitting/test/Algorithms/PlotPeakByLogValueTest.h
+++ b/Framework/CurveFitting/test/Algorithms/PlotPeakByLogValueTest.h
@@ -539,34 +539,20 @@ public:
     TS_ASSERT(fits);
 
     if (fits->size() > 0) {
-      // Get the Fit algorithm history
       auto fit = fits->getItem(0);
-      const auto &wsHistory = fit->getHistory();
-      const auto &child = wsHistory.getAlgorithmHistory(0);
-      TS_ASSERT_EQUALS(child->name(), "Fit");
-      const auto &properties = child->getProperties();
-
-      // Check max iterations property
-      PropertyNameIs maxIterationsCheck("MaxIterations");
-      auto prop = std::find_if(properties.begin(), properties.end(),
-                               maxIterationsCheck);
-      TS_ASSERT_EQUALS((*prop)->value(), "50");
-
-      // Check minimizer property
-      PropertyNameIs minimizerCheck("Minimizer");
-      prop = std::find_if(properties.begin(), properties.end(), minimizerCheck);
-      TS_ASSERT_EQUALS((*prop)->value(),
-                       "Levenberg-Marquardt,AbsError=0.01,RelError=1");
+      TS_ASSERT_EQUALS(fit->history().size(), 0);
+      TS_ASSERT_EQUALS(fit->getName(), "PlotPeakResult_Workspaces_1");
     }
 
     AnalysisDataService::Instance().clear();
   }
 
-  void test_histogram_fit() {
+  void test_parameters_are_correct_for_a_histogram_fit() {
     createHistogramWorkspace("InputWS", 10, -10.0, 10.0);
 
     PlotPeakByLogValue alg;
     alg.initialize();
+    alg.setAlwaysStoreInADS(false);
     alg.setProperty("EvaluationType", "Histogram");
     alg.setPropertyValue("Input", "InputWS,v1:3");
     alg.setPropertyValue("OutputWorkspace", "out");
@@ -574,23 +560,10 @@ public:
     alg.setPropertyValue("Function", "name=FlatBackground,A0=2");
     alg.execute();
 
-    {
-      auto params = AnalysisDataService::Instance().retrieveWS<ITableWorkspace>(
-          "InputWS_0_Parameters");
-      TS_ASSERT_DELTA(params->Double(0, 1), 1.0, 1e-15);
-    }
-
-    {
-      auto params = AnalysisDataService::Instance().retrieveWS<ITableWorkspace>(
-          "InputWS_1_Parameters");
-      TS_ASSERT_DELTA(params->Double(0, 1), 1.1, 1e-15);
-    }
-
-    {
-      auto params = AnalysisDataService::Instance().retrieveWS<ITableWorkspace>(
-          "InputWS_2_Parameters");
-      TS_ASSERT_DELTA(params->Double(0, 1), 0.6, 1e-15);
-    }
+    ITableWorkspace_sptr params = alg.getProperty("OutputWorkspace");
+    TS_ASSERT_DELTA(params->Double(0, 1), 1.0, 1e-15);
+    TS_ASSERT_DELTA(params->Double(1, 1), 1.1, 1e-15);
+    TS_ASSERT_DELTA(params->Double(2, 1), 0.6, 1e-15);
 
     AnalysisDataService::Instance().clear();
   }
diff --git a/Framework/DataHandling/inc/MantidDataHandling/LoadSpiceXML2DDet.h b/Framework/DataHandling/inc/MantidDataHandling/LoadSpiceXML2DDet.h
index fccbdb4dc41d30ee8fbde169bc8bd7f62811e7d0..448e638278234d33126bd36717113f879efa52f6 100644
--- a/Framework/DataHandling/inc/MantidDataHandling/LoadSpiceXML2DDet.h
+++ b/Framework/DataHandling/inc/MantidDataHandling/LoadSpiceXML2DDet.h
@@ -71,25 +71,29 @@ private:
   /// Process inputs
   void processInputs();
 
+  /// create workspace (good to load instrument) from vector of counts
+  API::MatrixWorkspace_sptr
+  createMatrixWorkspace(const std::vector<unsigned int> &vec_counts);
+  /// parse binary integer file
+  std::vector<unsigned int> binaryParseIntegers(std::string &binary_file_name);
+
   /// Parse SPICE XML file
-  std::vector<SpiceXMLNode> parseSpiceXML(const std::string &xmlfilename);
+  std::vector<SpiceXMLNode> xmlParseSpice(const std::string &xmlfilename);
 
   /// Create output MatrixWorkspace
-  API::MatrixWorkspace_sptr
-  createMatrixWorkspace(const std::vector<SpiceXMLNode> &vecxmlnode,
-                        const size_t &numpixelx, const size_t &numpixely,
-                        const std::string &detnodename,
-                        const bool &loadinstrument);
+  API::MatrixWorkspace_sptr xmlCreateMatrixWorkspaceKnownGeometry(
+      const std::vector<SpiceXMLNode> &vecxmlnode, const size_t &numpixelx,
+      const size_t &numpixely, const std::string &detnodename,
+      const bool &loadinstrument);
 
   /// Create output MatrixWorkspace
-  API::MatrixWorkspace_sptr
-  createMatrixWorkspaceVersion2(const std::vector<SpiceXMLNode> &vecxmlnode,
-                                const std::string &detnodename,
-                                const bool &loadinstrument);
+  API::MatrixWorkspace_sptr xmlCreateMatrixWorkspaceUnknowGeometry(
+      const std::vector<SpiceXMLNode> &vecxmlnode,
+      const std::string &detnodename, const bool &loadinstrument);
 
-  API::MatrixWorkspace_sptr parseDetectorNode(const std::string &detvaluestr,
-                                              bool loadinstrument,
-                                              double &max_counts);
+  API::MatrixWorkspace_sptr xmlParseDetectorNode(const std::string &detvaluestr,
+                                                 bool loadinstrument,
+                                                 double &max_counts);
 
   /// Set up sample logs from table workspace loaded where SPICE data file is
   /// loaded
diff --git a/Framework/DataHandling/inc/MantidDataHandling/LoadSwans.h b/Framework/DataHandling/inc/MantidDataHandling/LoadSwans.h
index aa016c8d3aa67177a0df5810810c8bdd750e6786..7fe597619f5fac393d69ab7d17f1d4dd4f829df7 100644
--- a/Framework/DataHandling/inc/MantidDataHandling/LoadSwans.h
+++ b/Framework/DataHandling/inc/MantidDataHandling/LoadSwans.h
@@ -43,8 +43,7 @@ private:
   unsigned int getDetectorSize();
 
   // Member variables
-  DataObjects::EventWorkspace_sptr m_ws =
-      boost::make_shared<Mantid::DataObjects::EventWorkspace>();
+  DataObjects::EventWorkspace_sptr m_ws;
   unsigned int m_detector_size = 0;
 
   // Constants:
diff --git a/Framework/DataHandling/src/LoadSpiceXML2DDet.cpp b/Framework/DataHandling/src/LoadSpiceXML2DDet.cpp
index 8fa580cc843f1cea8494a084727410a3ef1219b7..b6b3f76d7787f630ce5c3a1b6d5235d06230516c 100644
--- a/Framework/DataHandling/src/LoadSpiceXML2DDet.cpp
+++ b/Framework/DataHandling/src/LoadSpiceXML2DDet.cpp
@@ -30,6 +30,9 @@
 
 #include <algorithm>
 #include <fstream>
+#include <iostream>
+
+using namespace std;
 
 namespace Mantid {
 namespace DataHandling {
@@ -165,9 +168,12 @@ const std::string LoadSpiceXML2DDet::summary() const {
  * @brief LoadSpiceXML2DDet::init
  */
 void LoadSpiceXML2DDet::init() {
+  std::vector<std::string> exts;
+  exts.push_back(".xml");
+  exts.push_back(".bin");
   declareProperty(
       make_unique<FileProperty>("Filename", "", FileProperty::FileAction::Load,
-                                ".xml"),
+                                exts),
       "XML file name for one scan including 2D detectors counts from SPICE");
 
   declareProperty(
@@ -185,7 +191,10 @@ void LoadSpiceXML2DDet::init() {
   declareProperty(
       make_unique<ArrayProperty<size_t>>("DetectorGeometry"),
       "A size-2 unsigned integer array [X, Y] for detector geometry. "
-      "Such that the detector contains X x Y pixels.");
+      "Such that the detector contains X x Y pixels."
+      "If the input data is a binary file, input for DetectorGeometry will be "
+      "overridden "
+      "by detector geometry specified in the binary file");
 
   declareProperty(
       "LoadInstrument", true,
@@ -322,6 +331,7 @@ bool LoadSpiceXML2DDet::setupSampleLogs(API::MatrixWorkspace_sptr outws) {
   return return_true;
 }
 
+//----------------------------------------------------------------------------------------------
 /** Main execution
  * @brief LoadSpiceXML2DDet::exec
  */
@@ -329,17 +339,26 @@ void LoadSpiceXML2DDet::exec() {
   // Load input
   processInputs();
 
-  // Parse detector XML file
-  std::vector<SpiceXMLNode> vec_xmlnode = parseSpiceXML(m_detXMLFileName);
-
-  // Create output workspace
+  // check the file end
   MatrixWorkspace_sptr outws;
-  if (m_numPixelX * m_numPixelY > 0)
-    outws = createMatrixWorkspace(vec_xmlnode, m_numPixelX, m_numPixelY,
-                                  m_detXMLNodeName, m_loadInstrument);
-  else
-    outws = createMatrixWorkspaceVersion2(vec_xmlnode, m_detXMLNodeName,
-                                          m_loadInstrument);
+  if (m_detXMLFileName.substr(m_detXMLFileName.find_last_of('.') + 1) ==
+      "bin") {
+    std::vector<unsigned int> vec_counts =
+        binaryParseIntegers(m_detXMLFileName);
+    outws = createMatrixWorkspace(vec_counts);
+  } else {
+    // Parse detector XML file
+    std::vector<SpiceXMLNode> vec_xmlnode = xmlParseSpice(m_detXMLFileName);
+
+    // Create output workspace
+    if (m_numPixelX * m_numPixelY > 0)
+      outws = xmlCreateMatrixWorkspaceKnownGeometry(
+          vec_xmlnode, m_numPixelX, m_numPixelY, m_detXMLNodeName,
+          m_loadInstrument);
+    else
+      outws = xmlCreateMatrixWorkspaceUnknowGeometry(
+          vec_xmlnode, m_detXMLNodeName, m_loadInstrument);
+  }
 
   // Set up log for loading instrument
   bool can_set_instrument = setupSampleLogs(outws);
@@ -368,7 +387,7 @@ void LoadSpiceXML2DDet::exec() {
  * @return vector of SpiceXMLNode containing information in XML file
  */
 std::vector<SpiceXMLNode>
-LoadSpiceXML2DDet::parseSpiceXML(const std::string &xmlfilename) {
+LoadSpiceXML2DDet::xmlParseSpice(const std::string &xmlfilename) {
   // Declare output
   std::vector<SpiceXMLNode> vecspicenode;
 
@@ -460,6 +479,95 @@ LoadSpiceXML2DDet::parseSpiceXML(const std::string &xmlfilename) {
   return vecspicenode;
 }
 
+//----------------------------------------------------------------------------------------------
+/// parse binary integer file
+std::vector<unsigned int>
+LoadSpiceXML2DDet::binaryParseIntegers(std::string &binary_file_name) {
+  // check binary file size
+  ifstream infile(binary_file_name.c_str(), ios::binary);
+  streampos begin, end;
+  begin = infile.tellg();
+  infile.seekg(0, ios::end);
+  end = infile.tellg();
+  g_log.information() << "File size is: " << (end - begin) << " bytes.\n";
+
+  size_t num_unsigned_int =
+      static_cast<size_t>(end - begin) / sizeof(unsigned int);
+  if (num_unsigned_int <= 2)
+    throw std::runtime_error(
+        "Input binary file size is too small (<= 2 unsigned int)");
+
+  size_t num_dets = num_unsigned_int - 2;
+  g_log.information() << "File contains " << num_unsigned_int
+                      << " unsigned integers and thus " << num_dets
+                      << " detectors.\n";
+
+  // define output vector
+  std::vector<unsigned int> vec_counts(num_dets);
+  infile.seekg(0, ios::beg);
+
+  // read each integer... time consuming
+  //  int max_count = 0;
+  // char buffer[sizeof(int)];
+  unsigned int buffer;
+  unsigned int total_counts(0);
+
+  // read detector size (row and column)
+  infile.read((char *)&buffer, sizeof(buffer));
+  size_t num_rows = static_cast<size_t>(buffer);
+  infile.read((char *)&buffer, sizeof(buffer));
+  size_t num_cols = static_cast<size_t>(buffer);
+  if (num_rows * num_cols != num_dets) {
+    g_log.error() << "Input binary file " << binary_file_name
+                  << " has inconsistent specification "
+                  << "on detector size. "
+                  << "First 2 unsigned integers are " << num_rows << ", "
+                  << num_cols
+                  << ", while the detector number specified in the file is "
+                  << num_dets << "\n";
+    throw std::runtime_error(
+        "Input binary file has inconsistent specification on detector size.");
+  }
+
+  for (size_t i = 0; i < num_dets; ++i) {
+    // infile.read(buffer, sizeof(int));
+    infile.read((char *)&buffer, sizeof(buffer));
+    vec_counts[i] = buffer;
+    total_counts += buffer;
+  }
+
+  g_log.information() << "For detector " << num_rows << " x " << num_cols
+                      << ", total counts = " << total_counts << "\n";
+
+  return vec_counts;
+}
+
+//----------------------------------------------------------------------------------------------
+MatrixWorkspace_sptr LoadSpiceXML2DDet::createMatrixWorkspace(
+    const std::vector<unsigned int> &vec_counts) {
+  // Create matrix workspace
+  size_t numspec = vec_counts.size();
+  MatrixWorkspace_sptr outws = boost::dynamic_pointer_cast<MatrixWorkspace>(
+      WorkspaceFactory::Instance().create("Workspace2D", numspec, 2, 1));
+
+  g_log.information("Workspace created");
+
+  // set up value
+  for (size_t i = 0; i < numspec; ++i) {
+    outws->mutableX(i)[0] = 0.;
+    outws->mutableX(i)[1] = 1;
+    double counts = static_cast<double>(vec_counts[i]);
+    outws->mutableY(i)[0] = counts;
+    if (counts > 0.5)
+      outws->mutableE(i)[0] = sqrt(counts);
+    else
+      outws->mutableE(i)[0] = 1.0;
+  }
+
+  return outws;
+}
+
+//-----
 /** Create MatrixWorkspace from Spice XML file
  * @brief LoadSpiceXML2DDet::createMatrixWorkspace
  * @param vecxmlnode :: vector of SpiceXMLNode obtained from XML file
@@ -469,7 +577,7 @@ LoadSpiceXML2DDet::parseSpiceXML(const std::string &xmlfilename) {
  * @param loadinstrument :: flag to load instrument to output workspace or not.
  * @return
  */
-MatrixWorkspace_sptr LoadSpiceXML2DDet::createMatrixWorkspace(
+MatrixWorkspace_sptr LoadSpiceXML2DDet::xmlCreateMatrixWorkspaceKnownGeometry(
     const std::vector<SpiceXMLNode> &vecxmlnode, const size_t &numpixelx,
     const size_t &numpixely, const std::string &detnodename,
     const bool &loadinstrument) {
@@ -623,7 +731,7 @@ MatrixWorkspace_sptr LoadSpiceXML2DDet::createMatrixWorkspace(
 /** create the output matrix workspace without knowledge of detector geometry
  *
  */
-MatrixWorkspace_sptr LoadSpiceXML2DDet::createMatrixWorkspaceVersion2(
+MatrixWorkspace_sptr LoadSpiceXML2DDet::xmlCreateMatrixWorkspaceUnknowGeometry(
     const std::vector<SpiceXMLNode> &vecxmlnode, const std::string &detnodename,
     const bool &loadinstrument) {
 
@@ -647,7 +755,8 @@ MatrixWorkspace_sptr LoadSpiceXML2DDet::createMatrixWorkspaceVersion2(
       // Get node value string (256x256 as a whole)
       const std::string detvaluestr = xmlnode.getValue();
 
-      outws = this->parseDetectorNode(detvaluestr, loadinstrument, max_counts);
+      outws =
+          this->xmlParseDetectorNode(detvaluestr, loadinstrument, max_counts);
 
       // Set flag
       parsedDet = true;
@@ -705,9 +814,8 @@ MatrixWorkspace_sptr LoadSpiceXML2DDet::createMatrixWorkspaceVersion2(
 
 /**
  */
-API::MatrixWorkspace_sptr
-LoadSpiceXML2DDet::parseDetectorNode(const std::string &detvaluestr,
-                                     bool loadinstrument, double &max_counts) {
+API::MatrixWorkspace_sptr LoadSpiceXML2DDet::xmlParseDetectorNode(
+    const std::string &detvaluestr, bool loadinstrument, double &max_counts) {
   // Split to lines
   std::vector<std::string> vecLines;
   boost::split(vecLines, detvaluestr, boost::algorithm::is_any_of("\n"));
diff --git a/Framework/DataHandling/src/LoadSwans.cpp b/Framework/DataHandling/src/LoadSwans.cpp
index d7b9962e39102f4f75bcbf2f95bb9308f72e852b..4f26b6069ada58b6174238ff02c77be2176336f3 100644
--- a/Framework/DataHandling/src/LoadSwans.cpp
+++ b/Framework/DataHandling/src/LoadSwans.cpp
@@ -14,7 +14,6 @@
 #include <algorithm>
 #include <boost/tokenizer.hpp>
 #include <fstream>
-#include <iostream>
 #include <map>
 
 namespace Mantid {
@@ -88,7 +87,7 @@ void LoadSwans::init() {
 /** Execute the algorithm.
  */
 void LoadSwans::exec() {
-
+  m_ws = boost::make_shared<Mantid::DataObjects::EventWorkspace>();
   // Load instrument here to get the necessary Parameters from the XML file
   loadInstrument();
   m_detector_size = getDetectorSize();
diff --git a/Framework/DataHandling/test/LoadSpiceXML2DDetTest.h b/Framework/DataHandling/test/LoadSpiceXML2DDetTest.h
index ef0e124fe2201638e21ba535c9777303703ee9fe..f197cd17a209261fb979651de730b60190cedb7d 100644
--- a/Framework/DataHandling/test/LoadSpiceXML2DDetTest.h
+++ b/Framework/DataHandling/test/LoadSpiceXML2DDetTest.h
@@ -684,6 +684,43 @@ public:
     AnalysisDataService::Instance().remove("Exp0335_S0038F");
   }
 
+  //----
+  void test_loadBinaryFile() {
+    // HB3A_exp685_scan0248_0004.bin
+    // initialize the algorithm
+    LoadSpiceXML2DDet loader;
+    loader.initialize();
+
+    // set up properties
+    const std::string filename("LaB6_10kev_35deg.bin");
+    TS_ASSERT_THROWS_NOTHING(loader.setProperty("Filename", filename));
+    TS_ASSERT_THROWS_NOTHING(
+        loader.setProperty("OutputWorkspace", "Exp0335_S0038F"));
+    std::vector<size_t> geometryvec;
+    geometryvec.push_back(0);
+    geometryvec.push_back(0);
+    loader.setProperty("LoadInstrument", false);
+
+    loader.execute();
+    TS_ASSERT(loader.isExecuted());
+
+    // Get data
+    MatrixWorkspace_sptr outws = boost::dynamic_pointer_cast<MatrixWorkspace>(
+        AnalysisDataService::Instance().retrieve("Exp0335_S0038F"));
+    TS_ASSERT(outws);
+    TS_ASSERT_EQUALS(outws->getNumberHistograms(), 1024 * 1024);
+
+    // Value
+    // test signal value on various pixels
+    // pixel at (256, 1): column 1
+    TS_ASSERT_DELTA(outws->readY(0)[0], 60000., 0.0001);
+    TS_ASSERT_DELTA(outws->readY(1)[0], 50000., 0.0001);
+    TS_ASSERT_DELTA(outws->readY(2)[0], 40000., 0.0001);
+
+    // Clean
+    AnalysisDataService::Instance().remove("Exp0335_S0038F");
+  }
+
   //----------------------------------------------------------------------------------------------
   /** Create SPICE scan table workspace
    * @brief createSpiceScanTable
diff --git a/Framework/Kernel/CMakeLists.txt b/Framework/Kernel/CMakeLists.txt
index 20123a0f94869668502d9a26e17c36153104e065..eef665587c030544d673251947cd966ffc7b1d77 100644
--- a/Framework/Kernel/CMakeLists.txt
+++ b/Framework/Kernel/CMakeLists.txt
@@ -25,7 +25,6 @@ set ( SRC_FILES
 	src/DirectoryValidator.cpp
 	src/DiskBuffer.cpp
 	src/DllOpen.cpp
-	src/EmptyValues.cpp
 	src/EnabledWhenProperty.cpp
 	src/EnvironmentHistory.cpp
 	src/EqualBinsChecker.cpp
@@ -589,37 +588,6 @@ set ( COLORMAPS_FOLDER ${MANTID_ROOT}/installers/colormaps/ )
 set ( MANTIDPUBLISHER "http://upload.mantidproject.org/scriptrepository?debug=1" )
 set ( HTML_ROOT ${DOCS_BUILDDIR}/html )
 
-# Construct script paths.
-set ( MANTID_SCRIPTS ${MANTID_ROOT}/scripts )
-# First the required scripts variable...
-# Omitting the following (as of now) empty directories as CMake doesn't copy them on install and you then end up with a warning message....
-set ( REQUIREDSCRIPT_SUBDIRS Engineering Inelastic Reflectometry SANS)
-
-# If other external interfaces are added then we need a better approach here..
-set ( REQUIREDSCRIPT_DIRS ${MANTID_ROOT}/scripts;${CMAKE_BINARY_DIR}/scripts/ExternalInterfaces/mslice/src/mslice )
-foreach ( SUBDIR ${REQUIREDSCRIPT_SUBDIRS} )
-  set ( REQUIREDSCRIPT_DIRS "${REQUIREDSCRIPT_DIRS};${MANTID_SCRIPTS}/${SUBDIR}" )
-endforeach()
-
-# Second the standard scripts variable. The variable in the file is NOT recursive so add the top level directories from here
-set ( EXCLUDED_DIRS test )
-set ( PYTHONSCRIPT_DIRS "" )
-file ( GLOB SCRIPT_SUBDIRS RELATIVE ${MANTID_SCRIPTS} ${MANTID_SCRIPTS}/* )
-foreach ( SUBDIR ${SCRIPT_SUBDIRS} )
-  set ( DIR ${MANTID_SCRIPTS}/${SUBDIR} )
-  if ( IS_DIRECTORY ${DIR} AND NOT EXISTS ${DIR}/__init__.py )
-    list ( FIND REQUIREDSCRIPT_SUBDIRS ${SUBDIR} HAVE_DIR )
-    list ( FIND EXCLUDED_DIRS ${SUBDIR} EXCLUDE_DIR )
-    if ( HAVE_DIR EQUAL -1 AND EXCLUDE_DIR EQUAL -1 ) # If it is not in REQUIRED and not EXCLUDED
-      if ( PYTHONSCRIPT_DIRS )
-        set ( PYTHONSCRIPT_DIRS "${PYTHONSCRIPT_DIRS};${DIR}" )
-      else()
-        set ( PYTHONSCRIPT_DIRS "${DIR}" ) # Avoid first ;
-      endif()
-    endif()
-  endif()
-endforeach()
-
 # For an mpi-enabled build, do not log to file, format console output (which winds up in a file) differently
 # These settings carry down to the installation configuration below
 if ( MPI_BUILD )
@@ -694,25 +662,6 @@ set ( DATADIRS "" )
 set ( MANTIDPUBLISHER "http://upload.mantidproject.org/scriptrepository" )
 set ( HTML_ROOT ../share/doc/html )
 
-# script paths
-set ( MANTID_SCRIPTS ${MANTID_ROOT}/scripts )
-set ( REQUIREDSCRIPT_DIRS ${MANTID_SCRIPTS};${MANTID_SCRIPTS}/ExternalInterfaces )
-foreach ( SUBDIR ${REQUIREDSCRIPT_SUBDIRS} )
-  set ( REQUIREDSCRIPT_DIRS "${REQUIREDSCRIPT_DIRS};${MANTID_SCRIPTS}/${SUBDIR}" )
-endforeach()
-
-# PYTHONSCRIPT_DIRS
-set ( WITH_SEMICOLONS "" )
-foreach ( DIR ${PYTHONSCRIPT_DIRS} )
-  string ( REGEX REPLACE "${MANTID_ROOT_BUILD}" "${MANTID_ROOT}" DIR ${DIR} )
-  if ( WITH_SEMICOLONS )
-    set ( WITH_SEMICOLONS "${WITH_SEMICOLONS};${DIR}" )
-  else()
-    set ( WITH_SEMICOLONS "${DIR}" ) # Avoid first ;
-  endif()
-endforeach()
-set ( PYTHONSCRIPT_DIRS "${WITH_SEMICOLONS}" )
-
 # For a framework-only (e.g. MPI) build some of these are not relevant and should
 # be left empty to avoid warnings on starting Mantid
 if ( ${CMAKE_PROJECT_NAME} MATCHES "MantidFramework" )
diff --git a/Framework/Kernel/inc/MantidKernel/EmptyValues.h b/Framework/Kernel/inc/MantidKernel/EmptyValues.h
index 4e7f0011c878ee20952bca06773ed7f68d1f7593..1fa64c292bd4e0f892f3c17f30d041bbaaa0e8ba 100644
--- a/Framework/Kernel/inc/MantidKernel/EmptyValues.h
+++ b/Framework/Kernel/inc/MantidKernel/EmptyValues.h
@@ -15,19 +15,40 @@
 #include "MantidKernel/DllConfig.h"
 #include "MantidKernel/System.h"
 
+#include <limits>
+
 namespace Mantid {
 
-/// Returns what we consider an "empty" integer
-DLLExport int EMPTY_INT();
+/**
+ * Returns what we consider an "empty" integer within a property
+ * @returns An flag value
+ */
+constexpr int EMPTY_INT() noexcept { return std::numeric_limits<int>::max(); }
+
+/**
+ * Returns what we consider an "empty" long within a property
+ * @returns An flag value
+ */
+constexpr long EMPTY_LONG() noexcept {
+  return std::numeric_limits<long>::max();
+}
 
-/// Returns what we consider an "empty" long
-DLLExport long EMPTY_LONG();
+/**
+ * Returns what we consider an "empty" int64_t within a property
+ * @returns An flag value
+ */
+constexpr int64_t EMPTY_INT64() noexcept {
+  return std::numeric_limits<int64_t>::max();
+}
 
-/// Returns what we consider an "empty" int64_t
-DLLExport int64_t EMPTY_INT64();
+/**
+ * Returns what we consider an "empty" double within a property
+ * @returns An flag value
+ */
+constexpr double EMPTY_DBL() noexcept {
+  return std::numeric_limits<double>::max() / 2;
+}
 
-/// Return what we consider to be an empty double
-DLLExport double EMPTY_DBL();
 } // namespace Mantid
 
 #endif // MANTID_KERNEL_EMPTYVALUES_H_
diff --git a/Framework/Kernel/inc/MantidKernel/UsageService.h b/Framework/Kernel/inc/MantidKernel/UsageService.h
index b17a111f9c2d3a3d927411ef10b59631b5111a71..3572577984737497dd76e5f22c56ed3c64f40985 100644
--- a/Framework/Kernel/inc/MantidKernel/UsageService.h
+++ b/Framework/Kernel/inc/MantidKernel/UsageService.h
@@ -50,9 +50,9 @@ public:
 class MANTID_KERNEL_DLL UsageServiceImpl {
 public:
   /// Sets the application name that has invoked Mantid
-  void setApplication(const std::string &name);
+  void setApplicationName(const std::string &name);
   /// Returns the application name that has invoked Mantid
-  std::string getApplication() const;
+  std::string getApplicationName() const;
   /// Sets the interval that the timer checks for tasks
   void setInterval(const uint32_t seconds = 60);
   /// Registers the Startup of Mantid
diff --git a/Framework/Kernel/src/EmptyValues.cpp b/Framework/Kernel/src/EmptyValues.cpp
deleted file mode 100644
index aa21ee810b748f553efc0eb180461968f8c546be..0000000000000000000000000000000000000000
--- a/Framework/Kernel/src/EmptyValues.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-// Mantid Repository : https://github.com/mantidproject/mantid
-//
-// Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
-//     NScD Oak Ridge National Laboratory, European Spallation Source
-//     & Institut Laue - Langevin
-// SPDX - License - Identifier: GPL - 3.0 +
-//------------------------------------------------------------------------------
-// Includes
-//------------------------------------------------------------------------------
-#include "MantidKernel/EmptyValues.h"
-#include <cfloat>
-#include <climits>
-
-namespace Mantid {
-
-/**
- * Returns what we consider an "empty" integer within a property
- * @returns An flag value
- */
-int EMPTY_INT() { return INT_MAX; }
-
-/**
- * Returns what we consider an "empty" long within a property
- * @returns An flag value
- */
-long EMPTY_LONG() { return LONG_MAX; }
-
-/**
- * Returns what we consider an "empty" int64_t within a property
- * @returns An flag value
- */
-int64_t EMPTY_INT64() { return INT64_MAX; }
-
-/**
- * Returns what we consider an "empty" double within a property
- * @returns An flag value
- */
-double EMPTY_DBL() { return DBL_MAX / 2; }
-
-} // namespace Mantid
diff --git a/Framework/Kernel/src/UsageService.cpp b/Framework/Kernel/src/UsageService.cpp
index e99031b1bf4143d480cd1bb8690d0e8746612672..d113bc4fa86e442501be9035bb4e81525adec173 100644
--- a/Framework/Kernel/src/UsageService.cpp
+++ b/Framework/Kernel/src/UsageService.cpp
@@ -68,6 +68,7 @@ UsageServiceImpl::UsageServiceImpl()
     : m_timer(), m_timerTicks(0), m_timerTicksTarget(0), m_FeatureQueue(),
       m_FeatureQueueSizeThreshold(50), m_isEnabled(false), m_mutex(),
       m_application("python"),
+      m_startTime(Types::Core::DateAndTime::getCurrentTime()),
       m_startupActiveMethod(this, &UsageServiceImpl::sendStartupAsyncImpl),
       m_featureActiveMethod(this, &UsageServiceImpl::sendFeatureAsyncImpl) {
   setInterval(60);
@@ -78,15 +79,16 @@ UsageServiceImpl::UsageServiceImpl()
   } else {
     m_url = url.get();
     g_log.debug() << "Root usage reporting url is " << m_url << "\n";
-  }
-  m_startTime = Types::Core::DateAndTime::getCurrentTime();
+  };
 }
 
-void UsageServiceImpl::setApplication(const std::string &name) {
+void UsageServiceImpl::setApplicationName(const std::string &name) {
   m_application = name;
 }
 
-std::string UsageServiceImpl::getApplication() const { return m_application; }
+std::string UsageServiceImpl::getApplicationName() const {
+  return m_application;
+}
 
 void UsageServiceImpl::setInterval(const uint32_t seconds) {
   // set the ticks target to by 24 hours / interval
diff --git a/Framework/Kernel/test/UsageServiceTest.h b/Framework/Kernel/test/UsageServiceTest.h
index 5187942f7041748c7a876e761ccce4157312ee23..3a5048c87aea34291bafb5405b0be4834b2734c5 100644
--- a/Framework/Kernel/test/UsageServiceTest.h
+++ b/Framework/Kernel/test/UsageServiceTest.h
@@ -63,7 +63,7 @@ public:
   void test_startupMessage() {
     TestableUsageService usageService;
     std::string name = "My testing application name";
-    usageService.setApplication(name);
+    usageService.setApplicationName(name);
     std::string message = usageService.generateStartupMessage();
 
     ::Json::Reader reader;
@@ -162,11 +162,11 @@ public:
   void test_setApplicationName() {
     TestableUsageService usageService;
     // test default first
-    TS_ASSERT_EQUALS(usageService.getApplication(), "python");
+    TS_ASSERT_EQUALS(usageService.getApplicationName(), "python");
 
     std::string name = "My testing application name";
-    usageService.setApplication(name);
-    TS_ASSERT_EQUALS(usageService.getApplication(), name);
+    usageService.setApplicationName(name);
+    TS_ASSERT_EQUALS(usageService.getApplicationName(), name);
   }
 };
 
diff --git a/Framework/MDAlgorithms/src/MDWSDescription.cpp b/Framework/MDAlgorithms/src/MDWSDescription.cpp
index 6203fcab08221f707a431c33de2787bbc412dc93..5b6801aa10ab68e9072f6b72ec8e804052bc6add 100644
--- a/Framework/MDAlgorithms/src/MDWSDescription.cpp
+++ b/Framework/MDAlgorithms/src/MDWSDescription.cpp
@@ -355,29 +355,18 @@ void MDWSDescription::fillAddProperties(
   size_t nDimPropNames = dimPropertyNames.size();
   if (AddCoord.size() != nDimPropNames)
     AddCoord.resize(nDimPropNames);
+  const auto &runObj = inWS2D->run();
 
   for (size_t i = 0; i < nDimPropNames; i++) {
-    // HACK: A METHOD, Which converts TSP into value, correspondent to time
-    // scale of matrix workspace has to be developed and deployed!
-    Kernel::Property *pProperty =
-        (inWS2D->run().getProperty(dimPropertyNames[i]));
-    Kernel::TimeSeriesProperty<double> *run_property =
-        dynamic_cast<Kernel::TimeSeriesProperty<double> *>(pProperty);
-    if (run_property) {
-      AddCoord[i] = coord_t(run_property->firstValue());
-    } else {
-      // e.g Ei can be a property and dimension
-      Kernel::PropertyWithValue<double> *proc_property =
-          dynamic_cast<Kernel::PropertyWithValue<double> *>(pProperty);
-      if (!proc_property) {
-        std::string ERR =
-            " Can not interpret property, used as dimension.\n Property: " +
-            dimPropertyNames[i] +
-            " is neither a time series (run) property "
-            "nor a property with value<double>";
-        throw(std::invalid_argument(ERR));
-      }
-      AddCoord[i] = coord_t(*(proc_property));
+    try {
+      const double value = runObj.getLogAsSingleValue(
+          dimPropertyNames[i], Mantid::Kernel::Math::TimeAveragedMean);
+      AddCoord[i] = static_cast<coord_t>(value);
+    } catch (std::invalid_argument &) {
+      std::string ERR =
+          " Can not interpret property, used as dimension.\n Property: " +
+          dimPropertyNames[i] + " cannot be converted into a double.";
+      throw(std::invalid_argument(ERR));
     }
   }
 }
diff --git a/Framework/MDAlgorithms/test/ConvertToMDMinMaxLocalTest.h b/Framework/MDAlgorithms/test/ConvertToMDMinMaxLocalTest.h
index c57a700d6f3fd517b5005d42ea80c5dec231185e..fed9171f0709cd504c774c3b6878d4d8d9334c1b 100644
--- a/Framework/MDAlgorithms/test/ConvertToMDMinMaxLocalTest.h
+++ b/Framework/MDAlgorithms/test/ConvertToMDMinMaxLocalTest.h
@@ -193,9 +193,8 @@ public:
     TS_ASSERT_THROWS_NOTHING(alg.execute(););
     TS_ASSERT(alg.isExecuted());
     // Check the results
-
-    TS_ASSERT_EQUALS(alg.getPropertyValue("MinValues"), "0.12187,9.99");
-    TS_ASSERT_EQUALS(alg.getPropertyValue("MaxValues"), "0.126745,9.99");
+    TS_ASSERT_EQUALS(alg.getPropertyValue("MinValues"), "0.12187,7.69667");
+    TS_ASSERT_EQUALS(alg.getPropertyValue("MaxValues"), "0.126745,7.69667");
     // Remove workspace from the data service.
     Mantid::API::AnalysisDataService::Instance().remove(WSName);
   }
@@ -249,6 +248,8 @@ private:
     Mantid::Geometry::OrientedLattice latt(2, 3, 4, 90, 90, 90);
     ws->mutableSample().setOrientedLattice(&latt);
 
+    // time average value of this is the simple average
+    // of the first three values = 7.69667
     Mantid::Kernel::TimeSeriesProperty<double> *p =
         new Mantid::Kernel::TimeSeriesProperty<double>("doubleProp");
     TS_ASSERT_THROWS_NOTHING(p->addValue("2007-11-30T16:17:00", 9.99));
diff --git a/Framework/Muon/CMakeLists.txt b/Framework/Muon/CMakeLists.txt
index 676a0670d395d59b5c1a59e01a1ecf7b4f54a373..a37eed87108b3383ab2c5dfeda77b84c3de3e836 100644
--- a/Framework/Muon/CMakeLists.txt
+++ b/Framework/Muon/CMakeLists.txt
@@ -14,7 +14,9 @@ set ( SRC_FILES
         src/MuonAsymmetryHelper.cpp
         src/MuonGroupDetectors.cpp
         src/MuonGroupingCounts.cpp
+        src/MuonGroupingAsymmetry.cpp
         src/MuonPreProcess.cpp
+        src/MuonPairingAsymmetry.cpp
         src/PhaseQuadMuon.cpp
         src/PlotAsymmetryByLogValue.cpp
         src/RemoveExpDecay.cpp
@@ -37,6 +39,8 @@ set ( INC_FILES
         inc/MantidMuon/MuonAsymmetryHelper.h
         inc/MantidMuon/MuonGroupDetectors.h
         inc/MantidMuon/MuonGroupingCounts.h
+        inc/MantidMuon/MuonPairingAsymmetry.h
+        inc/MantidMuon/MuonGroupingAsymmetry.h
         inc/MantidMuon/MuonPreProcess.h
         inc/MantidMuon/PhaseQuadMuon.h
         inc/MantidMuon/PlotAsymmetryByLogValue.h
@@ -58,7 +62,9 @@ set ( TEST_FILES
         MuonAlgorithmHelperTest.h
         EstimateMuonAsymmetryFromCountsTest.h
         MuonGroupDetectorsTest.h
+        MuonPairingAsymmetryTest.h
         MuonGroupingCountsTest.h
+        MuonGroupingAsymmetryTest.h
         MuonPreProcessTest.h
         PhaseQuadMuonTest.h
         PlotAsymmetryByLogValueTest.h
diff --git a/Framework/Muon/inc/MantidMuon/MuonAlgorithmHelper.h b/Framework/Muon/inc/MantidMuon/MuonAlgorithmHelper.h
index eafd889e648203f4f57b1bc3034e6e814ebac68b..fd81667ec18cc17afdf09547ed4156ae4e46c5e5 100644
--- a/Framework/Muon/inc/MantidMuon/MuonAlgorithmHelper.h
+++ b/Framework/Muon/inc/MantidMuon/MuonAlgorithmHelper.h
@@ -124,6 +124,8 @@ DLLExport bool checkValidPair(const std::string &name1,
 /// Check whether a group or pair name is valid
 DLLExport bool checkValidGroupPairName(const std::string &name);
 
+DLLExport bool is_alphanumerical_or_underscore(char character);
+
 DLLExport Mantid::API::MatrixWorkspace_sptr
 sumPeriods(const Mantid::API::WorkspaceGroup_sptr &inputWS,
            const std::vector<int> &periodsToSum);
diff --git a/Framework/Muon/inc/MantidMuon/MuonGroupingAsymmetry.h b/Framework/Muon/inc/MantidMuon/MuonGroupingAsymmetry.h
new file mode 100644
index 0000000000000000000000000000000000000000..4450c90a65c4fb1ef99e16e71829fc9aea8616c5
--- /dev/null
+++ b/Framework/Muon/inc/MantidMuon/MuonGroupingAsymmetry.h
@@ -0,0 +1,49 @@
+// Mantid Repository : https://github.com/mantidproject/mantid
+//
+// Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
+//     NScD Oak Ridge National Laboratory, European Spallation Source
+//     & Institut Laue - Langevin
+// SPDX - License - Identifier: GPL - 3.0 +
+#ifndef MANTID_MUON_MUONGROUPINGASYMMETRY_H_
+#define MANTID_MUON_MUONGROUPINGASYMMETRY_H_
+
+#include "MantidAPI/Algorithm.h"
+#include "MantidAPI/MatrixWorkspace.h"
+#include "MantidAPI/WorkspaceGroup.h"
+
+using namespace Mantid::API;
+
+namespace Mantid {
+namespace Muon {
+
+class DLLExport MuonGroupingAsymmetry : public API::Algorithm {
+public:
+  MuonGroupingAsymmetry() : API::Algorithm() {}
+  ~MuonGroupingAsymmetry() {}
+
+  const std::string name() const override { return "MuonGroupingAsymmetry"; }
+  int version() const override { return (1); }
+  const std::string category() const override { return "Muon\\DataHandling"; }
+  const std::string summary() const override {
+    return "Apply an estimate of the asymmetry to a particular detector "
+           "grouping in Muon data.";
+  }
+  const std::vector<std::string> seeAlso() const override {
+    return {"MuonProcess", "EstimateMuonAsymmetryFromCounts", "Minus", "Plus"};
+  }
+
+private:
+  void init() override;
+  void exec() override;
+
+  std::map<std::string, std::string> validateInputs() override;
+
+  WorkspaceGroup_sptr createGroupWorkspace(WorkspaceGroup_sptr inputWS);
+
+  void addGroupingAsymmetrySampleLogs(MatrixWorkspace_sptr workspace);
+};
+
+} // namespace Muon
+} // namespace Mantid
+
+#endif /* MANTID_MUON_MUONGROUPINGASYMMETRY_H_ */
diff --git a/Framework/Muon/inc/MantidMuon/MuonPairingAsymmetry.h b/Framework/Muon/inc/MantidMuon/MuonPairingAsymmetry.h
new file mode 100644
index 0000000000000000000000000000000000000000..f04d267a7033f08fcfa7d296cb07e46aab1c30ee
--- /dev/null
+++ b/Framework/Muon/inc/MantidMuon/MuonPairingAsymmetry.h
@@ -0,0 +1,70 @@
+// Mantid Repository : https://github.com/mantidproject/mantid
+//
+// Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
+//     NScD Oak Ridge National Laboratory, European Spallation Source
+//     & Institut Laue - Langevin
+// SPDX - License - Identifier: GPL - 3.0 +
+#ifndef MANTID_MUON_MUONPAIRINGASYMMETRY_H_
+#define MANTID_MUON_MUONPAIRINGASYMMETRY_H_
+
+#include "MantidAPI/Algorithm.h"
+#include "MantidAPI/MatrixWorkspace.h"
+#include "MantidAPI/WorkspaceGroup.h"
+
+using namespace Mantid::API;
+
+namespace Mantid {
+namespace Muon {
+
+class DLLExport MuonPairingAsymmetry : public API::Algorithm {
+public:
+  MuonPairingAsymmetry() : API::Algorithm() {}
+  ~MuonPairingAsymmetry() {}
+
+  const std::string name() const override { return "MuonPairingAsymmetry"; }
+  int version() const override { return (1); }
+  const std::string category() const override { return "Muon\\DataHandling"; }
+  const std::string summary() const override {
+    return "Apply a pairing asymmetry calculation between two detector groups "
+           "from Muon data.";
+  }
+  const std::vector<std::string> seeAlso() const override {
+    return {"MuonProcess",   "MuonPreProcess", "AsymmetryCalc",
+            "AppendSpectra", "Plus",           "Minus"};
+  }
+
+private:
+  void init() override;
+  void exec() override;
+  bool checkGroups() override;
+  // Validation split across several functions due to size
+  std::map<std::string, std::string> validateInputs() override;
+  void validateManualGroups(std::map<std::string, std::string> &errors);
+  void validateGroupsWorkspaces(std::map<std::string, std::string> &errors);
+  void validatePeriods(WorkspaceGroup_sptr inputWS,
+                       std::map<std::string, std::string> &errors);
+
+  WorkspaceGroup_sptr createGroupWorkspace(WorkspaceGroup_sptr inputWS);
+  MatrixWorkspace_sptr appendSpectra(MatrixWorkspace_sptr inputWS1,
+                                     MatrixWorkspace_sptr inputWS2);
+
+  /// Perform an asymmetry calculation
+  MatrixWorkspace_sptr pairAsymmetryCalc(MatrixWorkspace_sptr inputWS,
+                                         const double &alpha);
+  MatrixWorkspace_sptr calcPairAsymmetryWithSummedAndSubtractedPeriods(
+      const std::vector<int> &summedPeriods,
+      const std::vector<int> &subtractedPeriods,
+      WorkspaceGroup_sptr groupedPeriods, const double &alpha);
+
+  /// Execute the algorithm if "SpecifyGroupsManually" is checked
+  MatrixWorkspace_sptr execSpecifyGroupsManually();
+
+  MatrixWorkspace_sptr execGroupWorkspaceInput();
+
+  void setPairAsymmetrySampleLogs(MatrixWorkspace_sptr workspace);
+};
+
+} // namespace Muon
+} // namespace Mantid
+
+#endif /* MANTID_MUON_MUONPAIRINGASYMMETRY_H_ */
diff --git a/Framework/Muon/src/MuonAlgorithmHelper.cpp b/Framework/Muon/src/MuonAlgorithmHelper.cpp
index f6599d66788fae017c380482237417ea0915a9b3..2cfa0df611d2d6fe0917502a6cdd3e8f3300fb60 100644
--- a/Framework/Muon/src/MuonAlgorithmHelper.cpp
+++ b/Framework/Muon/src/MuonAlgorithmHelper.cpp
@@ -522,6 +522,10 @@ bool checkValidGroupPairName(const std::string &name) {
   return true;
 }
 
+bool is_alphanumerical_or_underscore(char character) {
+  return (isalpha(character) || isdigit(character) || (character == '_'));
+}
+
 /**
  * Sums the specified periods of the input workspace group
  * @param periodsToSum :: [input] List of period indexes (1-based) to be summed
diff --git a/Framework/Muon/src/MuonGroupingAsymmetry.cpp b/Framework/Muon/src/MuonGroupingAsymmetry.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e100808b0b0454192a63c6c762b20ddc3d1dca19
--- /dev/null
+++ b/Framework/Muon/src/MuonGroupingAsymmetry.cpp
@@ -0,0 +1,318 @@
+// Mantid Repository : https://github.com/mantidproject/mantid
+//
+// Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
+//     NScD Oak Ridge National Laboratory, European Spallation Source
+//     & Institut Laue - Langevin
+// SPDX - License - Identifier: GPL - 3.0 +
+#include "MantidMuon/MuonGroupingAsymmetry.h"
+#include "MantidAPI/Algorithm.h"
+#include "MantidAPI/AlgorithmManager.h"
+#include "MantidAPI/MatrixWorkspace.h"
+#include "MantidAPI/WorkspaceFactory.h"
+#include "MantidAPI/WorkspaceGroup.h"
+#include "MantidDataObjects/TableWorkspace.h"
+#include "MantidHistogramData/HistogramMath.h"
+#include "MantidKernel/ArrayProperty.h"
+#include "MantidKernel/make_unique.h"
+#include "MantidMuon/MuonAlgorithmHelper.h"
+
+#include <algorithm>
+
+using namespace Mantid::API;
+using namespace Mantid::DataObjects;
+using namespace Mantid::Kernel;
+
+namespace {
+
+bool checkPeriodInWorkspaceGroup(const int &period,
+                                 WorkspaceGroup_const_sptr &workspace) {
+  return period <= workspace->getNumberOfEntries();
+}
+
+/**
+ * Estimate the asymmetrey for the given workspace (TF data).
+ * @param inputWS :: [input] Workspace to calculate asymmetry for
+ * @param index :: [input] GroupIndex (fit only the requested spectrum): use -1
+ * for "unset"
+ * @returns Result of the removal
+ */
+MatrixWorkspace_sptr estimateAsymmetry(const Workspace_sptr &inputWS,
+                                       const int index, const double startX,
+                                       const double endX,
+                                       const double normalizationIn) {
+  IAlgorithm_sptr asym =
+      AlgorithmManager::Instance().create("EstimateMuonAsymmetryFromCounts");
+  asym->setChild(true);
+  asym->setProperty("InputWorkspace", inputWS);
+  asym->setProperty("WorkspaceName", inputWS->getName());
+  if (index > -1) {
+    const std::vector<int> spec(1, index);
+    asym->setProperty("Spectra", spec);
+  }
+  asym->setProperty("OutputWorkspace", "__NotUsed__");
+  asym->setProperty("StartX", startX);
+  asym->setProperty("EndX", endX);
+  asym->setProperty("NormalizationIn", normalizationIn);
+  asym->setProperty("OutputUnNormData", false);
+  asym->setProperty("OutputUnNormWorkspace", "tmp_unNorm");
+  asym->execute();
+  MatrixWorkspace_sptr outWS = asym->getProperty("OutputWorkspace");
+
+  return outWS;
+}
+
+Mantid::API::MatrixWorkspace_sptr estimateMuonAsymmetry(
+    WorkspaceGroup_sptr inputWS, const std::vector<int> &summedPeriods,
+    const std::vector<int> &subtractedPeriods, int groupIndex,
+    const double startX, const double endX, const double normalizationIn) {
+  MatrixWorkspace_sptr tempWS;
+  int numPeriods = inputWS->getNumberOfEntries();
+  if (numPeriods > 1) {
+
+    auto summedWS =
+        Mantid::MuonAlgorithmHelper::sumPeriods(inputWS, summedPeriods);
+    auto subtractedWS =
+        Mantid::MuonAlgorithmHelper::sumPeriods(inputWS, subtractedPeriods);
+
+    // Remove decay (summed periods ws)
+    MatrixWorkspace_sptr asymSummedPeriods =
+        estimateAsymmetry(summedWS, groupIndex, startX, endX, normalizationIn);
+
+    if (!subtractedPeriods.empty()) {
+      // Remove decay (subtracted periods ws)
+      MatrixWorkspace_sptr asymSubtractedPeriods = estimateAsymmetry(
+          subtractedWS, groupIndex, startX, endX, normalizationIn);
+
+      // Now subtract
+      tempWS = Mantid::MuonAlgorithmHelper::subtractWorkspaces(
+          asymSummedPeriods, asymSubtractedPeriods);
+    } else {
+      tempWS = asymSummedPeriods;
+    }
+  } else {
+    // Only one period was supplied
+    tempWS =
+        estimateAsymmetry(inputWS->getItem(0), groupIndex, startX, endX,
+                          normalizationIn); // change -1 to m_groupIndex and
+                                            // follow through to store as a
+                                            // table for later.
+  }
+
+  MatrixWorkspace_sptr outWS =
+      Mantid::MuonAlgorithmHelper::extractSpectrum(tempWS, groupIndex);
+  return outWS;
+}
+
+MatrixWorkspace_sptr groupDetectors(MatrixWorkspace_sptr workspace,
+                                    const std::vector<int> &detectorIDs) {
+
+  auto outputWS = WorkspaceFactory::Instance().create(workspace, 1);
+
+  const std::vector<size_t> wsIndices =
+      workspace->getIndicesFromDetectorIDs(detectorIDs);
+
+  if (wsIndices.size() != detectorIDs.size())
+    throw std::invalid_argument("Some of the detector IDs were not found");
+
+  outputWS->getSpectrum(0).clearDetectorIDs();
+  outputWS->setSharedX(0, workspace->sharedX(wsIndices.front()));
+
+  auto hist = outputWS->histogram(0);
+  for (auto &wsIndex : wsIndices) {
+    hist += workspace->histogram(wsIndex);
+    outputWS->getSpectrum(0).addDetectorIDs(
+        workspace->getSpectrum(wsIndex).getDetectorIDs());
+  }
+  outputWS->setHistogram(0, hist);
+  outputWS->getSpectrum(0).setSpectrumNo(static_cast<int32_t>(1));
+  return outputWS;
+}
+
+} // namespace
+
+namespace Mantid {
+namespace Muon {
+
+// Register the algorithm into the AlgorithmFactory
+DECLARE_ALGORITHM(MuonGroupingAsymmetry)
+
+void MuonGroupingAsymmetry::init() {
+  const std::string emptyString("");
+  const std::vector<int> defaultGrouping = {1};
+  const std::vector<int> defaultPeriods = {1};
+
+  declareProperty(
+      Mantid::Kernel::make_unique<WorkspaceProperty<WorkspaceGroup>>(
+          "InputWorkspace", emptyString, Direction::Input,
+          PropertyMode::Mandatory),
+      "Input workspace containing data from detectors which are to "
+      "be grouped.");
+
+  declareProperty(Mantid::Kernel::make_unique<API::WorkspaceProperty<>>(
+                      "OutputWorkspace", emptyString, Direction::Output),
+                  "Output workspace which will hold the results of the group "
+                  "asymmetry calculation.");
+
+  declareProperty("GroupName", emptyString,
+                  "The name of the group. Must "
+                  "contain at least one alphanumeric "
+                  "character.",
+                  Direction::Input);
+
+  declareProperty(make_unique<ArrayProperty<int>>(
+                      "Grouping", defaultGrouping,
+                      IValidator_sptr(new NullValidator), Direction::Input),
+                  "The grouping of detectors, comma separated list of detector "
+                  "IDs or hyphenated ranges of IDs.");
+
+  declareProperty("AsymmetryTimeMin", 0.0,
+                  "Start time for the asymmetry estimation (in micro "
+                  "seconds). Defaults to the start time of the InputWorkspace.",
+                  Direction::Input);
+
+  declareProperty("AsymmetryTimeMax", 32.0,
+                  "End time for the asymmetry estimation (in micro seconds). "
+                  "Defaults to the end time of the InputWorkspace.",
+                  Direction::Input);
+
+  declareProperty("NormalizationIn", 0.0,
+                  "If this value is non-zero then this is used for the "
+                  "normalization, instead of being estimated.",
+                  Direction::Input);
+
+  declareProperty(make_unique<ArrayProperty<int>>(
+                      "SummedPeriods", defaultPeriods,
+                      IValidator_sptr(new NullValidator), Direction::Input),
+                  "A list of periods to sum in multiperiod data.");
+  declareProperty(
+      make_unique<ArrayProperty<int>>("SubtractedPeriods", Direction::Input),
+      "A list of periods to subtract in multiperiod data.");
+
+  // Perform Group Associations.
+
+  std::string groupingGrp("Grouping Information");
+  setPropertyGroup("GroupName", groupingGrp);
+  setPropertyGroup("Grouping", groupingGrp);
+  setPropertyGroup("AsymmetryTimeMin", groupingGrp);
+  setPropertyGroup("AsymmetryTimeMax", groupingGrp);
+
+  std::string periodGrp("Multi-period Data");
+  setPropertyGroup("SummedPeriods", periodGrp);
+  setPropertyGroup("SubtractedPeriods", periodGrp);
+}
+
+std::map<std::string, std::string> MuonGroupingAsymmetry::validateInputs() {
+  std::map<std::string, std::string> errors;
+
+  const std::string groupName = this->getProperty("GroupName");
+  if (groupName.empty()) {
+    errors["GroupName"] = "Group name must be specified.";
+  }
+
+  if (!std::all_of(std::begin(groupName), std::end(groupName),
+                   Mantid::MuonAlgorithmHelper::isAlphanumericOrUnderscore)) {
+    errors["GroupName"] =
+        "The group name must contain alphnumeric characters and _ only.";
+  }
+
+  WorkspaceGroup_const_sptr inputWS = getProperty("InputWorkspace");
+  const std::vector<int> summedPeriods = getProperty("SummedPeriods");
+  const std::vector<int> subtractedPeriods = getProperty("SubtractedPeriods");
+
+  if (summedPeriods.empty() && subtractedPeriods.empty()) {
+    errors["SummedPeriods"] = "At least one period must be specified";
+  }
+
+  if (!summedPeriods.empty()) {
+    const int highestSummedPeriod =
+        *std::max_element(summedPeriods.begin(), summedPeriods.end());
+    if (!checkPeriodInWorkspaceGroup(highestSummedPeriod, inputWS)) {
+      errors["SummedPeriods"] = "Requested period (" +
+                                std::to_string(highestSummedPeriod) +
+                                ") exceeds periods in data";
+    }
+    if (std::any_of(summedPeriods.begin(), summedPeriods.end(),
+                    [](const int &i) { return i < 0; })) {
+      errors["SummedPeriods"] = "Requested periods must be greater that 0.";
+    }
+  }
+
+  if (!subtractedPeriods.empty()) {
+    const int highestSubtractedPeriod =
+        *std::max_element(subtractedPeriods.begin(), subtractedPeriods.end());
+    if (!checkPeriodInWorkspaceGroup(highestSubtractedPeriod, inputWS)) {
+      errors["SubtractedPeriods"] = "Requested period (" +
+                                    std::to_string(highestSubtractedPeriod) +
+                                    ") exceeds periods in data";
+    }
+    if (std::any_of(subtractedPeriods.begin(), subtractedPeriods.end(),
+                    [](const int &i) { return i < 0; })) {
+      errors["SubtractedPeriods"] = "Requested periods must be greater that 0.";
+    }
+  }
+
+  if (inputWS->getNumberOfEntries() < 1) {
+    errors["InputWorkspace"] = "WorkspaceGroup contains no periods.";
+  }
+
+  const double xMin = getProperty("AsymmetryTimeMin");
+  const double xMax = getProperty("AsymmetryTimeMax");
+  if (xMax <= xMin) {
+    errors["AsymmetryTimeMin"] = "TimeMax <= TimeMin";
+  }
+
+  return errors;
+}
+
+WorkspaceGroup_sptr
+MuonGroupingAsymmetry::createGroupWorkspace(WorkspaceGroup_sptr inputWS) {
+  const std::vector<int> group = this->getProperty("Grouping");
+  auto groupedPeriods = boost::make_shared<WorkspaceGroup>();
+  // for each period
+  for (auto &&workspace : *inputWS) {
+    auto groupWS = groupDetectors(
+        boost::dynamic_pointer_cast<MatrixWorkspace>(workspace), group);
+    groupedPeriods->addWorkspace(groupWS);
+  }
+  return groupedPeriods;
+}
+
+void MuonGroupingAsymmetry::exec() {
+
+  WorkspaceGroup_sptr inputWS = getProperty("InputWorkspace");
+  MatrixWorkspace_sptr outWS;
+
+  const double startX = getProperty("AsymmetryTimeMin");
+  const double endX = getProperty("AsymmetryTimeMax");
+  const double normalizationIn = getProperty("NormalizationIn");
+
+  const std::vector<int> summedPeriods = getProperty("SummedPeriods");
+  const std::vector<int> subtractedPeriods = getProperty("SubtractedPeriods");
+
+  WorkspaceGroup_sptr groupedWS = createGroupWorkspace(inputWS);
+
+  outWS = estimateMuonAsymmetry(groupedWS, summedPeriods, subtractedPeriods, 0,
+                                startX, endX, normalizationIn);
+
+  addGroupingAsymmetrySampleLogs(outWS);
+  setProperty("OutputWorkspace", outWS);
+}
+
+void MuonGroupingAsymmetry::addGroupingAsymmetrySampleLogs(
+    MatrixWorkspace_sptr workspace) {
+  MuonAlgorithmHelper::addSampleLog(workspace, "analysis_asymmetry_group_name",
+                                    getPropertyValue("GroupName"));
+  MuonAlgorithmHelper::addSampleLog(workspace, "analysis_asymmetry_group",
+                                    getPropertyValue("Grouping"));
+  MuonAlgorithmHelper::addSampleLog(workspace, "analysis_asymmetry_x_min",
+                                    getPropertyValue("AsymmetryTimeMin"));
+  MuonAlgorithmHelper::addSampleLog(workspace, "analysis_asymmetry_x_max",
+                                    getPropertyValue("AsymmetryTimeMax"));
+  MuonAlgorithmHelper::addSampleLog(workspace, "analysis_periods_summed",
+                                    getPropertyValue("SummedPeriods"));
+  MuonAlgorithmHelper::addSampleLog(workspace, "analysis_periods_subtracted",
+                                    getPropertyValue("SubtractedPeriods"));
+}
+
+} // namespace Muon
+} // namespace Mantid
diff --git a/Framework/Muon/src/MuonPairingAsymmetry.cpp b/Framework/Muon/src/MuonPairingAsymmetry.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1c52c2a2d88980001f5f744813dc1da44806047d
--- /dev/null
+++ b/Framework/Muon/src/MuonPairingAsymmetry.cpp
@@ -0,0 +1,475 @@
+// Mantid Repository : https://github.com/mantidproject/mantid
+//
+// Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
+//     NScD Oak Ridge National Laboratory, European Spallation Source
+//     & Institut Laue - Langevin
+// SPDX - License - Identifier: GPL - 3.0 +
+#include "MantidMuon/MuonPairingAsymmetry.h"
+#include "MantidAPI/Algorithm.h"
+#include "MantidAPI/AlgorithmManager.h"
+#include "MantidAPI/WorkspaceFactory.h"
+#include "MantidAPI/WorkspaceGroup.h"
+#include "MantidDataObjects/TableWorkspace.h"
+#include "MantidHistogramData/HistogramMath.h"
+#include "MantidKernel/ArrayProperty.h"
+#include "MantidKernel/EnabledWhenProperty.h"
+#include "MantidKernel/MandatoryValidator.h"
+#include "MantidMuon/MuonAlgorithmHelper.h"
+#include <boost/format.hpp>
+
+using namespace Mantid::API;
+using namespace Mantid::DataObjects;
+using namespace Mantid::Kernel;
+
+namespace {
+bool checkPeriodInWorkspaceGroup(const int &period,
+                                 WorkspaceGroup_const_sptr workspace) {
+  return period <= workspace->getNumberOfEntries();
+}
+
+int countPeriods(Workspace_const_sptr ws) {
+  if (auto tmp = boost::dynamic_pointer_cast<const WorkspaceGroup>(ws)) {
+    return tmp->getNumberOfEntries();
+  } else {
+    return 1;
+  }
+}
+
+bool checkConsistentPeriods(Workspace_const_sptr ws1,
+                            Workspace_const_sptr ws2) {
+  if (ws1->isGroup()) {
+    if (!ws2->isGroup()) {
+      return false;
+    }
+    if (countPeriods(ws1) != countPeriods(ws2)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+MatrixWorkspace_sptr getWorkspace(WorkspaceGroup_sptr group, const int &index) {
+  auto ws = group->getItem(index);
+  return boost::dynamic_pointer_cast<MatrixWorkspace>(ws);
+}
+
+MatrixWorkspace_sptr groupDetectors(MatrixWorkspace_sptr workspace,
+                                    const std::vector<int> &detectorIDs) {
+
+  auto outputWS = WorkspaceFactory::Instance().create(workspace, 1);
+
+  std::vector<size_t> wsIndices =
+      workspace->getIndicesFromDetectorIDs(detectorIDs);
+
+  if (wsIndices.size() != detectorIDs.size())
+    throw std::invalid_argument(
+        str(boost::format("The number of detectors requested does not equal"
+                          "the number of detectors provided %1% != %2%") %
+            wsIndices.size() % detectorIDs.size()));
+
+  outputWS->getSpectrum(0).clearDetectorIDs();
+  outputWS->setSharedX(0, workspace->sharedX(wsIndices.front()));
+
+  auto hist = outputWS->histogram(0);
+  for (auto &wsIndex : wsIndices) {
+    hist += workspace->histogram(wsIndex);
+    outputWS->getSpectrum(0).addDetectorIDs(
+        workspace->getSpectrum(wsIndex).getDetectorIDs());
+  }
+  outputWS->setHistogram(0, hist);
+  outputWS->getSpectrum(0).setSpectrumNo(static_cast<int32_t>(1));
+  return outputWS;
+}
+
+// Convert a Workspace_sptr (which may be single period, MatrixWorkspace, or
+// multi period WorkspaceGroup) to a WorkspaceGroup_sptr
+WorkspaceGroup_sptr workspaceToWorkspaceGroup(Workspace_sptr workspace) {
+
+  WorkspaceGroup_sptr ws1;
+  if (workspace->isGroup()) {
+    ws1 = boost::dynamic_pointer_cast<WorkspaceGroup>(workspace);
+  } else {
+    ws1 = boost::make_shared<WorkspaceGroup>();
+    ws1->addWorkspace(boost::dynamic_pointer_cast<MatrixWorkspace>(workspace));
+  }
+  return ws1;
+}
+
+} // namespace
+
+namespace Mantid {
+namespace Muon {
+
+// Register the algorithm into the AlgorithmFactory
+DECLARE_ALGORITHM(MuonPairingAsymmetry)
+
+void MuonPairingAsymmetry::init() {
+  std::string emptyString("");
+  const std::vector<int> defaultGrouping1 = {1};
+  const std::vector<int> defaultGrouping2 = {2};
+
+  declareProperty(
+      Mantid::Kernel::make_unique<WorkspaceProperty<MatrixWorkspace>>(
+          "OutputWorkspace", emptyString, Direction::Output),
+      "The workspace which will hold the results of the asymmetry "
+      "calculation.");
+
+  declareProperty("PairName", emptyString,
+                  "The name of the pair. Must "
+                  "contain at least one alphanumeric "
+                  "character.",
+                  Direction::Input);
+
+  declareProperty(
+      "Alpha", 1.0, boost::make_shared<MandatoryValidator<double>>(),
+      "Alpha parameter used in the asymmetry calculation.", Direction::Input);
+
+  declareProperty("SpecifyGroupsManually", false,
+                  "Specify the pair of groups manually");
+
+  // Select groups via workspaces
+
+  declareProperty(Mantid::Kernel::make_unique<WorkspaceProperty<Workspace>>(
+                      "InputWorkspace1", emptyString, Direction::Input,
+                      PropertyMode::Optional),
+                  "Input workspace containing data from grouped detectors.");
+
+  declareProperty(Mantid::Kernel::make_unique<WorkspaceProperty<Workspace>>(
+                      "InputWorkspace2", emptyString, Direction::Input,
+                      PropertyMode::Optional),
+                  "Input workspace containing data from grouped detectors.");
+
+  setPropertySettings("InputWorkspace1",
+                      make_unique<Kernel::EnabledWhenProperty>(
+                          "SpecifyGroupsManually", Kernel::IS_EQUAL_TO, "0"));
+  setPropertySettings("InputWorkspace2",
+                      make_unique<Kernel::EnabledWhenProperty>(
+                          "SpecifyGroupsManually", Kernel::IS_EQUAL_TO, "0"));
+
+  // Specify groups manually
+
+  declareProperty(
+      Mantid::Kernel::make_unique<WorkspaceProperty<WorkspaceGroup>>(
+          "InputWorkspace", emptyString, Direction::Input,
+          PropertyMode::Optional),
+      "Input workspace containing data from detectors which are to "
+      "be grouped.");
+  setPropertySettings("InputWorkspace",
+                      make_unique<Kernel::EnabledWhenProperty>(
+                          "SpecifyGroupsManually", Kernel::IS_EQUAL_TO, "1"));
+  declareProperty(make_unique<ArrayProperty<int>>(
+                      "Group1", defaultGrouping1,
+                      IValidator_sptr(new NullValidator), Direction::Input),
+                  "The grouping of detectors, comma separated list of detector "
+                  "IDs or hyphenated ranges of IDs.");
+  declareProperty(make_unique<ArrayProperty<int>>(
+                      "Group2", defaultGrouping2,
+                      IValidator_sptr(new NullValidator), Direction::Input),
+                  "The grouping of detectors, comma separated list of detector "
+                  "IDs or hyphenated ranges of IDs.");
+  setPropertySettings("Group1",
+                      make_unique<Kernel::EnabledWhenProperty>(
+                          "SpecifyGroupsManually", Kernel::IS_EQUAL_TO, "1"));
+  setPropertySettings("Group2",
+                      make_unique<Kernel::EnabledWhenProperty>(
+                          "SpecifyGroupsManually", Kernel::IS_EQUAL_TO, "1"));
+
+  declareProperty(make_unique<ArrayProperty<int>>("SummedPeriods", "1"),
+                  "A list of periods to sum in multiperiod data.");
+  setPropertySettings("SummedPeriods",
+                      make_unique<Kernel::EnabledWhenProperty>(
+                          "SpecifyGroupsManually", Kernel::IS_EQUAL_TO, "1"));
+
+  declareProperty(
+      make_unique<ArrayProperty<int>>("SubtractedPeriods", Direction::Input),
+      "A list of periods to subtract in multiperiod data.");
+  setPropertySettings("SubtractedPeriods",
+                      make_unique<Kernel::EnabledWhenProperty>(
+                          "SpecifyGroupsManually", Kernel::IS_EQUAL_TO, "1"));
+
+  // Group common entries in the interface for clarity.
+  const std::string workspaceGrp("Specify Group Workspaces");
+  setPropertyGroup("InputWorkspace1", workspaceGrp);
+  setPropertyGroup("InputWorkspace2", workspaceGrp);
+
+  const std::string manualGroupGrp("Specify Detector ID Groups Manually");
+  setPropertyGroup("InputWorkspace", manualGroupGrp);
+  setPropertyGroup("Group1", manualGroupGrp);
+  setPropertyGroup("Group2", manualGroupGrp);
+
+  const std::string periodGrp("Multi-period Data");
+  setPropertyGroup("SummedPeriods", periodGrp);
+  setPropertyGroup("SubtractedPeriods", periodGrp);
+}
+
+std::map<std::string, std::string> MuonPairingAsymmetry::validateInputs() {
+  std::map<std::string, std::string> errors;
+
+  // Pair name must be given, and must only contain characters, digits and "_"
+  const std::string pairName = this->getProperty("PairName");
+  if (pairName.empty()) {
+    errors["PairName"] = "Pair name must be specified.";
+  }
+  if (!std::all_of(std::begin(pairName), std::end(pairName),
+                   MuonAlgorithmHelper::isAlphanumericOrUnderscore)) {
+    errors["PairName"] =
+        "The pair name must contain alphnumeric characters and _ only.";
+  }
+
+  double alpha = this->getProperty("Alpha");
+  if (alpha < 0.0) {
+    errors["Alpha"] = "Alpha must be non-negative.";
+  }
+
+  if (this->getProperty("SpecifyGroupsManually")) {
+    validateManualGroups(errors);
+  } else {
+    validateGroupsWorkspaces(errors);
+  }
+
+  return errors;
+}
+
+// Validation on the parameters given if
+// "SpecifyGroupsManually" is true.
+void MuonPairingAsymmetry::validateManualGroups(
+    std::map<std::string, std::string> &errors) {
+
+  std::vector<int> group1 = this->getProperty("Group1");
+  std::vector<int> group2 = this->getProperty("Group2");
+  if (group1.empty()) {
+    errors["Group1"] =
+        "A valid grouping must be supplied (e.g. \"1,2,3,4,5\").";
+  }
+  if (group2.empty()) {
+    errors["Group2"] =
+        "A valid grouping must be supplied (e.g. \"1,2,3,4,5\").";
+  }
+
+  if (std::is_permutation(group1.begin(), group1.end(), group2.begin())) {
+    errors["Group1"] = "The two groups must be different.";
+  }
+
+  WorkspaceGroup_sptr inputWS = this->getProperty("InputWorkspace");
+  validatePeriods(inputWS, errors);
+}
+
+void MuonPairingAsymmetry::validateGroupsWorkspaces(
+    std::map<std::string, std::string> &errors) {
+  Workspace_sptr ws1 = this->getProperty("InputWorkspace1");
+  Workspace_sptr ws2 = this->getProperty("InputWorkspace2");
+  if (ws1->isGroup() && !ws2->isGroup()) {
+    errors["InputWorkspace1"] =
+        "InputWorkspace2 should be multi period to match InputWorkspace1";
+  }
+  if (ws2->isGroup() && !ws1->isGroup()) {
+    errors["InputWorkspace2"] =
+        "InputWorkspace1 should be multi period to match InputWorkspace2";
+  }
+  if (!checkConsistentPeriods(ws1, ws2)) {
+    errors["InputWorkspace1"] = "InputWorkspace1 and InputWorkspace2 have "
+                                "inconsistent numbers of periods.";
+  }
+  if (ws1->isGroup() && ws2->isGroup()) {
+    validatePeriods(boost::dynamic_pointer_cast<WorkspaceGroup>(ws1), errors);
+    validatePeriods(boost::dynamic_pointer_cast<WorkspaceGroup>(ws2), errors);
+  }
+}
+
+bool MuonPairingAsymmetry::checkGroups() { return false; }
+
+void MuonPairingAsymmetry::exec() {
+
+  MatrixWorkspace_sptr outWS;
+
+  if (getProperty("SpecifyGroupsManually")) {
+    outWS = execSpecifyGroupsManually();
+  } else {
+    outWS = execGroupWorkspaceInput();
+  }
+
+  // outWS = boost::dynamic_pointer_cast<MatrixWorkspace>(outWS);
+
+  setPairAsymmetrySampleLogs(outWS);
+  if (!outWS->isGroup()) {
+    setProperty("OutputWorkspace", outWS);
+  }
+}
+
+MatrixWorkspace_sptr MuonPairingAsymmetry::execGroupWorkspaceInput() {
+
+  // Get the input workspace into a useful form
+  Workspace_sptr tmpWS1 = getProperty("InputWorkspace1");
+  Workspace_sptr tmpWS2 = getProperty("InputWorkspace2");
+  WorkspaceGroup_sptr ws1 = workspaceToWorkspaceGroup(tmpWS1);
+  WorkspaceGroup_sptr ws2 = workspaceToWorkspaceGroup(tmpWS2);
+  WorkspaceGroup_sptr groupedPeriods = boost::make_shared<WorkspaceGroup>();
+  for (int i = 0; i < countPeriods(ws1); i++) {
+    groupedPeriods->addWorkspace(
+        appendSpectra(getWorkspace(ws1, i), getWorkspace(ws2, i)));
+  }
+
+  // Do the asymmetry calculation
+  const double alpha = static_cast<double>(getProperty("Alpha"));
+  std::vector<int> summedPeriods = getProperty("SummedPeriods");
+  std::vector<int> subtractedPeriods = getProperty("SubtractedPeriods");
+  return calcPairAsymmetryWithSummedAndSubtractedPeriods(
+      summedPeriods, subtractedPeriods, groupedPeriods, alpha);
+}
+
+MatrixWorkspace_sptr MuonPairingAsymmetry::execSpecifyGroupsManually() {
+
+  WorkspaceGroup_sptr inputWS = getProperty("InputWorkspace");
+  auto groupedPeriods = createGroupWorkspace(inputWS);
+
+  // Do the asymmetry calculation
+  const std::vector<int> summedPeriods = getProperty("SummedPeriods");
+  const std::vector<int> subtractedPeriods = getProperty("SubtractedPeriods");
+  const double alpha = static_cast<double>(getProperty("Alpha"));
+
+  return calcPairAsymmetryWithSummedAndSubtractedPeriods(
+      summedPeriods, subtractedPeriods, groupedPeriods, alpha);
+}
+
+MatrixWorkspace_sptr
+MuonPairingAsymmetry::calcPairAsymmetryWithSummedAndSubtractedPeriods(
+    const std::vector<int> &summedPeriods,
+    const std::vector<int> &subtractedPeriods,
+    WorkspaceGroup_sptr groupedPeriods, const double &alpha) {
+  auto summedWS =
+      MuonAlgorithmHelper::sumPeriods(groupedPeriods, summedPeriods);
+  auto subtractedWS =
+      MuonAlgorithmHelper::sumPeriods(groupedPeriods, subtractedPeriods);
+
+  MatrixWorkspace_sptr asymSummedPeriods = pairAsymmetryCalc(summedWS, alpha);
+
+  if (subtractedPeriods.empty()) {
+    return asymSummedPeriods;
+  }
+
+  MatrixWorkspace_sptr asymSubtractedPeriods =
+      pairAsymmetryCalc(subtractedWS, alpha);
+
+  return MuonAlgorithmHelper::subtractWorkspaces(asymSummedPeriods,
+                                                 asymSubtractedPeriods);
+}
+
+/*
+Create a WorkspaceGroup containing one or more periods; for each period the
+workspace has two spectra corresponding to the two groupings specified in the
+inputs.
+*/
+WorkspaceGroup_sptr
+MuonPairingAsymmetry::createGroupWorkspace(WorkspaceGroup_sptr inputWS) {
+
+  std::vector<int> group1 = this->getProperty("Group1");
+  std::vector<int> group2 = this->getProperty("Group2");
+  auto groupedPeriods = boost::make_shared<WorkspaceGroup>();
+  // for each period
+  for (auto &&workspace : *inputWS) {
+    auto groupWS1 = groupDetectors(
+        boost::dynamic_pointer_cast<MatrixWorkspace>(workspace), group1);
+    auto groupWS2 = groupDetectors(
+        boost::dynamic_pointer_cast<MatrixWorkspace>(workspace), group2);
+    groupedPeriods->addWorkspace(appendSpectra(groupWS1, groupWS2));
+  }
+  return groupedPeriods;
+}
+
+/**
+ * Performs asymmetry calculation on the given workspace using indices 0,1.
+ * @param inputWS :: [input] Workspace to calculate asymmetry from (will use
+ * workspace indices 0,1).
+ * @returns MatrixWorkspace containing result of the calculation.
+ */
+MatrixWorkspace_sptr
+MuonPairingAsymmetry::pairAsymmetryCalc(MatrixWorkspace_sptr inputWS,
+                                        const double &alpha) {
+  MatrixWorkspace_sptr outWS;
+
+  // Ensure our specified spectra definitely point to the data
+  inputWS->getSpectrum(0).setSpectrumNo(0);
+  inputWS->getSpectrum(1).setSpectrumNo(1);
+  const std::vector<int> fwdSpectra = {0};
+  const std::vector<int> bwdSpectra = {1};
+
+  IAlgorithm_sptr alg = this->createChildAlgorithm("AsymmetryCalc");
+  alg->setProperty("InputWorkspace", inputWS);
+  alg->setProperty("ForwardSpectra", fwdSpectra);
+  alg->setProperty("BackwardSpectra", bwdSpectra);
+  alg->setProperty("Alpha", alpha);
+  alg->setProperty("OutputWorkspace", "__NotUsed__");
+  alg->execute();
+  outWS = alg->getProperty("OutputWorkspace");
+
+  return outWS;
+}
+
+void MuonPairingAsymmetry::setPairAsymmetrySampleLogs(
+    MatrixWorkspace_sptr workspace) {
+  MuonAlgorithmHelper::addSampleLog(workspace, "analysis_pairName",
+                                    getProperty("PairName"));
+  MuonAlgorithmHelper::addSampleLog(workspace, "analysis_alpha",
+                                    getProperty("Alpha"));
+  MuonAlgorithmHelper::addSampleLog(workspace, "analysis_group1",
+                                    getProperty("Group1"));
+  MuonAlgorithmHelper::addSampleLog(workspace, "analysis_group2",
+                                    getProperty("Group2"));
+  MuonAlgorithmHelper::addSampleLog(workspace, "analysis_periods_summed",
+                                    getPropertyValue("SummedPeriods"));
+  MuonAlgorithmHelper::addSampleLog(workspace, "analysis_periods_subtracted",
+                                    getPropertyValue("SubtractedPeriods"));
+}
+
+MatrixWorkspace_sptr
+MuonPairingAsymmetry::appendSpectra(MatrixWorkspace_sptr inputWS1,
+                                    MatrixWorkspace_sptr inputWS2) {
+
+  IAlgorithm_sptr alg = this->createChildAlgorithm("AppendSpectra");
+  alg->setProperty("InputWorkspace1", inputWS1);
+  alg->setProperty("InputWorkspace2", inputWS2);
+  alg->setProperty("ValidateInputs", true);
+  alg->execute();
+  MatrixWorkspace_sptr ws = alg->getProperty("OutputWorkspace");
+  return ws;
+}
+
+void MuonPairingAsymmetry::validatePeriods(
+    WorkspaceGroup_sptr inputWS, std::map<std::string, std::string> &errors) {
+  const std::vector<int> summedPeriods = getProperty("SummedPeriods");
+  const std::vector<int> subtractedPeriods = getProperty("SubtractedPeriods");
+  if (summedPeriods.empty() && subtractedPeriods.empty()) {
+    errors["SummedPeriods"] = "At least one period must be specified";
+  }
+
+  if (!summedPeriods.empty()) {
+    const int highestSummedPeriod =
+        *std::max_element(summedPeriods.begin(), summedPeriods.end());
+    if (!checkPeriodInWorkspaceGroup(highestSummedPeriod, inputWS)) {
+      errors["SummedPeriods"] = "Requested period (" +
+                                std::to_string(highestSummedPeriod) +
+                                ") exceeds periods in data";
+    }
+    if (std::any_of(summedPeriods.begin(), summedPeriods.end(),
+                    [](const int &i) { return i < 0; })) {
+      errors["SummedPeriods"] = "Requested periods must be greater that 0.";
+    }
+  }
+
+  if (!subtractedPeriods.empty()) {
+    const int highestSubtractedPeriod =
+        *std::max_element(subtractedPeriods.begin(), subtractedPeriods.end());
+    if (!checkPeriodInWorkspaceGroup(highestSubtractedPeriod, inputWS)) {
+      errors["SubtractedPeriods"] = "Requested period (" +
+                                    std::to_string(highestSubtractedPeriod) +
+                                    ") exceeds periods in data";
+    }
+    if (std::any_of(subtractedPeriods.begin(), subtractedPeriods.end(),
+                    [](const int &i) { return i < 0; })) {
+      errors["SubtractedPeriods"] = "Requested periods must be greater that 0.";
+    }
+  }
+}
+
+} // namespace Muon
+} // namespace Mantid
diff --git a/Framework/Muon/test/MuonGroupingAsymmetryTest.h b/Framework/Muon/test/MuonGroupingAsymmetryTest.h
new file mode 100644
index 0000000000000000000000000000000000000000..2d6c83b1117ed62b583ab77f4d43ba3377d1033e
--- /dev/null
+++ b/Framework/Muon/test/MuonGroupingAsymmetryTest.h
@@ -0,0 +1,373 @@
+// Mantid Repository : https://github.com/mantidproject/mantid
+//
+// Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
+//     NScD Oak Ridge National Laboratory, European Spallation Source
+//     & Institut Laue - Langevin
+// SPDX - License - Identifier: GPL - 3.0 +
+#ifndef MANTID_MUON_MUONGROUPINGASYMMETRYTEST_H_
+#define MANTID_MUON_MUONGROUPINGASYMMETRYTEST_H_
+
+#include <cxxtest/TestSuite.h>
+#include <string>
+
+#include "MantidAPI/AlgorithmManager.h"
+#include "MantidAPI/FrameworkManager.h"
+#include "MantidMuon/MuonGroupingAsymmetry.h"
+#include "MantidTestHelpers/MuonWorkspaceCreationHelper.h"
+
+using namespace Mantid;
+using namespace Mantid::Kernel;
+using namespace Mantid::API;
+using namespace Mantid::DataObjects;
+using namespace Mantid::Muon;
+using namespace MuonWorkspaceCreationHelper;
+
+namespace {
+
+// Simple class to set up the ADS with the configuration required by the
+// algorithm (a MatrixWorkspace).
+class setUpADSWithWorkspace {
+public:
+  setUpADSWithWorkspace(Workspace_sptr ws) {
+    AnalysisDataService::Instance().addOrReplace(inputWSName, ws);
+  };
+  ~setUpADSWithWorkspace() { AnalysisDataService::Instance().clear(); };
+
+  const std::string inputWSName = "inputData";
+};
+
+// Set only mandatory fields; input and output workspace
+IAlgorithm_sptr
+algorithmWithWorkspacePropertiesSet(const std::string &inputWSName) {
+
+  auto alg = boost::make_shared<MuonGroupingAsymmetry>();
+  alg->initialize();
+  alg->setProperty("InputWorkspace", inputWSName);
+  alg->setProperty("OutputWorkspace", "__notUsed");
+  alg->setAlwaysStoreInADS(false);
+  alg->setLogging(false);
+  return alg;
+}
+
+// Set up algorithm without any optional properties
+// i.e. just the input workspace and group name.
+IAlgorithm_sptr
+setUpAlgorithmWithoutOptionalProperties(WorkspaceGroup_sptr ws,
+                                        const std::string &name,
+                                        const std::vector<int> &grouping) {
+  setUpADSWithWorkspace setup(ws);
+  IAlgorithm_sptr alg = algorithmWithWorkspacePropertiesSet(setup.inputWSName);
+  alg->setProperty("GroupName", name);
+  alg->setProperty("Grouping", grouping);
+  return alg;
+}
+
+// Retrieve the output workspace from an executed algorithm
+MatrixWorkspace_sptr getOutputWorkspace(IAlgorithm_sptr alg) {
+  MatrixWorkspace_sptr outputWS = alg->getProperty("OutputWorkspace");
+  return outputWS;
+}
+
+} // namespace
+
+class MuonGroupingAsymmetryTest : public CxxTest::TestSuite {
+public:
+  static MuonGroupingAsymmetryTest *createSuite() {
+    return new MuonGroupingAsymmetryTest();
+  }
+  static void destroySuite(MuonGroupingAsymmetryTest *suite) { delete suite; }
+
+  // --------------------------------------------------------------------------
+  // Initialization / Execution
+  // --------------------------------------------------------------------------
+
+  void test_algorithm_initializes() {
+    MuonGroupingAsymmetry alg;
+
+    TS_ASSERT_THROWS_NOTHING(alg.initialize());
+    TS_ASSERT(alg.isInitialized());
+  }
+
+  void test_algorithm_executes_with_default_arguments() {
+    const std::vector<int> group = {1};
+    auto ws = createMultiPeriodWorkspaceGroup(1, 5, 10, "asym");
+    auto alg = setUpAlgorithmWithoutOptionalProperties(ws, "asym", group);
+
+    TS_ASSERT_THROWS_NOTHING(alg->execute());
+    TS_ASSERT(alg->isExecuted())
+  }
+
+  // --------------------------------------------------------------------------
+  // Validation : InputWorkspace and GroupName
+  // --------------------------------------------------------------------------
+
+  void test_that_input_workspace_cannot_be_a_Workspace2D() {
+
+    auto ws = createCountsWorkspace(5, 10, 0.0);
+    setUpADSWithWorkspace setup(ws);
+    auto alg = boost::make_shared<MuonGroupingAsymmetry>();
+    alg->initialize();
+
+    TS_ASSERT_THROWS_ANYTHING(
+        alg->setProperty("InputWorkspace", setup.inputWSName));
+  }
+
+  void test_that_input_workspace_can_be_a_WorkspaceGroup() {
+
+    auto ws = createMultiPeriodWorkspaceGroup(2, 1, 10, "group1");
+    setUpADSWithWorkspace setup(ws);
+    auto alg = boost::make_shared<MuonGroupingAsymmetry>();
+    alg->initialize();
+
+    TS_ASSERT_THROWS_NOTHING(
+        alg->setProperty("InputWorkspace", setup.inputWSName))
+  }
+
+  void test_that_group_name_must_be_supplied() {
+
+    auto ws = createMultiPeriodWorkspaceGroup(2, 1, 10, "group1");
+    setUpADSWithWorkspace setup(ws);
+    IAlgorithm_sptr alg =
+        algorithmWithWorkspacePropertiesSet(setup.inputWSName);
+
+    TS_ASSERT_THROWS(alg->execute(), std::runtime_error);
+  }
+
+  void
+  test_that_group_names_with_alphanumeric_characters_or_underscores_are_allowed() {
+    const std::vector<int> group = {1};
+    auto ws = createMultiPeriodWorkspaceGroup(1, 1, 10, "group1");
+
+    const std::vector<std::string> validNames = {"fwd", "fwd2", "bwd_2"};
+    for (auto &&validName : validNames) {
+      auto alg = setUpAlgorithmWithoutOptionalProperties(ws, validName, group);
+      TS_ASSERT_THROWS_NOTHING(alg->execute());
+    }
+  }
+
+  void
+  test_that_exec_throws_if_group_name_is_not_alphanumeric_or_underscored() {
+    const std::vector<int> group = {1};
+    auto ws = createMultiPeriodWorkspaceGroup(1, 1, 10, "group1");
+
+    const std::vector<std::string> invalidNames = {"@", "fwd!", "#1", "fwd @",
+                                                   "   "};
+    for (auto &&invalidName : invalidNames) {
+      auto alg =
+          setUpAlgorithmWithoutOptionalProperties(ws, invalidName, group);
+      TS_ASSERT_THROWS_ANYTHING(alg->execute());
+    }
+  }
+
+  // --------------------------------------------------------------------------
+  // Validation : Grouping
+  // --------------------------------------------------------------------------
+
+  void
+  test_that_cannot_add_spectra_to_group_which_exceed_those_in_the_workspace() {
+    auto ws = createMultiPeriodWorkspaceGroup(1, 5, 10, "asym");
+
+    const std::vector<int> detectors = {6, 7, 8, 9, 10};
+    auto alg = setUpAlgorithmWithoutOptionalProperties(ws, "asym", detectors);
+
+    alg->execute();
+    TS_ASSERT(!alg->isExecuted());
+  }
+
+  // --------------------------------------------------------------------------
+  // Validation : multi period data
+  // --------------------------------------------------------------------------
+
+  void test_that_at_least_one_period_must_be_specified() {
+    auto ws = createMultiPeriodWorkspaceGroup(2, 3, 10, "group");
+    const std::vector<int> detectors = {1, 2};
+    auto alg = setUpAlgorithmWithoutOptionalProperties(ws, "group", detectors);
+
+    const std::vector<int> summedPeriods = {};
+    const std::vector<int> subtractedPeriods = {};
+    alg->setProperty("SummedPeriods", summedPeriods);
+    alg->setProperty("SubtractedPeriods", subtractedPeriods);
+
+    TS_ASSERT_THROWS(alg->execute(), std::runtime_error);
+  }
+
+  void
+  test_that_supplying_too_many_periods_to_SummedPeriods_throws_on_execute() {
+    auto ws = createMultiPeriodWorkspaceGroup(2, 3, 10, "group");
+    const std::vector<int> detectors = {1, 2, 3};
+    auto alg = setUpAlgorithmWithoutOptionalProperties(ws, "group", detectors);
+
+    const std::vector<int> summedPeriods = {3};
+    alg->setProperty("SummedPeriods", summedPeriods);
+
+    TS_ASSERT_THROWS(alg->execute(), std::runtime_error);
+  }
+
+  void
+  test_that_supplying_too_many_periods_to_SubtractedPeriods_throws_on_execute() {
+    auto ws = createMultiPeriodWorkspaceGroup(2, 3, 10, "group");
+    const std::vector<int> detectors = {1, 2, 3};
+    auto alg = setUpAlgorithmWithoutOptionalProperties(ws, "group", detectors);
+
+    const std::vector<int> subtractedPeriods = {3};
+    alg->setProperty("SubtractedPeriods", subtractedPeriods);
+
+    TS_ASSERT_THROWS(alg->execute(), std::runtime_error);
+  }
+
+  void test_algorithm_fails_if_summed_periods_has_negative_entry() {
+    auto ws = createMultiPeriodWorkspaceGroup(2, 3, 10, "group");
+    const std::vector<int> detectors = {1, 2, 3};
+    auto alg = setUpAlgorithmWithoutOptionalProperties(ws, "group", detectors);
+
+    const std::vector<int> summedPeriods = {-1};
+    alg->setProperty("SummedPeriods", summedPeriods);
+
+    TS_ASSERT_THROWS(alg->execute(), std::runtime_error);
+  }
+
+  void test_algorithm_fails_if_subtracted_periods_has_negative_entry() {
+    auto ws = createMultiPeriodWorkspaceGroup(2, 3, 10, "group");
+    const std::vector<int> detectors = {1, 2, 3};
+    auto alg = setUpAlgorithmWithoutOptionalProperties(ws, "group", detectors);
+
+    const std::vector<int> subtractedPeriods = {-1};
+    alg->setProperty("SubtractedPeriods", subtractedPeriods);
+
+    TS_ASSERT_THROWS(alg->execute(), std::runtime_error);
+  }
+
+  // --------------------------------------------------------------------------
+  // Correct output
+  // --------------------------------------------------------------------------
+
+  void
+  test_grouping_with_single_detector_and_asymmetry_analysis_gives_correct_values() {
+
+    auto ws = createMultiPeriodAsymmetryData(1, 3, 10, "group_asym");
+    const std::vector<int> detectors = {1};
+    auto alg =
+        setUpAlgorithmWithoutOptionalProperties(ws, "group_asym", detectors);
+    alg->execute();
+
+    auto wsOut = getOutputWorkspace(alg);
+
+    TS_ASSERT_DELTA(wsOut->readX(0)[0], 0.000, 0.001);
+    TS_ASSERT_DELTA(wsOut->readX(0)[4], 0.400, 0.001);
+    TS_ASSERT_DELTA(wsOut->readX(0)[9], 0.900, 0.001);
+
+    TS_ASSERT_DELTA(wsOut->readY(0)[0], 2.18243, 0.001);
+    TS_ASSERT_DELTA(wsOut->readY(0)[1], 1.68932, 0.001);
+
+    TS_ASSERT_DELTA(wsOut->readE(0)[0], 0.0002906, 0.00001);
+    TS_ASSERT_DELTA(wsOut->readE(0)[1], 0.0003041, 0.00001);
+  }
+
+  void
+  test_grouping_with_multiple_detectors_and_asymmetry_analysis_gives_correct_values() {
+
+    auto ws = createMultiPeriodAsymmetryData(1, 2, 10, "group_asym");
+    const std::vector<int> detectors = {1, 2};
+    auto alg =
+        setUpAlgorithmWithoutOptionalProperties(ws, "group_asym", detectors);
+    alg->execute();
+
+    auto wsOut = getOutputWorkspace(alg);
+
+    TS_ASSERT_DELTA(wsOut->readX(0)[0], 0.000, 0.001);
+    TS_ASSERT_DELTA(wsOut->readX(0)[4], 0.400, 0.001);
+    TS_ASSERT_DELTA(wsOut->readX(0)[9], 0.900, 0.001);
+
+    TS_ASSERT_DELTA(wsOut->readY(0)[0], 2.2751, 0.001);
+    TS_ASSERT_DELTA(wsOut->readY(0)[1], 1.7005, 0.001);
+
+    TS_ASSERT_DELTA(wsOut->readE(0)[0], 0.0001418, 0.00001);
+    TS_ASSERT_DELTA(wsOut->readE(0)[1], 0.0001418, 0.00001);
+  }
+
+  void
+  test_grouping_asymmetry_with_subtracted_multiple_periods_gives_correct_values() {
+
+    auto ws = createMultiPeriodAsymmetryData(3, 2, 10, "group_asym");
+    std::vector<int> detectors = {1, 2};
+    auto alg =
+        setUpAlgorithmWithoutOptionalProperties(ws, "group_asym", detectors);
+
+    const std::vector<int> summedPeriods = {1};
+    const std::vector<int> subtractedPeriods = {2, 3};
+    alg->setProperty("SummedPeriods", summedPeriods);
+    alg->setProperty("SubtractedPeriods", subtractedPeriods);
+    alg->execute();
+
+    auto wsOut = getOutputWorkspace(alg);
+
+    TS_ASSERT_DELTA(wsOut->readX(0)[0], 0.000, 0.001);
+    TS_ASSERT_DELTA(wsOut->readX(0)[4], 0.400, 0.001);
+    TS_ASSERT_DELTA(wsOut->readX(0)[9], 0.900, 0.001);
+
+    TS_ASSERT_DELTA(wsOut->readY(0)[0], -0.29901, 0.001);
+    TS_ASSERT_DELTA(wsOut->readY(0)[1], 0.06680, 0.001);
+
+    TS_ASSERT_DELTA(wsOut->readE(0)[0], 0.0001497, 0.00001);
+    TS_ASSERT_DELTA(wsOut->readE(0)[1], 0.0001567, 0.00001);
+  }
+
+  void
+  test_grouping_asymmetry_with_summed_multiple_periods_gives_correct_values() {
+    auto ws = createMultiPeriodAsymmetryData(3, 2, 10, "group_asym");
+    std::vector<int> detectors = {1, 2};
+    auto alg =
+        setUpAlgorithmWithoutOptionalProperties(ws, "group_asym", detectors);
+
+    const std::vector<int> summedPeriods = {3, 2};
+    const std::vector<int> subtractedPeriods = {1};
+    alg->setProperty("SummedPeriods", summedPeriods);
+    alg->setProperty("SubtractedPeriods", subtractedPeriods);
+    alg->execute();
+
+    auto wsOut = getOutputWorkspace(alg);
+
+    TS_ASSERT_DELTA(wsOut->readX(0)[0], 0.000, 0.001);
+    TS_ASSERT_DELTA(wsOut->readX(0)[4], 0.400, 0.001);
+    TS_ASSERT_DELTA(wsOut->readX(0)[9], 0.900, 0.001);
+
+    TS_ASSERT_DELTA(wsOut->readY(0)[0], 0.29901, 0.001);
+    TS_ASSERT_DELTA(wsOut->readY(0)[1], -0.06680, 0.001);
+
+    TS_ASSERT_DELTA(wsOut->readE(0)[0], 0.0001497, 0.00001);
+    TS_ASSERT_DELTA(wsOut->readE(0)[1], 0.0001567, 0.00001);
+  }
+
+  void
+  test_grouping_asymmetry_with_specified_normalization_gives_correct_values() {
+    auto ws = createMultiPeriodAsymmetryData(3, 2, 10, "group_asym");
+    std::vector<int> detectors = {1, 2};
+    auto alg =
+        setUpAlgorithmWithoutOptionalProperties(ws, "group_asym", detectors);
+
+    const std::vector<int> summedPeriods = {3, 2};
+    const std::vector<int> subtractedPeriods = {1};
+    alg->setProperty("SummedPeriods", summedPeriods);
+    alg->setProperty("SubtractedPeriods", subtractedPeriods);
+    alg->setProperty("NormalizationIn", 15.0);
+    alg->execute();
+
+    auto wsOut = getOutputWorkspace(alg);
+
+    TS_ASSERT_DELTA(wsOut->readX(0)[0], 0.000, 0.001);
+    TS_ASSERT_DELTA(wsOut->readX(0)[4], 0.400, 0.001);
+    TS_ASSERT_DELTA(wsOut->readX(0)[9], 0.900, 0.001);
+
+    TS_ASSERT_DELTA(wsOut->readY(0)[0], 1.39055, 0.001);
+    TS_ASSERT_DELTA(wsOut->readY(0)[1], 0.92922, 0.001);
+
+    TS_ASSERT_DELTA(wsOut->readE(0)[0], 0.0000577, 0.00001);
+    TS_ASSERT_DELTA(wsOut->readE(0)[1], 0.0000604, 0.00001);
+
+    std::string norm = wsOut->getLog("analysis_asymmetry_norm")->value();
+
+    TS_ASSERT_EQUALS(std::stod(norm), 15.0);
+  }
+};
+
+#endif /* MANTID_MUON_MUONGROUPINGASYMMETRYTEST_H_ */
diff --git a/Framework/Muon/test/MuonPairingAsymmetryTest.h b/Framework/Muon/test/MuonPairingAsymmetryTest.h
new file mode 100644
index 0000000000000000000000000000000000000000..874dcbd199dfc9fe6d3c6eb163603163bf90fb65
--- /dev/null
+++ b/Framework/Muon/test/MuonPairingAsymmetryTest.h
@@ -0,0 +1,517 @@
+// Mantid Repository : https://github.com/mantidproject/mantid
+//
+// Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
+//     NScD Oak Ridge National Laboratory, European Spallation Source
+//     & Institut Laue - Langevin
+// SPDX - License - Identifier: GPL - 3.0 +
+#ifndef MANTID_MUON_MUONPAIRINGASYMMETRYTEST_H_
+#define MANTID_MUON_MUONPAIRINGASYMMETRYTEST_H_
+
+#include "MantidAPI/AlgorithmManager.h"
+#include "MantidAPI/FrameworkManager.h"
+#include "MantidMuon/MuonGroupingCounts.h"
+#include "MantidMuon/MuonPairingAsymmetry.h"
+#include "MantidTestHelpers/MuonWorkspaceCreationHelper.h"
+
+#include <cxxtest/TestSuite.h>
+
+using namespace Mantid;
+using namespace Mantid::Kernel;
+using namespace Mantid::API;
+using namespace Mantid::DataObjects;
+using namespace Mantid::Muon;
+using namespace MuonWorkspaceCreationHelper;
+
+namespace {
+
+// Simple class to set up the ADS with the configuration required by the
+// algorithm (a MatrixWorkspace).
+class setUpADSWithWorkspace {
+public:
+  setUpADSWithWorkspace(Workspace_sptr ws) {
+    AnalysisDataService::Instance().addOrReplace(inputWSName, ws);
+  };
+  ~setUpADSWithWorkspace() { AnalysisDataService::Instance().clear(); };
+
+  std::string const inputWSName = "inputData";
+};
+
+// Set only mandatory fields
+IAlgorithm_sptr algorithmWithoutOptionalPropertiesSet(
+    const std::string &inputWSName, const std::string &pairName,
+    const std::vector<int> &group1, const std::vector<int> &group2) {
+
+  auto alg = boost::make_shared<MuonPairingAsymmetry>();
+  alg->initialize();
+  alg->setProperty("SpecifyGroupsManually", true);
+  alg->setProperty("OutputWorkspace", "__notUsed");
+  alg->setProperty("InputWorkspace", inputWSName);
+  alg->setProperty("PairName", pairName);
+  alg->setProperty("Group1", group1);
+  alg->setProperty("Group2", group2);
+  alg->setAlwaysStoreInADS(false);
+  alg->setLogging(false);
+  return alg;
+}
+
+// Set up algorithm without any optional properties.
+IAlgorithm_sptr
+setUpAlgorithmWithoutOptionalProperties(WorkspaceGroup_sptr ws,
+                                        const std::string &name) {
+  const std::vector<int> group1 = {1, 2};
+  const std::vector<int> group2 = {3, 4};
+
+  setUpADSWithWorkspace setup(ws);
+  IAlgorithm_sptr alg = algorithmWithoutOptionalPropertiesSet(
+      setup.inputWSName, name, group1, group2);
+  return alg;
+}
+
+// Set up algorithm with groupings
+IAlgorithm_sptr setUpAlgorithmWithGroups(WorkspaceGroup_sptr ws,
+                                         const std::vector<int> &group1,
+                                         const std::vector<int> &group2) {
+  setUpADSWithWorkspace setup(ws);
+  IAlgorithm_sptr alg = algorithmWithoutOptionalPropertiesSet(
+      setup.inputWSName, "pair1", group1, group2);
+  return alg;
+}
+
+// Set up algorithm to accept two group workspaces
+IAlgorithm_sptr
+setUpAlgorithmWithGroupWorkspaces(MatrixWorkspace_sptr groupedWS1,
+                                  MatrixWorkspace_sptr groupedWS2) {
+  auto alg = boost::make_shared<MuonPairingAsymmetry>();
+  alg->initialize();
+  alg->setProperty("SpecifyGroupsManually", false);
+  alg->setProperty("OutputWorkspace", "__notUsed");
+  alg->setProperty("InputWorkspace1", groupedWS1);
+  alg->setProperty("InputWorkspace2", groupedWS2);
+  alg->setProperty("PairName", "pair1");
+  alg->setAlwaysStoreInADS(false);
+  alg->setLogging(false);
+  return alg;
+}
+
+// Set up MuonPairingAsymmetry algorithm to accept two WorkspaceGroups
+IAlgorithm_sptr
+setUpAlgorithmWithGroupWorkspaceGroups(WorkspaceGroup_sptr groupedWS1,
+                                       WorkspaceGroup_sptr groupedWS2) {
+  auto alg = boost::make_shared<MuonPairingAsymmetry>();
+  alg->setRethrows(true);
+  alg->initialize();
+  alg->setProperty("SpecifyGroupsManually", false);
+  alg->setProperty("OutputWorkspace", "__notUsed");
+  alg->setProperty("InputWorkspace1", groupedWS1);
+  alg->setProperty("InputWorkspace2", groupedWS2);
+  alg->setProperty("PairName", "pair1");
+  alg->setAlwaysStoreInADS(false);
+  alg->setLogging(false);
+  return alg;
+}
+
+// Retrieve the output workspace from an executed algorithm
+MatrixWorkspace_sptr getOutputWorkspace(IAlgorithm_sptr alg) {
+  MatrixWorkspace_sptr outputWS = alg->getProperty("OutputWorkspace");
+  return outputWS;
+}
+
+MatrixWorkspace_sptr createGroupWorkspace(const std::string &groupName,
+                                          const std::vector<int> &grouping,
+                                          const int &nPeriods) {
+  auto ws = createMultiPeriodAsymmetryData(nPeriods, 4, 10, "group");
+  setUpADSWithWorkspace setup(ws);
+
+  auto alg = boost::make_shared<MuonGroupingCounts>();
+  alg->initialize();
+  alg->setProperty("OutputWorkspace", "__notUsed");
+  alg->setProperty("InputWorkspace", setup.inputWSName);
+  alg->setProperty("GroupName", groupName);
+  alg->setProperty("Grouping", grouping);
+  alg->setAlwaysStoreInADS(false);
+  alg->setLogging(false);
+
+  alg->execute();
+
+  Workspace_sptr outputWS = alg->getProperty("OutputWorkspace");
+  return boost::dynamic_pointer_cast<MatrixWorkspace>(outputWS);
+}
+
+WorkspaceGroup_sptr
+createMultiPeriodGroupedWorkspace(const std::string &groupName,
+                                  const std::vector<int> &grouping,
+                                  const int &nPeriods) {
+  auto ws = createMultiPeriodAsymmetryData(nPeriods, 4, 10, "group");
+
+  auto wsGroup = boost::make_shared<WorkspaceGroup>();
+
+  for (int i = 1; i < nPeriods + 1; i++) {
+    std::vector<int> periods = {i};
+    auto alg = boost::make_shared<MuonGroupingCounts>();
+    alg->initialize();
+    alg->setProperty("OutputWorkspace", "__notUsed");
+    alg->setProperty("InputWorkspace", ws);
+    alg->setProperty("GroupName", groupName);
+    alg->setProperty("Grouping", grouping);
+    alg->setProperty("SummedPeriods", periods);
+    alg->setAlwaysStoreInADS(false);
+    alg->setLogging(false);
+
+    alg->execute();
+
+    Workspace_sptr outputWS = alg->getProperty("OutputWorkspace");
+    wsGroup->addWorkspace(outputWS);
+  }
+
+  return wsGroup;
+}
+
+} // namespace
+
+class MuonPairingAsymmetryTest : public CxxTest::TestSuite {
+public:
+  static MuonPairingAsymmetryTest *createSuite() {
+    return new MuonPairingAsymmetryTest();
+  }
+  static void destroySuite(MuonPairingAsymmetryTest *suite) { delete suite; }
+
+  // --------------------------------------------------------------------------
+  // Initialization / Execution
+  // --------------------------------------------------------------------------
+
+  void test_that_algorithm_initializes() {
+    MuonPairingAsymmetry alg;
+
+    TS_ASSERT_THROWS_NOTHING(alg.initialize());
+    TS_ASSERT(alg.isInitialized());
+  }
+
+  void test_that_algorithm_executes_with_no_optional_properties_set() {
+
+    auto ws = createMultiPeriodWorkspaceGroup(1, 6, 10, "pair1");
+    auto alg = setUpAlgorithmWithoutOptionalProperties(ws, "pair1");
+
+    TS_ASSERT_THROWS_NOTHING(alg->execute());
+    TS_ASSERT(alg->isExecuted())
+  }
+
+  // --------------------------------------------------------------------------
+  // Validation : Pair Name
+  // --------------------------------------------------------------------------
+
+  void test_that_input_workspace_cannot_be_a_Workspace2D() {
+
+    auto ws = createCountsWorkspace(5, 10, 0.0);
+    setUpADSWithWorkspace setup(ws);
+    auto alg = boost::make_shared<MuonPairingAsymmetry>();
+    alg->initialize();
+
+    TS_ASSERT_THROWS_ANYTHING(
+        alg->setProperty("InputWorkspace", setup.inputWSName));
+  }
+
+  void test_that_input_workspace_can_be_a_WorkspaceGroup() {
+
+    auto ws = createMultiPeriodWorkspaceGroup(1, 6, 10, "group1");
+    setUpADSWithWorkspace setup(ws);
+    auto alg = boost::make_shared<MuonPairingAsymmetry>();
+    alg->initialize();
+
+    TS_ASSERT_THROWS_NOTHING(
+        alg->setProperty("InputWorkspace", setup.inputWSName))
+  }
+
+  void test_that_non_empty_pair_name_must_be_supplied() {
+    const std::vector<int> group1 = {1, 2};
+    const std::vector<int> group2 = {3, 4};
+
+    auto ws = createMultiPeriodWorkspaceGroup(2, 1, 10, "pair1");
+    setUpADSWithWorkspace setup(ws);
+    IAlgorithm_sptr alg = algorithmWithoutOptionalPropertiesSet(
+        setup.inputWSName, "", group1, group2);
+
+    TS_ASSERT_THROWS(alg->execute(), std::runtime_error);
+  }
+
+  void
+  test_that_pair_names_with_alphanumeric_characters_or_underscores_are_allowed() {
+    const std::vector<int> group1 = {1, 2};
+    const std::vector<int> group2 = {3, 4};
+    auto ws = createMultiPeriodWorkspaceGroup(2, 1, 10, "pairWS");
+
+    std::vector<std::string> validNames = {"fwd", "fwd2", "bwd_2"};
+    for (auto &&validName : validNames) {
+      auto alg = algorithmWithoutOptionalPropertiesSet("pairWS", validName,
+                                                       group1, group2);
+      TS_ASSERT_THROWS_NOTHING(alg->execute());
+    }
+  }
+
+  void test_that_exec_throws_if_pair_name_is_not_alphanumeric_or_underscored() {
+    const std::vector<int> group1 = {1, 2};
+    const std::vector<int> group2 = {3, 4};
+    auto ws = createMultiPeriodWorkspaceGroup(2, 1, 10, "pair1");
+
+    std::vector<std::string> invalidNames = {"@", "fwd!", "#1", "fwd @", "   "};
+    for (auto &&invalidName : invalidNames) {
+      auto alg = algorithmWithoutOptionalPropertiesSet("pairWS", invalidName,
+                                                       group1, group2);
+      TS_ASSERT_THROWS_ANYTHING(alg->execute());
+    }
+  }
+
+  // --------------------------------------------------------------------------
+  // Validation : Alpha
+  // --------------------------------------------------------------------------
+
+  void test_that_exec_throws_if_alpha_is_negative() {
+    const std::vector<int> group1 = {1, 2};
+    const std::vector<int> group2 = {3, 4};
+    auto ws = createMultiPeriodWorkspaceGroup(2, 1, 10, "pairWS");
+    auto alg =
+        algorithmWithoutOptionalPropertiesSet("pairWS", "pair", group1, group2);
+
+    alg->setProperty("Alpha", -0.1);
+
+    TS_ASSERT_THROWS_ANYTHING(alg->execute());
+  }
+
+  // --------------------------------------------------------------------------
+  // Validation : Grouping
+  // --------------------------------------------------------------------------
+
+  void test_that_two_groupings_must_be_supplied() {
+
+    auto ws = createMultiPeriodWorkspaceGroup(1, 5, 10, "pair1");
+    auto alg = setUpAlgorithmWithoutOptionalProperties(ws, "pair1");
+
+    std::vector<int> group1 = {};
+    std::vector<int> group2 = {};
+    alg->setProperty("Group1", group1);
+    alg->setProperty("Group2", group2);
+
+    TS_ASSERT_THROWS_ANYTHING(alg->execute());
+  }
+
+  void test_that_two_different_groupings_must_be_supplied() {
+
+    auto ws = createMultiPeriodWorkspaceGroup(1, 5, 10, "pair1");
+    auto alg = setUpAlgorithmWithoutOptionalProperties(ws, "pair1");
+
+    std::vector<int> group1 = {1, 2, 3};
+    std::vector<int> group2 = {1, 2, 3};
+    alg->setProperty("Group1", group1);
+    alg->setProperty("Group2", group2);
+
+    TS_ASSERT_THROWS_ANYTHING(alg->execute());
+  }
+
+  // --------------------------------------------------------------------------
+  // Validation : Multi period data
+  // --------------------------------------------------------------------------
+
+  void test_that_at_least_one_period_must_be_specified() {
+    auto ws = createMultiPeriodWorkspaceGroup(2, 3, 10, "group");
+    const std::vector<int> detectors = {1, 2};
+    auto alg = setUpAlgorithmWithoutOptionalProperties(ws, "pair1");
+
+    std::vector<int> summedPeriods = {};
+    std::vector<int> subtractedPeriods = {};
+    alg->setProperty("SummedPeriods", summedPeriods);
+    alg->setProperty("SubtractedPeriods", subtractedPeriods);
+
+    TS_ASSERT_THROWS(alg->execute(), std::runtime_error);
+  }
+
+  void
+  test_that_supplying_too_many_periods_to_SummedPeriods_throws_on_execute() {
+    auto ws = createMultiPeriodWorkspaceGroup(2, 3, 10, "group");
+    auto alg = setUpAlgorithmWithoutOptionalProperties(ws, "pair1");
+
+    std::vector<int> summedPeriods = {3};
+    alg->setProperty("SummedPeriods", summedPeriods);
+
+    TS_ASSERT_THROWS(alg->execute(), std::runtime_error);
+  }
+
+  void
+  test_that_supplying_too_many_periods_to_SubtractedPeriods_throws_on_execute() {
+    auto ws = createMultiPeriodWorkspaceGroup(2, 3, 10, "group");
+    auto alg = setUpAlgorithmWithoutOptionalProperties(ws, "pair1");
+
+    std::vector<int> subtractedPeriods = {3};
+    alg->setProperty("SubtractedPeriods", subtractedPeriods);
+
+    TS_ASSERT_THROWS(alg->execute(), std::runtime_error);
+  }
+
+  // --------------------------------------------------------------------------
+  // Correct Output : Single Period
+  // --------------------------------------------------------------------------
+
+  void
+  test_that_single_period_data_combines_detectors_correctly_for_manually_specified_detectors() {
+    // 4 spectra per period, 10 bins
+    auto ws = createMultiPeriodAsymmetryData(1, 4, 10, "pairWS");
+    const std::vector<int> group1 = {1, 2};
+    const std::vector<int> group2 = {3, 4};
+
+    auto alg = setUpAlgorithmWithGroups(ws, group1, group2);
+    alg->execute();
+    auto wsOut = getOutputWorkspace(alg);
+
+    TS_ASSERT_DELTA(wsOut->readX(0)[0], 0.050, 0.001);
+    TS_ASSERT_DELTA(wsOut->readX(0)[4], 0.450, 0.001);
+    TS_ASSERT_DELTA(wsOut->readX(0)[9], 0.950, 0.001);
+
+    TS_ASSERT_DELTA(wsOut->readY(0)[0], -0.3889, 0.001);
+    TS_ASSERT_DELTA(wsOut->readY(0)[4], 0.000, 0.001);
+    TS_ASSERT_DELTA(wsOut->readY(0)[9], -0.8211, 0.001);
+
+    TS_ASSERT_DELTA(wsOut->readE(0)[0], 0.04641, 0.0001);
+    TS_ASSERT_DELTA(wsOut->readE(0)[4], 1.00000, 0.0001);
+    TS_ASSERT_DELTA(wsOut->readE(0)[9], 0.19818, 0.0001);
+  }
+
+  void
+  test_that_single_period_data_combines_detectors_correctly_for_two_group_workspaces() {
+
+    const std::vector<int> group1 = {1, 2};
+    const std::vector<int> group2 = {3, 4};
+    auto ws1 = createGroupWorkspace("fwd", group1, 1);
+    auto ws2 = createGroupWorkspace("bwd", group2, 1);
+
+    auto alg = setUpAlgorithmWithGroupWorkspaces(ws1, ws2);
+    alg->execute();
+
+    auto wsOut = getOutputWorkspace(alg);
+
+    TS_ASSERT_DELTA(wsOut->readX(0)[0], 0.05, 0.001);
+    TS_ASSERT_DELTA(wsOut->readX(0)[4], 0.45, 0.001);
+    TS_ASSERT_DELTA(wsOut->readX(0)[9], 0.95, 0.001);
+
+    TS_ASSERT_DELTA(wsOut->readY(0)[0], -0.3889, 0.001);
+    TS_ASSERT_DELTA(wsOut->readY(0)[4], 0.000, 0.001);
+    TS_ASSERT_DELTA(wsOut->readY(0)[9], -0.8211, 0.001);
+
+    TS_ASSERT_DELTA(wsOut->readE(0)[0], 0.04641, 0.0001);
+    TS_ASSERT_DELTA(wsOut->readE(0)[4], 1.00000, 0.0001);
+    TS_ASSERT_DELTA(wsOut->readE(0)[9], 0.19818, 0.0001);
+  }
+
+  // //
+  // --------------------------------------------------------------------------
+  // // Correct Output : Multi Period
+  // //
+  // --------------------------------------------------------------------------
+
+  void
+  test_that_multi_period_data_combines_detectors_correctly_for_manually_specified_detectors_and_summed_periods() {
+
+    auto ws = createMultiPeriodAsymmetryData(2, 4, 10, "pairWS");
+    const std::vector<int> group1 = {1, 2};
+    const std::vector<int> group2 = {3, 4};
+    auto alg = setUpAlgorithmWithGroups(ws, group1, group2);
+    alg->setProperty("SummedPeriods", group1);
+    alg->execute();
+
+    auto wsOut = getOutputWorkspace(alg);
+
+    TS_ASSERT_DELTA(wsOut->readX(0)[0], 0.050, 0.001);
+    TS_ASSERT_DELTA(wsOut->readX(0)[4], 0.450, 0.001);
+    TS_ASSERT_DELTA(wsOut->readX(0)[9], 0.950, 0.001);
+
+    TS_ASSERT_DELTA(wsOut->readY(0)[0], -0.38484955, 0.001);
+    TS_ASSERT_DELTA(wsOut->readY(0)[4], 0.000, 0.001);
+    TS_ASSERT_DELTA(wsOut->readY(0)[9], -0.74269249, 0.001);
+
+    TS_ASSERT_DELTA(wsOut->readE(0)[0], 0.02743, 0.0001);
+    TS_ASSERT_DELTA(wsOut->readE(0)[4], 1.0000, 0.0001);
+    TS_ASSERT_DELTA(wsOut->readE(0)[9], 0.098512, 0.0001);
+  }
+
+  void
+  test_that_multi_period_data_combines_detectors_correctly_for_manually_specified_detectors_and_subtracted_periods() {
+
+    auto ws = createMultiPeriodAsymmetryData(2, 4, 10, "pairWS");
+    const std::vector<int> group1 = {1, 2};
+    const std::vector<int> group2 = {3, 4};
+    auto alg = setUpAlgorithmWithGroups(ws, group1, group2);
+    const std::vector<int> summedPeriods = {1};
+    const std::vector<int> subtractedPeriods = {2};
+    alg->setProperty("SummedPeriods", summedPeriods);
+    alg->setProperty("SubtractedPeriods", subtractedPeriods);
+    alg->execute();
+
+    auto wsOut = getOutputWorkspace(alg);
+
+    TS_ASSERT_DELTA(wsOut->readX(0)[0], 0.050, 0.001);
+    TS_ASSERT_DELTA(wsOut->readX(0)[4], 0.450, 0.001);
+    TS_ASSERT_DELTA(wsOut->readX(0)[9], 0.950, 0.001);
+
+    TS_ASSERT_DELTA(wsOut->readY(0)[0], -0.00630986, 0.001);
+    TS_ASSERT_DELTA(wsOut->readY(0)[4], 0.000, 0.001);
+    TS_ASSERT_DELTA(wsOut->readY(0)[9], -0.10690094, 0.001);
+
+    TS_ASSERT_DELTA(wsOut->readE(0)[0], 0.05754263, 0.0001);
+    TS_ASSERT_DELTA(wsOut->readE(0)[9], 0.2283730, 0.0001);
+  }
+
+  void
+  test_that_multi_period_data_combines_detectors_correctly_for_manually_specified_detectors_summed_and_subtracted_periods() {
+
+    auto ws = createMultiPeriodAsymmetryData(3, 4, 10, "pairWS");
+    const std::vector<int> group1 = {1, 2};
+    const std::vector<int> group2 = {3, 4};
+    auto alg = setUpAlgorithmWithGroups(ws, group1, group2);
+    const std::vector<int> summedPeriods = {1, 2};
+    const std::vector<int> subtractedPeriods = {3};
+    alg->setProperty("SummedPeriods", summedPeriods);
+    alg->setProperty("SubtractedPeriods", subtractedPeriods);
+    alg->execute();
+
+    auto wsOut = getOutputWorkspace(alg);
+
+    TS_ASSERT_DELTA(wsOut->readX(0)[0], 0.050, 0.001);
+    TS_ASSERT_DELTA(wsOut->readX(0)[4], 0.450, 0.001);
+    TS_ASSERT_DELTA(wsOut->readX(0)[9], 0.950, 0.001);
+
+    TS_ASSERT_DELTA(wsOut->readY(0)[0], -0.00879057, 0.001);
+    TS_ASSERT_DELTA(wsOut->readY(0)[4], 0.0, 0.001);
+    TS_ASSERT_DELTA(wsOut->readY(0)[9], -0.130944, 0.001);
+
+    TS_ASSERT_DELTA(wsOut->readE(0)[0], 0.0395697, 0.0001);
+    TS_ASSERT_DELTA(wsOut->readE(0)[9], 0.122684, 0.0001);
+  }
+
+  void
+  test_that_multi_period_data_combines_detectors_correctly_for_group_workspaces_summed_and_subtracted_periods() {
+
+    const std::vector<int> group1 = {1, 2};
+    const std::vector<int> group2 = {3, 4};
+    auto ws1 = createMultiPeriodGroupedWorkspace("fwd", group1, 3);
+    auto ws2 = createMultiPeriodGroupedWorkspace("bwd", group2, 3);
+
+    auto alg = setUpAlgorithmWithGroupWorkspaceGroups(ws1, ws2);
+    const std::vector<int> summedPeriods = {1, 2};
+    const std::vector<int> subtractedPeriods = {3};
+    alg->setProperty("SummedPeriods", summedPeriods);
+    alg->setProperty("SubtractedPeriods", subtractedPeriods);
+    alg->execute();
+
+    auto wsOut = getOutputWorkspace(alg);
+
+    TS_ASSERT_DELTA(wsOut->readX(0)[0], 0.050, 0.001);
+    TS_ASSERT_DELTA(wsOut->readX(0)[4], 0.450, 0.001);
+    TS_ASSERT_DELTA(wsOut->readX(0)[9], 0.950, 0.001);
+
+    TS_ASSERT_DELTA(wsOut->readY(0)[0], -0.00879057, 0.001);
+    TS_ASSERT_DELTA(wsOut->readY(0)[4], 0.0, 0.001);
+    TS_ASSERT_DELTA(wsOut->readY(0)[9], -0.130944, 0.001);
+
+    TS_ASSERT_DELTA(wsOut->readE(0)[0], 0.0395697, 0.0001);
+    TS_ASSERT_DELTA(wsOut->readE(0)[9], 0.122684, 0.0001);
+  }
+};
+
+#endif /* MANTID_MUON_MUONPAIRINGASYMMETRYTEST_H_ */
diff --git a/Framework/Properties/Mantid.properties.template b/Framework/Properties/Mantid.properties.template
index 36893c2f6af0bedfd0b10c412818a0e42924d328..5e3ede53bb217162e1c36d77408e91716b26de39 100644
--- a/Framework/Properties/Mantid.properties.template
+++ b/Framework/Properties/Mantid.properties.template
@@ -69,16 +69,9 @@ openclKernelFiles.directory = @MANTID_ROOT@/Framework/GPUAlgorithms/src
 # Where to load colormap files (.map)
 colormaps.directory = @COLORMAPS_FOLDER@
 
-# Location of Python scripts that are required for certain aspects of Mantid to function
-# correctly, i.e. customized interfaces
-#
-# WARNING: Altering this value will almost certainly break Mantid functionality
-#
-requiredpythonscript.directories = @REQUIREDSCRIPT_DIRS@
-
 # Additional directories for Python scripts. These are added to the Python path by the Python API.
 # This key is NOT recursive so sub-directories must be added in addition
-pythonscripts.directories = @PYTHONSCRIPT_DIRS@
+pythonscripts.directories =
 
 # Setting this to 1 will allow python algorithms to be reloaded before execution.
 pythonalgorithms.refresh.allowed = 0
diff --git a/Framework/PythonInterface/CMakeLists.txt b/Framework/PythonInterface/CMakeLists.txt
index 959fd20f4b05c09a3608187f64db10777981c223..ad78194c942f509f22abc61b319c91728d07a608 100644
--- a/Framework/PythonInterface/CMakeLists.txt
+++ b/Framework/PythonInterface/CMakeLists.txt
@@ -110,6 +110,7 @@ clean_orphaned_pyc_files ( ${CMAKE_CURRENT_SOURCE_DIR}/plugins )
 ###########################################################################
 # tests
 ###########################################################################
+set ( PYTHONINTERFACE_PLUGINS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/plugins )
 add_subdirectory( test )
 
 ###########################################################################
diff --git a/Framework/PythonInterface/mantid/__init__.py b/Framework/PythonInterface/mantid/__init__.py
index 727b87b9bcaf48e459ef8c044d195b8b6fdfe9f7..e7f52bfcb059f3fa5ac4581f04f58a63563b3614 100644
--- a/Framework/PythonInterface/mantid/__init__.py
+++ b/Framework/PythonInterface/mantid/__init__.py
@@ -25,33 +25,45 @@ algorithms and data objects that are:
 from __future__ import (absolute_import, division,
                         print_function)
 
-###############################################################################
-# Check the current Python version is correct
-###############################################################################
-from . import pyversion
+import os
+import site
+import sys
+
+from .pyversion import check_python_version
+check_python_version()
+
 
-###############################################################################
-# Define the api version
-###############################################################################
 def apiVersion():
     """Indicates that this is version 2
     of the API
     """
     return 2
 
-###############################################################################
-# GUI - Do this as early as possible
-###############################################################################
-# Flag indicating whether the GUI layer is loaded.
+
+# Bail out early if a Mantid.properties files is not found in the
+# parent directory - it indicates a broken installation or build.
+_moduledir = os.path.abspath(os.path.dirname(__file__))
+_bindir = os.path.dirname(_moduledir)
+if not os.path.exists(os.path.join(_bindir, 'Mantid.properties')):
+    raise ImportError("Unable to find Mantid.properties file next to this package - broken installation!")
+
+# Windows doesn"t have rpath settings so make sure the C-extensions can find the rest of the
+# mantid dlls. We assume they will be next to the properties file.
+if sys.platform == "win32":
+    os.environ["PATH"] = _bindir + ";" + os.environ.get("PATH", "")
+# Make sure the config service loads this properties file
+os.environ["MANTIDPATH"] = _bindir
+# Add directory as a site directory to process the .pth files
+site.addsitedir(_bindir)
+
 try:
+    # Flag indicating whether mantidplot layer is loaded.
     import _qti
     __gui__ = True
 except ImportError:
     __gui__ = False
 
-###############################################################################
 # Set deprecation warnings back to default (they are ignored in 2.7)
-###############################################################################
 import warnings as _warnings
 # Default we see everything
 _warnings.filterwarnings("default",category=DeprecationWarning,
@@ -62,112 +74,18 @@ _warnings.filterwarnings("ignore",category=DeprecationWarning,
                          module="numpy.oldnumeric")
 
 ###############################################################################
-# Try to be smarter when finding Mantid framework libraries
+# Load all non-plugin subpackages that contain a C-extension. The boost.python
+# registry will be missing entries if all are not loaded.
 ###############################################################################
-# Peek to see if a Mantid.properties file is in the parent directory,
-# if so assume that it is the required Mantid bin directory containing
-# the Mantid libraries and ignore any MANTIDPATH that has been set
-import os as _os
-_moduledir = _os.path.abspath(_os.path.dirname(__file__))
-_bindir = _os.path.dirname(_moduledir)
-if _os.path.exists(_os.path.join(_bindir, 'Mantid.properties')):
-    _os.environ['MANTIDPATH'] = _bindir
+from . import kernel as _kernel
+from . import api as _api
+from . import geometry as _geometry
+from . import dataobjects as _dataobjects
 
-###############################################################################
-# Ensure the sub package C libraries are loaded
-###############################################################################
-import contextlib as _context
-
-@_context.contextmanager
-def _shared_cextension():
-    """Our extensions need to shared symbols amongst them due to:
-      - the static boost python type registry
-      - static singleton instances marked as weak symbols by clang
-    gcc uses an extension to mark these attributes as globally unique
-    but clang marks them as weak and without RTLD_GLOBAL each shared
-    library has its own copy of each singleton.
-
-    See https://docs.python.org/3/library/sys.html#sys.setdlopenflags
-    """
-    import sys
-    if not sys.platform.startswith('linux'):
-        yield
-        return
-
-    import six
-    if six.PY2:
-        import DLFCN as dl
-    else:
-        import os as dl
-    flags_orig = sys.getdlopenflags()
-    sys.setdlopenflags(dl.RTLD_NOW | dl.RTLD_GLOBAL)
-    yield
-    sys.setdlopenflags(flags_orig)
-
-with _shared_cextension():
-    import mantid.kernel as kernel
-    import mantid.geometry as geometry
-    import mantid.api as api
-    import mantid.dataobjects as dataobjects
-    import mantid._plugins as _plugins
-    import mantid.plots as plots
-
-###############################################################################
-# Make the aliases from each module accessible in a the mantid namspace
-###############################################################################
-from mantid.kernel._aliases import *
-from mantid.api._aliases import *
+# Make the aliases from each module accessible in a the mantid namespace
+from .kernel._aliases import *
+from .api._aliases import *
 
-###############################################################################
 # Make the version string accessible in the standard way
-###############################################################################
-__version__ = kernel.version_str()
-
-###############################################################################
-# Load the Python plugins now everything has started.
-#
-# Before the plugins are loaded the simpleapi module is called to create
-# fake error-raising functions for all of the plugins. After the plugins have been
-# loaded the correction translation is applied to create the "real" simple
-# API functions.
-#
-# Although this seems odd it is necessary so that any PythonAlgorithm
-# can call any other PythonAlgorithm through the simple API mechanism. If left
-# to the simple import mechanism then plugins that are loaded later cannot
-# be seen by the earlier ones (chicken & the egg essentially).
-################################################################################
-from . import simpleapi as _simpleapi
-from mantid.kernel import plugins as _plugins
-from mantid.kernel.packagesetup import update_sys_paths as _update_sys_paths
-
-_plugins_key = 'python.plugins.directories'
-_user_key = 'user.%s' % _plugins_key
-plugin_dirs = _plugins.get_plugin_paths_as_set(_plugins_key)
-plugin_dirs.update(_plugins.get_plugin_paths_as_set(_user_key))
-_update_sys_paths(plugin_dirs, recursive=True)
-
-# Load
-plugin_files = []
-alg_files = []
-for directory in plugin_dirs:
-    try:
-        all_plugins, algs = _plugins.find_plugins(directory)
-        plugin_files += all_plugins
-        alg_files += algs
-    except ValueError as exc:
-        logger.warning('Exception encountered during plugin discovery: {0}'.format(str(exc)))
-        continue
-
-# Mockup the full API first so that any Python algorithm module has something to import
-_simpleapi._mockup(alg_files)
-# Load the plugins.
-plugin_modules = _plugins.load(plugin_files)
-# Create the proper algorithm definitions in the module
-new_attrs = _simpleapi._translate()
-# Finally, overwrite the mocked function definitions in the loaded modules with the real ones
-_plugins.sync_attrs(_simpleapi, new_attrs, plugin_modules)
-
-################################################################################
-
-from .fitfunctions import _attach_wrappers
-_attach_wrappers(_simpleapi)
+from .kernel import version_str as _version_str
+__version__ = _version_str()
diff --git a/Framework/PythonInterface/mantid/_plugins/CMakeLists.txt b/Framework/PythonInterface/mantid/_plugins/CMakeLists.txt
index 0b1fd0e1346bba88c238136742dbc0554895caaa..d75e5076b2f8770e5d87fa7c757d8aebf1a6553d 100644
--- a/Framework/PythonInterface/mantid/_plugins/CMakeLists.txt
+++ b/Framework/PythonInterface/mantid/_plugins/CMakeLists.txt
@@ -67,11 +67,12 @@ target_link_libraries ( PythonCurveFittingModule LINK_PRIVATE ${TCMALLOC_LIBRARI
 
 if (OSX_VERSION VERSION_GREATER 10.8)
   set_target_properties( PythonCurveFittingModule PROPERTIES
-                         INSTALL_RPATH "@loader_path/../../../MacOS;@loader_path/../kernel/;@loader_path/../geometry/;@loader_path/../api/")
+    INSTALL_RPATH "@loader_path/../../../MacOS;@loader_path/../../../../plugins;@loader_path/../kernel/;@loader_path/../geometry/;@loader_path/../api/")
 elseif ( ${CMAKE_SYSTEM_NAME} STREQUAL "Linux" )
   set_target_properties( PythonCurveFittingModule PROPERTIES
-                         INSTALL_RPATH "\$ORIGIN/../../../${LIB_DIR};\$ORIGIN/../kernel/;\$ORIGIN/../geometry/;\$ORIGIN/../api/")
+    INSTALL_RPATH "\$ORIGIN/../../../${LIB_DIR};\$ORIGIN/../../../${PLUGINS_DIR};\$ORIGIN/../kernel/;\$ORIGIN/../geometry/;\$ORIGIN/../api/")
 endif ()
+
 ###########################################################################
 # Installation settings
 ###########################################################################
diff --git a/Framework/PythonInterface/mantid/_plugins/__init__.py b/Framework/PythonInterface/mantid/_plugins/__init__.py
index 1227b4da9c95bd8fb3f5ee14be7565e0f54956ab..ac9e8c5039dffa843f4415d63b7fbbaa782a2883 100644
--- a/Framework/PythonInterface/mantid/_plugins/__init__.py
+++ b/Framework/PythonInterface/mantid/_plugins/__init__.py
@@ -25,4 +25,7 @@ from __future__ import (absolute_import, division,
 ###############################################################################
 # Load the C++ library and register the C++ class exports
 ###############################################################################
-from . import _curvefitting
+from ..kernel import _shared_cextension
+
+with _shared_cextension():
+    from . import _curvefitting
diff --git a/Framework/PythonInterface/mantid/api/__init__.py b/Framework/PythonInterface/mantid/api/__init__.py
index 1dcdc32e70ff78dfcc7bd3eaa914919d81b75392..882f1a5d5bd8e8efc23c611dc969492f2bedb541 100644
--- a/Framework/PythonInterface/mantid/api/__init__.py
+++ b/Framework/PythonInterface/mantid/api/__init__.py
@@ -15,20 +15,9 @@ from __future__ import (absolute_import, division,
                         print_function)
 
 # Load the C++ library
-from . import _api
-from ._api import *
-
-# stdlib imports
-import atexit as _atexit
-
-###############################################################################
-# Start the framework
-###############################################################################
-FrameworkManagerImpl.Instance()
-_atexit.register(FrameworkManagerImpl.Instance().shutdown)
-
-# Declare any additional C++ algorithms defined in this package
-_api._declareCPPAlgorithms()
+from ..kernel import _shared_cextension
+with _shared_cextension():
+    from ._api import *
 
 ###############################################################################
 # Make aliases accessible in this namespace
diff --git a/Framework/PythonInterface/mantid/api/_aliases.py b/Framework/PythonInterface/mantid/api/_aliases.py
index ee9537702786fdcbca6d332fc9614d9ddc35e3cd..bc27830f56542b56254647a29abce2b4bfcfb379 100644
--- a/Framework/PythonInterface/mantid/api/_aliases.py
+++ b/Framework/PythonInterface/mantid/api/_aliases.py
@@ -14,18 +14,28 @@ from ._api import (FrameworkManagerImpl, AnalysisDataServiceImpl,
                    AlgorithmFactoryImpl, AlgorithmManagerImpl,
                    FileFinderImpl, FileLoaderRegistryImpl, FunctionFactoryImpl,
                    WorkspaceFactoryImpl, CatalogManagerImpl)
+from ..kernel._aliases import lazy_instance_access
 
-###############################################################################
-# Singleton
-###############################################################################
+# Historically the singleton aliases mapped to the instances rather than
+# the class types, i.e. AnalysisDataService is the instance and not the type,
+# which doesn't match the C++ behaviour.
+#
+# Exit handlers are important in some cases as the associated singleton
+# stores references to python objects that need to be cleaned up
+# Without a python-based exit handler the singletons are only cleaned
+# up after main() and this is too late to acquire the GIL to be able to
+# delete the python objects.
+# If you see a segfault late in a python process related to the GIL
+# it is likely an exit handler is missing.
+AnalysisDataService = lazy_instance_access(AnalysisDataServiceImpl)
+AlgorithmFactory = lazy_instance_access(AlgorithmFactoryImpl)
+AlgorithmManager = lazy_instance_access(AlgorithmManagerImpl)
+FileFinder = lazy_instance_access(FileFinderImpl)
+FileLoaderRegistry = lazy_instance_access(FileLoaderRegistryImpl)
+FrameworkManager = lazy_instance_access(FrameworkManagerImpl)
+FunctionFactory = lazy_instance_access(FunctionFactoryImpl)
+WorkspaceFactory = lazy_instance_access(WorkspaceFactoryImpl)
+CatalogManager = lazy_instance_access(CatalogManagerImpl)
 
-FrameworkManager = FrameworkManagerImpl.Instance()
-AnalysisDataService = AnalysisDataServiceImpl.Instance()
-mtd = AnalysisDataService #tradition
-AlgorithmFactory = AlgorithmFactoryImpl.Instance()
-AlgorithmManager = AlgorithmManagerImpl.Instance()
-FileFinder = FileFinderImpl.Instance()
-FileLoaderRegistry = FileLoaderRegistryImpl.Instance()
-FunctionFactory = FunctionFactoryImpl.Instance()
-WorkspaceFactory = WorkspaceFactoryImpl.Instance()
-CatalogManager = CatalogManagerImpl.Instance()
+# backwards-compatible
+mtd = AnalysisDataService
diff --git a/Framework/PythonInterface/mantid/api/src/Exports/AlgorithmManager.cpp b/Framework/PythonInterface/mantid/api/src/Exports/AlgorithmManager.cpp
index cc0aee81a49a26b55cdc1852a5da5c6054457096..9c4b374f962326c2331b27b00fbee7461a3a0f95 100644
--- a/Framework/PythonInterface/mantid/api/src/Exports/AlgorithmManager.cpp
+++ b/Framework/PythonInterface/mantid/api/src/Exports/AlgorithmManager.cpp
@@ -18,6 +18,26 @@ using Mantid::PythonInterface::AlgorithmIDProxy;
 using namespace boost::python;
 
 namespace {
+
+std::once_flag INIT_FLAG;
+
+/**
+ * Returns a reference to the AlgorithmManager object, creating it
+ * if necessary. In addition to creating the object the first call also:
+ *   - register AlgorithmManager.shutdown as an atexit function
+ * @return A reference to the AlgorithmManager instance
+ */
+AlgorithmManagerImpl &instance() {
+  // start the framework (if necessary)
+  auto &mgr = AlgorithmManager::Instance();
+  std::call_once(INIT_FLAG, []() {
+    PyRun_SimpleString("import atexit\n"
+                       "from mantid.api import AlgorithmManager\n"
+                       "atexit.register(lambda: AlgorithmManager.clear())");
+  });
+  return mgr;
+}
+
 /**
  * Return the algorithm identified by the given ID. A wrapper version that takes
  * a
@@ -107,7 +127,7 @@ void export_AlgorithmManager() {
            "Clears the current list of managed algorithms")
       .def("cancelAll", &AlgorithmManagerImpl::cancelAll, arg("self"),
            "Requests that all currently running algorithms be cancelled")
-      .def("Instance", &AlgorithmManager::Instance,
+      .def("Instance", instance,
            return_value_policy<reference_existing_object>(),
            "Return a reference to the singleton instance")
       .staticmethod("Instance");
diff --git a/Framework/PythonInterface/mantid/api/src/Exports/AnalysisDataService.cpp b/Framework/PythonInterface/mantid/api/src/Exports/AnalysisDataService.cpp
index a778874d61efcc4268f749bec833acd75aa6abc0..23213e94b6b6e0792f1d055be77f1737a44d3f35 100644
--- a/Framework/PythonInterface/mantid/api/src/Exports/AnalysisDataService.cpp
+++ b/Framework/PythonInterface/mantid/api/src/Exports/AnalysisDataService.cpp
@@ -24,6 +24,31 @@ using namespace boost::python;
 GET_POINTER_SPECIALIZATION(AnalysisDataServiceImpl)
 
 namespace {
+std::once_flag INIT_FLAG;
+
+/**
+ * Returns a reference to the AnalysisDataService object, creating it
+ * if necessary. In addition to creating the object the first call also:
+ *   - register AnalysisDataService.clear as an atexit function
+ * @return A reference to the FrameworkManagerImpl instance
+ */
+AnalysisDataServiceImpl &instance() {
+  // start the framework (if necessary)
+  auto &ads = AnalysisDataService::Instance();
+  std::call_once(INIT_FLAG, []() {
+    PyRun_SimpleString("import atexit\n"
+                       "from mantid.api import AnalysisDataService\n"
+                       "atexit.register(lambda: AnalysisDataService.clear())");
+  });
+  return ads;
+}
+
+/**
+ * @param self A reference to the AnalysisDataServiceImpl
+ * @param names The list of names to extract
+ * @param unrollGroups If true unroll the workspace groups
+ * @return a python list of the workspaces in the ADS
+ */
 list retrieveWorkspaces(AnalysisDataServiceImpl &self, const list &names,
                         bool unrollGroups = false) {
   return Converters::ToPyList<Workspace_sptr>()(self.retrieveWorkspaces(
@@ -45,7 +70,7 @@ void export_AnalysisDataService() {
       DataServiceExporter<AnalysisDataServiceImpl, Workspace_sptr>;
   auto pythonClass = ADSExporter::define("AnalysisDataServiceImpl");
   pythonClass
-      .def("Instance", &AnalysisDataService::Instance,
+      .def("Instance", instance,
            return_value_policy<reference_existing_object>(),
            "Return a reference to the singleton instance")
       .staticmethod("Instance")
diff --git a/Framework/PythonInterface/mantid/api/src/Exports/FrameworkManager.cpp b/Framework/PythonInterface/mantid/api/src/Exports/FrameworkManager.cpp
index c87f29ca7f860497f05fad31c54e7246fed63518..97eef7a231407184b44e91010908cb212344bce6 100644
--- a/Framework/PythonInterface/mantid/api/src/Exports/FrameworkManager.cpp
+++ b/Framework/PythonInterface/mantid/api/src/Exports/FrameworkManager.cpp
@@ -5,15 +5,90 @@
 //     & Institut Laue - Langevin
 // SPDX - License - Identifier: GPL - 3.0 +
 #include "MantidAPI/FrameworkManager.h"
+#include "MantidKernel/ConfigService.h"
+#include "MantidKernel/WarningSuppressions.h"
+#include "MantidPythonInterface/api/Algorithms/RunPythonScript.h"
 
 #include <boost/python/class.hpp>
+#include <boost/python/import.hpp>
 #include <boost/python/reference_existing_object.hpp>
 #include <boost/python/return_value_policy.hpp>
 
+#include <mutex>
+
+using Mantid::API::AlgorithmFactory;
 using Mantid::API::FrameworkManager;
 using Mantid::API::FrameworkManagerImpl;
+using Mantid::Kernel::ConfigService;
 using namespace boost::python;
 
+namespace {
+
+std::once_flag INIT_FLAG;
+bool INSTANCE_CALLED = false;
+constexpr auto PYTHONPATHS_KEY = "pythonscripts.directories";
+
+/**
+ * We don't want to register the C++ algorithms on loading the api python
+ * module since we want then be able to control when the various singletons
+ * are created if we are being imported from vanilla Python. This function
+ * registers the any C++ algorithms and should be called once.
+ */
+void declareCPPAlgorithms() {
+  AlgorithmFactory::Instance()
+      .subscribe<Mantid::PythonInterface::RunPythonScript>();
+}
+
+/**
+ * @brief Append to the sys.path any paths defined in the config key
+ * pythonscripts.directories
+ */
+void updatePythonPaths() {
+  auto packagesetup = import("mantid.kernel.packagesetup");
+  packagesetup.attr("update_sys_paths")(
+      ConfigService::Instance()
+          .getValue<std::string>(PYTHONPATHS_KEY)
+          .get_value_or(""));
+}
+
+/**
+ * Returns a reference to the FrameworkManager object, creating it
+ * if necessary. In addition to creating the object the first call also:
+ *   - registers the C++ algorithms declared in this library
+ *   - updates the Python paths with any user-defined directories
+ *     declared in the `pythonscripts.directories`
+ *   - import mantid.simpleapi (if not already imported) to load python plugins
+ *   - register FrameworkManager.shutdown as an atexit function
+ * @param importSimpleApi If true the the mantid.simpleapi module is imported on
+ * first access
+ * @return A reference to the FrameworkManagerImpl instance
+ */
+FrameworkManagerImpl &instance() {
+  // start the framework (if necessary)
+  auto &frameworkMgr = FrameworkManager::Instance();
+  std::call_once(INIT_FLAG, []() {
+    INSTANCE_CALLED = true;
+    declareCPPAlgorithms();
+    updatePythonPaths();
+    import("mantid.simpleapi");
+    // Without a python-based exit handler the singletons are only cleaned
+    // up after main() and this is too late to acquire the GIL to be able to
+    // delete any python objects still stored in other singletons like the
+    // ADS or AlgorithmManager.
+    PyRun_SimpleString("import atexit\n"
+                       "from mantid.api import FrameworkManager\n"
+                       "atexit.register(lambda: FrameworkManager.shutdown())");
+
+  });
+  return frameworkMgr;
+}
+
+/**
+ * @return True if .instance has been called, false otherwise
+ */
+bool hasInstance() { return INSTANCE_CALLED; }
+} // namespace
+
 void export_FrameworkManager() {
   class_<FrameworkManagerImpl, boost::noncopyable>("FrameworkManagerImpl",
                                                    no_init)
@@ -53,8 +128,11 @@ void export_FrameworkManager() {
       .def("shutdown", &FrameworkManagerImpl::shutdown, arg("self"),
            "Effectively shutdown this service")
 
-      .def("Instance", &FrameworkManager::Instance,
-           return_value_policy<reference_existing_object>(),
-           "Return a reference to the singleton instance")
+      .def("hasInstance", hasInstance,
+           "Returns True if Instance has been called, false otherwise")
+      .staticmethod("hasInstance")
+
+      .def("Instance", instance, "Return a reference to the singleton instance",
+           return_value_policy<reference_existing_object>())
       .staticmethod("Instance");
 }
diff --git a/Framework/PythonInterface/mantid/api/src/PythonAlgorithm/AlgorithmAdapter.cpp b/Framework/PythonInterface/mantid/api/src/PythonAlgorithm/AlgorithmAdapter.cpp
index abd52ba3b60b5323b0147ddba8f441fa51df71b5..1c52f09899d822b31a3cb64c8f702c5efd7cd2ff 100644
--- a/Framework/PythonInterface/mantid/api/src/PythonAlgorithm/AlgorithmAdapter.cpp
+++ b/Framework/PythonInterface/mantid/api/src/PythonAlgorithm/AlgorithmAdapter.cpp
@@ -334,7 +334,14 @@ template <typename BaseAlgorithm> void AlgorithmAdapter<BaseAlgorithm>::init() {
  * overridden in the subclass by a function named PyExec
  */
 template <typename BaseAlgorithm> void AlgorithmAdapter<BaseAlgorithm>::exec() {
-  callMethod<void>(getSelf(), "PyExec");
+  try {
+    callMethod<void>(getSelf(), "PyExec");
+  } catch (Mantid::PythonInterface::PythonException &) {
+    if (BaseAlgorithm::getCancel())
+      throw Mantid::API::Algorithm::CancelException();
+    else
+      throw;
+  }
 }
 
 //-----------------------------------------------------------------------------------------------------------------------------
diff --git a/Framework/PythonInterface/mantid/api/src/api.cpp.in b/Framework/PythonInterface/mantid/api/src/api.cpp.in
index 27d5554925e19187a3ae602d5ad6eaf41521eb47..33566e819bf0aa561340d602ab4439ceb764d09a 100644
--- a/Framework/PythonInterface/mantid/api/src/api.cpp.in
+++ b/Framework/PythonInterface/mantid/api/src/api.cpp.in
@@ -20,22 +20,9 @@
 
 #include "MantidAPI/AlgorithmFactory.h"
 #include "MantidAPI/Workspace.h"
-#include "MantidPythonInterface/api/Algorithms/RunPythonScript.h"
 
 namespace
 {
-  /**
-   * We don't want to register the C++ algorithms on loading the python module
-   * since we want then be able to control when the various singletons are started if
-   * we are being imported from vanilla Python.
-   * Here we export a single function that can be called after the FrameworkManager
-   * has been started to registered any hard-coded C++ algorithms that are
-   * contained within this module.
-   */
-  void _declareCPPAlgorithms()
-  {
-    Mantid::API::AlgorithmFactory::Instance().subscribe<Mantid::PythonInterface::RunPythonScript>();
-  }
 
   /**
    * Checks if two workspace shared pointers point to the same workspace
@@ -56,9 +43,6 @@ BOOST_PYTHON_MODULE(_api)
   boost::python::docstring_options docstrings(true, true, false);
   // Import numpy
   _import_array();
-  
-  // Internal function to declare C++ algorithms that are a part of this module
-  boost::python::def("_declareCPPAlgorithms", &_declareCPPAlgorithms);
 
   // Workspace address comparison
   boost::python::def("isSameWorkspaceObject",
diff --git a/Framework/PythonInterface/mantid/dataobjects/__init__.py b/Framework/PythonInterface/mantid/dataobjects/__init__.py
index 6250fac0b906b76429f7930537ca9739f1e455aa..7760476fd2bb0c5912a32d7a6a59f44b3ae96ab4 100644
--- a/Framework/PythonInterface/mantid/dataobjects/__init__.py
+++ b/Framework/PythonInterface/mantid/dataobjects/__init__.py
@@ -25,5 +25,10 @@ from __future__ import (absolute_import, division,
 ###############################################################################
 # Load the C++ library and register the C++ class exports
 ###############################################################################
-from . import _dataobjects
-from ._dataobjects import *
+# Load library dependencies
+from ..kernel import _shared_cextension
+import mantid.api
+
+with _shared_cextension():
+    from . import _dataobjects
+    from ._dataobjects import *
diff --git a/Framework/PythonInterface/mantid/fitfunctions.py b/Framework/PythonInterface/mantid/fitfunctions.py
index 55b3c78a001a3d3569b5c3d4ce737f16ffc2321b..fd53996acf888f8fbeb16907b8f70db2c4de404b 100644
--- a/Framework/PythonInterface/mantid/fitfunctions.py
+++ b/Framework/PythonInterface/mantid/fitfunctions.py
@@ -768,11 +768,12 @@ def _create_wrapper_function(name):
     return wrapper_function
 
 
-def _attach_wrappers(source_module):
+def _wrappers():
+    wrappers = []
     for name in FunctionFactory.getFunctionNames():
         # Wrap all registered functions which are not in the black list
         if name not in _do_not_wrap:
-            setattr(source_module, name, _create_wrapper_function(name))
+            yield name, _create_wrapper_function(name)
 
 
 _ExportedIFunction1D = IFunction1D
diff --git a/Framework/PythonInterface/mantid/geometry/__init__.py b/Framework/PythonInterface/mantid/geometry/__init__.py
index cd0a7d88786c602c11ce401ef21bb021d0214699..b63d13eb8231eed4cbe2c72d8dfb9806acf0035b 100644
--- a/Framework/PythonInterface/mantid/geometry/__init__.py
+++ b/Framework/PythonInterface/mantid/geometry/__init__.py
@@ -17,7 +17,10 @@ from __future__ import (absolute_import, division,
 ###############################################################################
 # Load the C++ library
 ###############################################################################
-from ._geometry import *
+from ..kernel import _shared_cextension
+
+with _shared_cextension():
+    from ._geometry import *
 
 ###############################################################################
 # Make aliases accessible in this namespace
diff --git a/Framework/PythonInterface/mantid/geometry/_aliases.py b/Framework/PythonInterface/mantid/geometry/_aliases.py
index d0a14a43638d5a881e902b4fbf94155d9ac2bf63..fe1422d409a3a5f831fc1201beb24cfbd1fdb065 100644
--- a/Framework/PythonInterface/mantid/geometry/_aliases.py
+++ b/Framework/PythonInterface/mantid/geometry/_aliases.py
@@ -11,13 +11,14 @@
 from __future__ import (absolute_import, division,
                         print_function)
 
+from ..kernel._aliases import lazy_instance_access
 from ._geometry import (SpaceGroupFactoryImpl, SymmetryOperationFactoryImpl,
                         SymmetryElementFactoryImpl, PointGroupFactoryImpl)
 
 ###############################################################################
-# Singleton
+# Singletons
 ###############################################################################
-SpaceGroupFactory = SpaceGroupFactoryImpl.Instance()
-SymmetryOperationFactory = SymmetryOperationFactoryImpl.Instance()
-SymmetryElementFactory = SymmetryElementFactoryImpl.Instance()
-PointGroupFactory = PointGroupFactoryImpl.Instance()
+SpaceGroupFactory = lazy_instance_access(SpaceGroupFactoryImpl)
+SymmetryOperationFactory = lazy_instance_access(SymmetryOperationFactoryImpl)
+SymmetryElementFactory = lazy_instance_access(SymmetryElementFactoryImpl)
+PointGroupFactory = lazy_instance_access(PointGroupFactoryImpl)
diff --git a/Framework/PythonInterface/mantid/kernel/__init__.py b/Framework/PythonInterface/mantid/kernel/__init__.py
index 29267e05171689767fd3e9ca74220dd07e4d78a3..8afb89228bdf0451ebabeb6fbe9f1cc6f1d14637 100644
--- a/Framework/PythonInterface/mantid/kernel/__init__.py
+++ b/Framework/PythonInterface/mantid/kernel/__init__.py
@@ -14,18 +14,44 @@ Defines Python objects that wrap the C++ Kernel namespace.
 from __future__ import (absolute_import, division,
                         print_function)
 
+import os as _os
+from contextlib import contextmanager
+
+@contextmanager
+def _shared_cextension():
+    """Our extensions need to shared symbols amongst them due to:
+      - the static boost python type registry
+      - static singleton instances marked as weak symbols by clang
+    gcc uses an extension to mark these attributes as globally unique
+    but clang marks them as weak and without RTLD_GLOBAL each shared
+    library has its own copy of each singleton.
+
+    See https://docs.python.org/3/library/sys.html#sys.setdlopenflags
+    """
+    import sys
+    if not sys.platform.startswith('linux'):
+        yield
+        return
+
+    import six
+    if six.PY2:
+        import DLFCN as dl
+    else:
+        import os as dl
+    flags_orig = sys.getdlopenflags()
+    sys.setdlopenflags(dl.RTLD_NOW | dl.RTLD_GLOBAL)
+    yield
+    sys.setdlopenflags(flags_orig)
+
+
 # Imports boost.mpi if applicable
 from . import mpisetup
 
 ###############################################################################
 # Load the C++ library
 ###############################################################################
-from ._kernel import *
-
-###############################################################################
-# Do any site-specific setup for packages
-###############################################################################
-from . import packagesetup as _packagesetup
+with _shared_cextension():
+    from ._kernel import *
 
 ###############################################################################
 # Make modules available in this namespace
@@ -37,3 +63,8 @@ from ._aliases import *
 # module alias for backwards-compatability in user scripts
 funcreturns = funcinspect
 
+###############################################################################
+# Do site-specific setup for packages
+###############################################################################
+from . import packagesetup as _mantidsite
+_mantidsite.set_NEXUSLIB_var()
diff --git a/Framework/PythonInterface/mantid/kernel/_aliases.py b/Framework/PythonInterface/mantid/kernel/_aliases.py
index b51972a2912dcb377e5b1464ececdde46fa526cd..607055a7163afcf95ab9f3fba31dc1b24967c261 100644
--- a/Framework/PythonInterface/mantid/kernel/_aliases.py
+++ b/Framework/PythonInterface/mantid/kernel/_aliases.py
@@ -14,17 +14,58 @@ from __future__ import (absolute_import, division,
 from ._kernel import (ConfigServiceImpl, Logger, UnitFactoryImpl,
                       UsageServiceImpl, PropertyManagerDataServiceImpl)
 
-###############################################################################
-# Singletons - Make them just look like static classes
-###############################################################################
-UsageService = UsageServiceImpl.Instance()
-ConfigService = ConfigServiceImpl.Instance()
-config = ConfigService
 
-PropertyManagerDataService = PropertyManagerDataServiceImpl.Instance()
-pmds = PropertyManagerDataService
+def lazy_instance_access(cls):
+    """
+    Takes a singleton class and wraps it in an LazySingletonHolder
+    that constructs the instance on first access.
+
+     Historically, mantid <= v3.13, the singleton aliases mapped to the
+    instances rather than the class types, i.e. UsageService is the instance and not the type.
+    To preserve compatibility with existing scripts, where users have not called UsageService.Instance(),
+    but also allow the singleton instance startup to be delayed we create a thin wrapper that
+    delays the first .Instance() call until an attribute is accessed on the wrapper.
+
+
+    :param cls: The singleton class type
+    :return: A new LazySingletonHolder wrapping cls
+    """
+    class LazySingletonHolder(object):
+        """
+        Delays construction of a singleton instance until the
+        first attribute access.
+        """
+
+        def __getattribute__(self, item):
+            # Called for each attribute access. cls.Instance() constructs
+            # the singleton at first access
+            return cls.__getattribute__(cls.Instance(), item)
+
+        def __len__(self):
+            return cls.__getattribute__(cls.Instance(), "__len__")()
 
-UnitFactory = UnitFactoryImpl.Instance()
+        def __getitem__(self, item):
+            return cls.__getattribute__(cls.Instance(), "__getitem__")(item)
+
+        def __setitem__(self, item, value):
+            return cls.__getattribute__(cls.Instance(), "__setitem__")(item, value)
+
+        def __delitem__(self, item):
+            return cls.__getattribute__(cls.Instance(), "__delitem__")(item)
+
+        def __contains__(self, item):
+            return cls.__getattribute__(cls.Instance(), "__contains__")(item)
+
+    return LazySingletonHolder()
+
+
+UsageService = lazy_instance_access(UsageServiceImpl)
+ConfigService = lazy_instance_access(ConfigServiceImpl)
+PropertyManagerDataService = lazy_instance_access(PropertyManagerDataServiceImpl)
+UnitFactory = lazy_instance_access(UnitFactoryImpl)
+
+config = ConfigService
+pmds = PropertyManagerDataService
 
 ###############################################################################
 # Set up a general Python logger. Others can be created as they are required
diff --git a/Framework/PythonInterface/mantid/kernel/funcinspect.py b/Framework/PythonInterface/mantid/kernel/funcinspect.py
index ebec73649dfa5724f0d77ecb053bf7a417819359..ff8eac18c91100a0a34c8fe98f670d41fd13f4c1 100644
--- a/Framework/PythonInterface/mantid/kernel/funcinspect.py
+++ b/Framework/PythonInterface/mantid/kernel/funcinspect.py
@@ -20,7 +20,7 @@ import sys
 import dis
 from six import PY3
 
-#-------------------------------------------------------------------------------
+
 def replace_signature(func, varnames):
     """
     Replace the signature of the given function object with that given by
@@ -48,7 +48,7 @@ def replace_signature(func, varnames):
     #endif
     setattr(func, code_attr, c)
 
-#-------------------------------------------------------------------------------
+
 def customise_func(func, name, signature, docstring):
     """
     Takes the definition of the algorithm function and replaces
diff --git a/Framework/PythonInterface/mantid/kernel/packagesetup.py.in b/Framework/PythonInterface/mantid/kernel/packagesetup.py.in
index 5f355a3bd05efcfaedff052b687d59e98376dccf..f8831ef9599c1c80a94ea1f5e526fa6eafd8e1b8 100644
--- a/Framework/PythonInterface/mantid/kernel/packagesetup.py.in
+++ b/Framework/PythonInterface/mantid/kernel/packagesetup.py.in
@@ -8,7 +8,7 @@ from __future__ import (absolute_import, division,
                         print_function)
 import os as _os
 import sys as _sys
-###############################################################################
+
 
 def update_sys_paths(paths, recursive=False):
     """
@@ -25,7 +25,7 @@ def update_sys_paths(paths, recursive=False):
         paths = paths.split(";")
 
     def _append_to_path(path):
-        #sys.path doesn't like trailing slashes
+        # sys.path doesn't like trailing slashes
         _sys.path.append(path.rstrip("\\").rstrip("/"))
 
     for path in paths:
@@ -35,7 +35,6 @@ def update_sys_paths(paths, recursive=False):
                 for dirname in dirnames:
                     _append_to_path(_os.path.join(dirpath, dirname))
 
-########################################################################################
 
 def set_NEXUSLIB_var():
     """
@@ -52,18 +51,3 @@ def set_NEXUSLIB_var():
         _nexuslib = _os.path.normpath(_os.path.join(thisdir, _nexuslib))
 
     _os.environ['NEXUSLIB'] = _nexuslib
-
-#########################################################################################
-
-## Setup on import ##
-import os as _os
-update_sys_paths(_os.environ.get("MANTIDPATH",""))
-
-from ._aliases import config as _config
-_path_keys = ['requiredpythonscript.directories','pythonscripts.directories']
-for key in _path_keys:
-    paths = _config[key]
-    update_sys_paths(paths)
-
-# NeXus
-set_NEXUSLIB_var()
diff --git a/Framework/PythonInterface/mantid/kernel/plugins.py b/Framework/PythonInterface/mantid/kernel/plugins.py
index 9fa5b5c6d6d5d2b76a47d998cce8f0da8047740b..99e7a396b09699d17e60e935dc403223c68597e7 100644
--- a/Framework/PythonInterface/mantid/kernel/plugins.py
+++ b/Framework/PythonInterface/mantid/kernel/plugins.py
@@ -15,6 +15,7 @@ from __future__ import (absolute_import, division,
 
 import os as _os
 import sys as _sys
+from traceback import format_exc
 try:
     from importlib.machinery import SourceFileLoader
 except ImportError:
@@ -193,8 +194,8 @@ def load_from_file(filepath):
     try:
         name, module = load_plugin(filepath)
         loaded.append(module)
-    except Exception as exc:
-        logger.warning("Failed to load plugin %s. Error: %s" % (filepath, str(exc)))
+    except Exception:
+        logger.warning("Failed to load plugin %s.\nError: %s" % (filepath, format_exc()))
 
     return loaded
 
@@ -215,20 +216,19 @@ def load_plugin(plugin_path):
 
 #======================================================================================================================
 
-def sync_attrs(source_module, attrs, clients):
+def sync_attrs(source, attrs, clients):
     """
         Syncs the attribute definitions between the
         given list from the source module & list of client modules such
         that the function defintions point to the same
         one
-        @param source_module :: The module containing the "correct"
-                                definitions
+        @param source :: A dictionary containing the real attribute definitions
         @param attrs :: The list of attributes to change in the client modules
         @param clients :: A list of modules whose attribute definitions
                           should be taken from source
     """
     for func_name in attrs:
-        attr = getattr(source_module, func_name)
+        attr = source[func_name]
         for plugin in clients:
             if hasattr(plugin, func_name):
                 setattr(plugin, func_name, attr)
diff --git a/Framework/PythonInterface/mantid/kernel/src/Exports/UsageService.cpp b/Framework/PythonInterface/mantid/kernel/src/Exports/UsageService.cpp
index f29a7668252e02da1151e3306ed2dd2ad8e05e0f..628f8ffedd53fe599c8aa32ecd9a498f904f479b 100644
--- a/Framework/PythonInterface/mantid/kernel/src/Exports/UsageService.cpp
+++ b/Framework/PythonInterface/mantid/kernel/src/Exports/UsageService.cpp
@@ -9,12 +9,36 @@
 #include <boost/python/class.hpp>
 #include <boost/python/reference_existing_object.hpp>
 
+#include <mutex>
+
 using Mantid::Kernel::UsageService;
 using Mantid::Kernel::UsageServiceImpl;
 using namespace boost::python;
 
 GET_POINTER_SPECIALIZATION(UsageServiceImpl)
 
+namespace {
+
+std::once_flag INIT_FLAG;
+
+/**
+ * Returns a reference to the UsageService object, creating it
+ * if necessary. In addition to creating the object the first call also:
+ *   - register UsageService.shutdown as an atexit function
+ * @return A reference to the UsageService instance
+ */
+UsageServiceImpl &instance() {
+  // start the framework (if necessary)
+  auto &svc = UsageService::Instance();
+  std::call_once(INIT_FLAG, []() {
+    PyRun_SimpleString("import atexit\n"
+                       "from mantid.kernel import UsageService\n"
+                       "atexit.register(lambda: UsageService.shutdown())");
+  });
+  return svc;
+}
+} // namespace
+
 void export_UsageService() {
 
   class_<UsageServiceImpl, boost::noncopyable>("UsageServiceImpl", no_init)
@@ -33,17 +57,17 @@ void export_UsageService() {
       .def("setInterval", &UsageServiceImpl::setEnabled,
            (arg("self"), arg("seconds")),
            "Sets the interval that the timer checks for tasks.")
-      .def("setApplication", &UsageServiceImpl::setApplication,
+      .def("setApplicationName", &UsageServiceImpl::setApplicationName,
            (arg("self"), arg("name")),
            "Sets the application name that has invoked Mantid.")
-      .def("getApplication", &UsageServiceImpl::getApplication, arg("self"),
-           "Gets the application name that has invoked Mantid.")
+      .def("getApplicationName", &UsageServiceImpl::getApplicationName,
+           arg("self"), "Gets the application name that has invoked Mantid.")
       .def("registerStartup", &UsageServiceImpl::registerStartup, arg("self"),
            "Registers the startup of Mantid.")
       .def("registerFeatureUsage", &UsageServiceImpl::registerFeatureUsage,
            (arg("self"), arg("type"), arg("name"), arg("internal")),
            "Registers the use of a feature in Mantid.")
-      .def("Instance", &UsageService::Instance,
+      .def("Instance", instance,
            return_value_policy<reference_existing_object>(),
            "Returns a reference to the UsageService")
       .staticmethod("Instance");
diff --git a/Framework/PythonInterface/mantid/kernel/src/kernel.cpp.in b/Framework/PythonInterface/mantid/kernel/src/kernel.cpp.in
index 5dd67ec43bd340fd98c56994d26d92516f53993f..23be3b48118a3015ed7752ff357389b7b486a90a 100644
--- a/Framework/PythonInterface/mantid/kernel/src/kernel.cpp.in
+++ b/Framework/PythonInterface/mantid/kernel/src/kernel.cpp.in
@@ -59,11 +59,11 @@ struct from_unicode_py2 {
     const char *value = PyString_AsString(pyutf8);
     if (value == 0)
       boost::python::throw_error_already_set();
-    Py_DECREF(pyutf8);
     using rvalue_from_python_std_string = converter::rvalue_from_python_storage<std::string>;
     void *storage = reinterpret_cast<rvalue_from_python_std_string*>(data)->storage.bytes;
     new (storage) std::string(value);
     data->convertible = storage;
+    Py_DECREF(pyutf8); // Must be deleted after the std::string has taken a copy
   }
 };
 #endif
diff --git a/Framework/PythonInterface/mantid/plots/__init__.py b/Framework/PythonInterface/mantid/plots/__init__.py
index c5e64186540e7fad44da8c57f908deb4d404341a..b910bc6ff731d5aa7ff72a1532ce232f707230ba 100644
--- a/Framework/PythonInterface/mantid/plots/__init__.py
+++ b/Framework/PythonInterface/mantid/plots/__init__.py
@@ -20,7 +20,27 @@ import mantid.plots.plotfunctions
 import mantid.plots.plotfunctions3D
 from matplotlib.axes import Axes
 from matplotlib.projections import register_projection
-from mpl_toolkits.mplot3d.axes3d import Axes3D
+
+try:
+    from mpl_toolkits.mplot3d.axes3d import Axes3D
+except ImportError:
+    # Special case to handle issues with importing mpl_toolkits
+    #
+    # Matplotlib adds a *nspkg.pth file to the user site packages directory.
+    # When that file is processed a fake built-in mpl_toolkits is imported
+    # which forces the site packages version to take precidence over our
+    # local copy regardless of python sys path settings.
+    #
+    # Work around by removing the fake built-in module from sys modules,
+    # then forcing python to search the path as expected.
+    #
+    # This is mostly but not necessarily limited to being an issue on OSX
+    # where there are multiple versions of matplotlib installed across the
+    # system.
+    import sys
+    del sys.modules['mpl_toolkits']
+    from mpl_toolkits.mplot3d.axes3d import Axes3D
+
 
 
 class MantidAxes(Axes):
diff --git a/Framework/PythonInterface/mantid/pyversion.py.in b/Framework/PythonInterface/mantid/pyversion.py.in
index 93a01f798583964edeb7744b14b9308e87171385..fe62ff95890b7e5582d6c59554d88fd6666fc3c8 100644
--- a/Framework/PythonInterface/mantid/pyversion.py.in
+++ b/Framework/PythonInterface/mantid/pyversion.py.in
@@ -7,13 +7,15 @@ import sys
 # Define the target major.minor version (i.e. the one mantid was built against)
 TARGET_VERSION="@PYTHON_VERSION_MAJOR@.@PYTHON_VERSION_MINOR@"
 
-_vers_info = sys.version_info
-_running_vers = "%d.%d" % (_vers_info[0],_vers_info[1])
-if _running_vers != TARGET_VERSION:
-    message = \
-"""Python version mismatch, cannot continue.
-Mantid was built against version '%s' but you are running version '%s'. These versions
-must match.
-"""
-    raise ImportError(message % (TARGET_VERSION, _running_vers))
+
+def check_python_version():
+    vers_info = sys.version_info
+    running_vers = "%d.%d" % (vers_info[0], vers_info[1])
+    if running_vers != TARGET_VERSION:
+        message = \
+    """Python version mismatch, cannot continue.
+    Mantid was built against version '%s' but you are running version '%s'. These versions
+    must match.
+    """
+        raise ImportError(message % (TARGET_VERSION, running_vers))
 
diff --git a/Framework/PythonInterface/mantid/simpleapi.py b/Framework/PythonInterface/mantid/simpleapi.py
index 129e222d05155266971f48ddd239363aada876f2..a31cea9392a7f3da5d4acd402d086bd04ce4cb67 100644
--- a/Framework/PythonInterface/mantid/simpleapi.py
+++ b/Framework/PythonInterface/mantid/simpleapi.py
@@ -5,7 +5,7 @@
 #     & Institut Laue - Langevin
 # SPDX - License - Identifier: GPL - 3.0 +
 """
-This module defines a simple function-style API for running Mantid
+This module defines a function-style API for running Mantid
 algorithms. Each algorithm within Mantid is mapped to a Python
 function of the same name with the parameters of the algorithm becoming
 arguments to the function.
@@ -22,21 +22,30 @@ the variable it is assigned to, i.e.
    rebinned = Rebin(input, Params = '0.1,0.05,10')
 
 would call Rebin with the given parameters and create a workspace called 'rebinned'
-and assign it to the rebinned variable
+and assign it to the rebinned variable.
+
+Importing this module starts the FrameworkManager instance.
 """
 from __future__ import (absolute_import, division,
                         print_function)
 
-import six
-from six import iteritems
+# stdlib imports
 from collections import OrderedDict, namedtuple
 import os
+import six
+from six import iteritems
+import sys
 
+import mantid
 from . import api as _api
 from . import kernel as _kernel
+from .kernel import plugins as _plugin_helper
+from .kernel.packagesetup import update_sys_paths as _update_sys_paths
 from .kernel.funcinspect import lhs_info as _lhs_info
 from .kernel.funcinspect import replace_signature as _replace_signature
 from .kernel.funcinspect import customise_func as _customise_func
+# register matplotlib projection
+from . import plots  # noqa
 
 # This is a simple API so give access to the aliases by default as well
 from . import apiVersion, __gui__
@@ -44,7 +53,8 @@ from .kernel._aliases import *
 from .api._aliases import *
 from .fitfunctions import *
 
-# ------------------------ Specialized function calls --------------------------
+MODULE_NAME = 'simpleapi'
+
 # List of specialized algorithms
 __SPECIALIZED_FUNCTIONS__ = ["Load", "StartLiveData", "CutMD", "RenameWorkspace"]
 # List of specialized algorithms
@@ -1280,8 +1290,6 @@ def _create_algorithm_dialog(algorithm, version, _algm_object):
     for alias in _algm_object.alias().strip().split(): # split on whitespace
         globals()["{}Dialog".format(alias)] = algm_wrapper
 
-# --------------------------------------------------------------------------------------------------
-
 
 def _create_fake_function(name):
     """Create fake functions for the given name
@@ -1296,13 +1304,10 @@ def _create_fake_function(name):
     _replace_signature(fake_function, ("", ""))
     globals()[name] = fake_function
 
-# ------------------------------------------------------------------------------------------------------------
-
 
 def _mockup(plugins):
     """
-        Creates fake, error-raising functions for all loaded algorithms plus
-        any plugins given.
+        Creates fake, error-raising functions for any plugins given.
         The function name for the Python algorithms are taken from the filename
         so this mechanism requires the algorithm name to match the filename.
         This mechanism solves the "chicken-and-egg" problem with Python algorithms trying
@@ -1315,7 +1320,8 @@ def _mockup(plugins):
         function definitions can overwrite the "fake" ones.
         :param plugins: A list of  modules that have been loaded
     """
-    # --------------------------------------------------------------------------------------------------------
+    module_attrs = globals()
+
     def create_fake_function(func_name):
         """Create fake functions for the given func_name
         """
@@ -1330,40 +1336,23 @@ def _mockup(plugins):
         if specialization_exists(func_name):
             return
         fake_function.__name__ = func_name
-        globals()[func_name] = fake_function
-    # --------------------------------------------------------
+        module_attrs[func_name] = fake_function
 
-    def create_fake_functions(alg_names):
-        """Create fake functions for all of the listed names
-        """
-        for alg_name in alg_names:
-            create_fake_function(alg_name)
-    # -------------------------------------
-
-    # Start with the loaded C++ algorithms
-    from mantid.api import AlgorithmFactory
-    cppalgs = AlgorithmFactory.getRegisteredAlgorithms(True)
-    create_fake_functions(cppalgs.keys())
-
-    # Now the plugins
     for plugin in plugins:
         name = os.path.basename(plugin)
         name = os.path.splitext(name)[0]
         create_fake_function(name)
 
-# ------------------------------------------------------------------------------------------------------------
-
 
 def _translate():
     """
         Loop through the algorithms and register a function call
         for each of them
-        :returns: a list of new function calls
+        :returns: a list of the name of new function calls
     """
     from mantid.api import AlgorithmFactory, AlgorithmManager
 
-    # Names of new functions added to the global namespace
-    new_functions = []
+    new_func_attrs = []
     # Method names mapped to their algorithm names. Used to detect multiple copies of same method name
     # on different algorithms, which is an error
     new_methods = {}
@@ -1393,12 +1382,12 @@ def _translate():
                                    % (method_name, algm_object.name(), other_alg))
             _attach_algorithm_func_as_method(method_name, algorithm_wrapper, algm_object)
             new_methods[method_name] = algm_object.name()
+        new_func_attrs.append(name)
 
         # Dialog variant
         _create_algorithm_dialog(name, max(versions), algm_object)
-        new_functions.append(name)
 
-    return new_functions
+    return new_func_attrs
 
 # -------------------------------------------------------------------------------------------------------------
 
@@ -1421,8 +1410,72 @@ def _attach_algorithm_func_as_method(method_name, algorithm_wrapper, algm_object
         raise RuntimeError("simpleapi: '%s' has requested to be attached as a workspace method but "
                            "Algorithm::workspaceMethodInputProperty() has returned a property name that "
                            "does not exist on the algorithm." % algm_object.name())
-
     _api._workspaceops.attach_func_as_method(method_name, algorithm_wrapper, input_prop,
                                              algm_object.workspaceMethodOn())
 
-# -------------------------------------------------------------------------------------------------------------
+
+# Initialization:
+#   - start FrameworkManager (if necessary). The check is necessary as
+#    _FrameworkManagerImpl.Instance() will import this module and deadlock if it
+#    calls Instance again while importing this module
+#   - loads the python plugins and create new algorithm functions
+if not _api.FrameworkManagerImpl.hasInstance():
+    _api.FrameworkManagerImpl.Instance()
+_translate()
+
+# Load the Python plugins
+# The exported C++ plugins
+from . import _plugins  # noqa
+
+# Now the algorithms
+# There is a chicken and egg problem with what we want to achieve here.
+# The simpleapi module should contain function definitions for all algorithms
+# and fit function classes but a python plugin can choose to import
+# simpleapi itself before we have been finished initializing the module
+# and creating a circular dependency. The only way to avoid this is to
+# restrict the usage of simpleapi in Python plugins so that
+# 'from simpleapi import *' is banned and all access is through
+# 'import mantid.simpleapi as sapi'
+
+# Set the .simpleapi attribute on the 'mantid' module before importing
+# the plugins. Python usual does this once the module has been fully imported
+# but we need to do this earlier
+setattr(mantid, MODULE_NAME, sys.modules['mantid.{}'.format(MODULE_NAME)])
+try:
+    _plugins_key = 'python.plugins.directories'
+    _user_key = 'user.%s' % _plugins_key
+    plugin_dirs = _plugin_helper.get_plugin_paths_as_set(_plugins_key)
+    plugin_dirs.update(_plugin_helper.get_plugin_paths_as_set(_user_key))
+    _update_sys_paths(plugin_dirs, recursive=True)
+
+    # Load
+    plugin_files = []
+    alg_files = []
+    for directory in plugin_dirs:
+        try:
+            all_plugins, algs = _plugin_helper.find_plugins(directory)
+            plugin_files += all_plugins
+            alg_files += algs
+        except ValueError as exc:
+            logger.warning('Exception encountered during plugin discovery: {0}'.format(str(exc)))
+            continue
+
+    # Mock out the expected functions
+    _mockup(alg_files)
+    # Load the plugins.
+    _plugin_modules = _plugin_helper.load(plugin_files)
+    # Create the final proper algorithm definitions for the plugins
+    _plugin_attrs = _translate()
+    # Finally, overwrite the mocked function definitions in the loaded modules with the real ones
+    _plugin_helper.sync_attrs(globals(), _plugin_attrs, _plugin_modules)
+
+    # Attach fit function wrappers
+    from .fitfunctions import _wrappers
+    _globals = globals()
+    for _name, _wrapper in _wrappers():
+        _globals[_name] = _wrapper
+except Exception:
+    # If an error gets raised remove the attribute to be consistent
+    # with standard python behaviour and reraise the exception
+    delattr(mantid, MODULE_NAME)
+    raise
diff --git a/Framework/PythonInterface/plugins/algorithms/Abins.py b/Framework/PythonInterface/plugins/algorithms/Abins.py
index 0981fa486689c289325390bd5fc4a84aba7adc1f..f3d82039caa4c9bc3ffd4493a6fd19d68daf4d1d 100644
--- a/Framework/PythonInterface/plugins/algorithms/Abins.py
+++ b/Framework/PythonInterface/plugins/algorithms/Abins.py
@@ -837,7 +837,4 @@ class Abins(PythonAlgorithm):
         self._bins = np.arange(start=start, stop=stop, step=step, dtype=AbinsModules.AbinsConstants.FLOAT_TYPE)
 
 
-try:
-    AlgorithmFactory.subscribe(Abins)
-except ImportError:
-    logger.debug('Failed to subscribe algorithm SimulatedDensityOfStates; The python package may be missing.')
+AlgorithmFactory.subscribe(Abins)
diff --git a/Framework/PythonInterface/plugins/algorithms/MagnetismReflectometryReduction.py b/Framework/PythonInterface/plugins/algorithms/MagnetismReflectometryReduction.py
index dadfc18c289268ec8a77aacfcedc9bab691160d5..8d163f4c9925d5d082d3899a33b8dbb256f50e6d 100644
--- a/Framework/PythonInterface/plugins/algorithms/MagnetismReflectometryReduction.py
+++ b/Framework/PythonInterface/plugins/algorithms/MagnetismReflectometryReduction.py
@@ -95,6 +95,7 @@ class MagnetismReflectometryReduction(PythonAlgorithm):
         self.declareProperty("RoundUpPixel", True, doc="If True, round up pixel position of the specular reflectivity")
         self.declareProperty("UseSANGLE", False, doc="If True, use SANGLE as the scattering angle")
         self.declareProperty("SpecularPixel", 180.0, doc="Pixel position of the specular reflectivity")
+        self.declareProperty("FinalRebin", True, doc="If True, the final reflectivity will be rebinned")
         self.declareProperty("QMin", 0.005, doc="Minimum Q-value")
         self.declareProperty("QStep", 0.02, doc="Step size in Q. Enter a negative value to get a log scale")
         self.declareProperty("AngleOffset", 0.0, doc="angle offset (rad)")
@@ -442,13 +443,22 @@ class MagnetismReflectometryReduction(PythonAlgorithm):
         q_workspace = SortXAxis(InputWorkspace=q_workspace, OutputWorkspace=str(q_workspace))
 
         name_output_ws = str(workspace)+'_reflectivity'
-        try:
-            q_rebin = Rebin(InputWorkspace=q_workspace, Params=q_range,
-                            OutputWorkspace=name_output_ws)
-        except:
-            raise RuntimeError("Could not rebin with %s" % str(q_range))
+        do_q_rebin = self.getProperty("FinalRebin").value
 
-        AnalysisDataService.remove(str(q_workspace))
+        if do_q_rebin:
+            try:
+                q_rebin = Rebin(InputWorkspace=q_workspace, Params=q_range,
+                                OutputWorkspace=name_output_ws)
+                AnalysisDataService.remove(str(q_workspace))
+            except:
+                logger.error("Could not rebin with %s" % str(q_range))
+                do_q_rebin = False
+
+        # If we either didn't want to rebin or we failed to rebin,
+        # rename the reflectivity workspace and proceed with it.
+        if not do_q_rebin:
+            q_rebin = RenameWorkspace(InputWorkspace=q_workspace,
+                                      OutputWorkspace=name_output_ws)
 
         return q_rebin
 
diff --git a/Framework/PythonInterface/plugins/algorithms/ReflectometryReductionOneLiveData.py b/Framework/PythonInterface/plugins/algorithms/ReflectometryReductionOneLiveData.py
index 9a03ee3a086b858914503a3433c02226c4ba09e1..a50d02a38671a768094e2a665089bc4b6c951529 100644
--- a/Framework/PythonInterface/plugins/algorithms/ReflectometryReductionOneLiveData.py
+++ b/Framework/PythonInterface/plugins/algorithms/ReflectometryReductionOneLiveData.py
@@ -48,7 +48,7 @@ class ReflectometryReductionOneLiveData(DataProcessorAlgorithm):
             'SecondTransmissionRun', 'Params', 'StartOverlap', 'EndOverlap',
             'CorrectionAlgorithm', 'Polynomial', 'C0', 'C1',
             'MomentumTransferMin', 'MomentumTransferStep', 'MomentumTransferMax',
-            'PolarizationAnalysis', 'Pp', 'Ap', 'Rho', 'Alpha', 'Debug', 'OutputWorkspace']
+            'PolarizationAnalysis', 'CPp', 'CAp', 'CRho', 'CAlpha', 'Debug', 'OutputWorkspace']
         self.copyProperties('ReflectometryReductionOneAuto', self._child_properties)
 
     def PyExec(self):
diff --git a/Framework/PythonInterface/plugins/algorithms/SaveGEMMAUDParamFile.py b/Framework/PythonInterface/plugins/algorithms/SaveGEMMAUDParamFile.py
index afb98fecc28b73fe5b8095683971ef4f7b96ecca..2fd7c1af90e190f7aced9518a7e3dbe80028b1e4 100644
--- a/Framework/PythonInterface/plugins/algorithms/SaveGEMMAUDParamFile.py
+++ b/Framework/PythonInterface/plugins/algorithms/SaveGEMMAUDParamFile.py
@@ -14,6 +14,17 @@ from string import Formatter
 
 from mantid.api import *
 from mantid.kernel import *
+import isis_powder.gem_routines
+
+_MAUD_TEMPLATE_PATH = None
+
+
+def _maud_template_path():
+    global _MAUD_TEMPLATE_PATH
+    if _MAUD_TEMPLATE_PATH is None:
+        _MAUD_TEMPLATE_PATH = os.path.join(os.path.dirname(isis_powder.gem_routines.__file__),
+                                           'maud_param_template.maud')
+    return _MAUD_TEMPLATE_PATH
 
 
 class SaveGEMMAUDParamFile(PythonAlgorithm):
@@ -52,7 +63,7 @@ class SaveGEMMAUDParamFile(PythonAlgorithm):
 
         self.declareProperty(FileProperty(name=self.PROP_TEMPLATE_FILE,
                                           action=FileAction.Load,
-                                          defaultValue=self._find_isis_powder_dir()),
+                                          defaultValue=_maud_template_path()),
                              doc="Template for the .maud file")
 
         self.declareProperty(IntArrayProperty(name=self.PROP_GROUPING_SCHEME),
@@ -135,20 +146,6 @@ class SaveGEMMAUDParamFile(PythonAlgorithm):
         """
         return (bank_param_list[grouping_scheme[spec_num] - 1] for spec_num in spectrum_numbers)
 
-    def _find_isis_powder_dir(self):
-        script_dirs = [directory for directory in config["pythonscripts.directories"].split(";")
-                       if "Diffraction" in directory]
-
-        for directory in script_dirs:
-            path_to_test = os.path.join(directory,
-                                        "isis_powder",
-                                        "gem_routines",
-                                        "maud_param_template.maud")
-            if os.path.exists(path_to_test):
-                return path_to_test
-
-        return ""
-
     def _format_param_list(self, param_list):
         return "\n".join(str(param) for param in param_list)
 
diff --git a/Framework/PythonInterface/plugins/algorithms/SortByQVectors.py b/Framework/PythonInterface/plugins/algorithms/SortByQVectors.py
index 0920554ba1ba2c5c149fa3b3d041897d60c2ebad..ac06d3a7590e7bed28ae0c878fd6d38133345340 100644
--- a/Framework/PythonInterface/plugins/algorithms/SortByQVectors.py
+++ b/Framework/PythonInterface/plugins/algorithms/SortByQVectors.py
@@ -6,8 +6,8 @@
 # SPDX - License - Identifier: GPL - 3.0 +
 #pylint: disable=no-init,invalid-name
 from __future__ import (absolute_import, division, print_function)
-from mantid.kernel import *
 from mantid.api import *
+from mantid.kernel import *
 import mantid.simpleapi as ms
 import numpy as np
 
diff --git a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/ResNorm2.py b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/ResNorm2.py
index 13de0b537cccaa336f9192ff3f68c8406cd3ae7c..4a2aa8a62ae214b8608942a0c8685c5127f917a8 100644
--- a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/ResNorm2.py
+++ b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/ResNorm2.py
@@ -95,16 +95,20 @@ class ResNorm(PythonAlgorithm):
 
     def PyExec(self):
         res_clone_name = '__' + self._res_ws
-        res_clone_ws= CloneWorkspace(InputWorkspace=self._res_ws, OutputWorkspace=res_clone_name)
+        res_clone_ws = CloneWorkspace(InputWorkspace=self._res_ws, OutputWorkspace=res_clone_name)
 
         if self._create_output:
             self._out_ws_table = self.getPropertyValue('OutputWorkspaceTable')
 
         # Process vanadium workspace
-        van_ws = ConvertSpectrumAxis(InputWorkspace=self._van_ws,
-                                     OutputWorkspace='__ResNorm_vanadium',
-                                     Target='ElasticQ',
-                                     EMode='Indirect')
+        if self._van_ws[-4:] == "_red":
+            van_ws = ConvertSpectrumAxis(InputWorkspace=self._van_ws,
+                                         OutputWorkspace='__ResNorm_vanadium',
+                                         Target='ElasticQ',
+                                         EMode='Indirect')
+        else:
+            van_ws = CloneWorkspace(InputWorkspace=self._van_ws,
+                                    OutputWorkspace='__ResNorm_vanadium')
 
         num_hist = van_ws.getNumberHistograms()
 
diff --git a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSSingleReduction.py b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSSingleReduction.py
index 2856184ef77f8b5ed2837e861a2fba0633377183..ee6b49605af9455e93a6d77ce2c7a5472d69eaec 100644
--- a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSSingleReduction.py
+++ b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSSingleReduction.py
@@ -40,6 +40,9 @@ class SANSSingleReduction(DistributedDataProcessorAlgorithm):
                                  "Depending on your concrete reduction, this could provide a significant"
                                  " performance boost")
 
+        self.declareProperty("SaveCan", False, direction=Direction.Input,
+                             doc="When enabled, the unsubtracted can and sam workspaces are added to the ADS.")
+
         # Sample Scatter Workspaces
         self.declareProperty(MatrixWorkspaceProperty('SampleScatterWorkspace', '',
                                                      optional=PropertyMode.Mandatory, direction=Direction.Input),
@@ -113,26 +116,18 @@ class SANSSingleReduction(DistributedDataProcessorAlgorithm):
         self.setPropertyGroup("OutputWorkspaceMerged", 'Output')
 
         # CAN output
-        # We want to output the can workspaces since they can be persited in the case of optimizations
         self.declareProperty(MatrixWorkspaceProperty('OutputWorkspaceLABCan', '',
                                                      optional=PropertyMode.Optional, direction=Direction.Output),
                              doc='The can output workspace for the low-angle bank, provided there is one.')
-        self.declareProperty(MatrixWorkspaceProperty('OutputWorkspaceLABCanCount', '',
-                                                     optional=PropertyMode.Optional, direction=Direction.Output),
-                             doc='The can count output workspace for the low-angle bank, provided there is one.')
-        self.declareProperty(MatrixWorkspaceProperty('OutputWorkspaceLABCanNorm', '',
-                                                     optional=PropertyMode.Optional, direction=Direction.Output),
-                             doc='The can norm output workspace for the low-angle bank, provided there is one.')
-
         self.declareProperty(MatrixWorkspaceProperty('OutputWorkspaceHABCan', '',
                                                      optional=PropertyMode.Optional, direction=Direction.Output),
                              doc='The can output workspace for the high-angle bank, provided there is one.')
-        self.declareProperty(MatrixWorkspaceProperty('OutputWorkspaceHABCanCount', '',
+        self.declareProperty(MatrixWorkspaceProperty('OutputWorkspaceLABSample', '',
                                                      optional=PropertyMode.Optional, direction=Direction.Output),
-                             doc='The can count output workspace for the high-angle bank, provided there is one.')
-        self.declareProperty(MatrixWorkspaceProperty('OutputWorkspaceHABCanNorm', '',
+                             doc='The sample output workspace for the low-angle bank, provided there is one.')
+        self.declareProperty(MatrixWorkspaceProperty('OutputWorkspaceHABSample', '',
                                                      optional=PropertyMode.Optional, direction=Direction.Output),
-                             doc='The can norm output workspace for the high-angle bank, provided there is one.')
+                             doc='The sample output workspace for the high-angle bank, provided there is one')
         self.declareProperty(MatrixWorkspaceProperty('OutputWorkspaceCalculatedTransmission', '',
                                                      optional=PropertyMode.Optional, direction=Direction.Output),
                              doc='The calculated transmission workspace')
@@ -146,11 +141,28 @@ class SANSSingleReduction(DistributedDataProcessorAlgorithm):
                                                      optional=PropertyMode.Optional, direction=Direction.Output),
                              doc='The unfitted transmission workspace for the can')
         self.setPropertyGroup("OutputWorkspaceLABCan", 'Can Output')
-        self.setPropertyGroup("OutputWorkspaceLABCanCount", 'Can Output')
-        self.setPropertyGroup("OutputWorkspaceLABCanNorm", 'Can Output')
         self.setPropertyGroup("OutputWorkspaceHABCan", 'Can Output')
-        self.setPropertyGroup("OutputWorkspaceHABCanCount", 'Can Output')
-        self.setPropertyGroup("OutputWorkspaceHABCanNorm", 'Can Output')
+        self.setPropertyGroup("OutputWorkspaceLABSample", 'Can Output')
+        self.setPropertyGroup("OutputWorkspaceHABSample", 'Can Output')
+
+        # Output CAN Count and Norm for optimizations
+        self.declareProperty(MatrixWorkspaceProperty('OutputWorkspaceLABCanNorm', '',
+                                                     optional=PropertyMode.Optional, direction=Direction.Output),
+                             doc='The can norm output workspace for the low-angle bank, provided there is one.')
+        self.declareProperty(MatrixWorkspaceProperty('OutputWorkspaceLABCanCount', '',
+                                                     optional=PropertyMode.Optional, direction=Direction.Output),
+                             doc='The can count output workspace for the low-angle bank, provided there is one.')
+        self.declareProperty(MatrixWorkspaceProperty('OutputWorkspaceHABCanCount', '',
+                                                     optional=PropertyMode.Optional, direction=Direction.Output),
+                             doc='The can count output workspace for the high-angle bank, provided there is one.')
+        self.declareProperty(MatrixWorkspaceProperty('OutputWorkspaceHABCanNorm', '',
+                                                     optional=PropertyMode.Optional, direction=Direction.Output),
+                             doc='The can norm output workspace for the high-angle bank, provided there is one.')
+
+        self.setPropertyGroup("OutputWorkspaceLABCanCount", 'Opt Output')
+        self.setPropertyGroup("OutputWorkspaceLABCanNorm", 'Opt Output')
+        self.setPropertyGroup("OutputWorkspaceHABCanCount", 'Opt Output')
+        self.setPropertyGroup("OutputWorkspaceHABCanNorm", 'Opt Output')
 
     def PyExec(self):
         # Get state
@@ -166,6 +178,7 @@ class SANSSingleReduction(DistributedDataProcessorAlgorithm):
 
         # Run core reductions
         use_optimizations = self.getProperty("UseOptimizations").value
+        save_can = self.getProperty("SaveCan").value
 
         # Create the reduction core algorithm
         reduction_name = "SANSReductionCore"
@@ -233,6 +246,9 @@ class SANSSingleReduction(DistributedDataProcessorAlgorithm):
         if use_optimizations:
             self.set_reduced_can_workspace_on_output(output_bundles, output_parts_bundles)
 
+        if save_can:
+            self.set_can_and_sam_on_output(output_bundles)
+
         if state.adjustment.show_transmission:
             self.set_transmission_workspaces_on_output(output_transmission_bundles,
                                                        state.adjustment.calculate_transmission.fit)
@@ -403,6 +419,45 @@ class SANSSingleReduction(DistributedDataProcessorAlgorithm):
                         raise RuntimeError("SANSSingleReduction: The reduction mode {0} should not"
                                            " be set with a partial can.".format(reduction_mode))
 
+    def set_can_and_sam_on_output(self, output_bundles):
+        '''
+        Sets the reduced can and sam workspaces.
+        These can be:
+        1. LAB Can
+        2. HAB Can
+        3. LAB Sample
+        4. HAB Sample
+        Cans are also output for optimization, so check for double output.
+        :param output_bundles: a list of output_bundles
+        '''
+
+        for output_bundle in output_bundles:
+            if output_bundle.data_type is DataType.Can:
+                reduction_mode = output_bundle.reduction_mode
+                output_workspace = output_bundle.output_workspace
+
+                if output_workspace is not None and not does_can_workspace_exist_on_ads(output_workspace):
+                    if reduction_mode is ISISReductionMode.LAB:
+                        self.setProperty("OutputWorkspaceLABCan", output_workspace)
+                    elif reduction_mode is ISISReductionMode.HAB:
+                        self.setProperty("OutputWorkspaceHABCan", output_bundle.output_workspace)
+                    else:
+                        raise RuntimeError("SANSSingleReduction: The reduction mode {0} should not"
+                                           " be set with a can.".format(reduction_mode))
+
+            elif output_bundle.data_type is DataType.Sample:
+                reduction_mode = output_bundle.reduction_mode
+                output_workspace = output_bundle.output_workspace
+
+                if output_workspace is not None:
+                    if reduction_mode is ISISReductionMode.LAB:
+                        self.setProperty("OutputWorkspaceLABSample", output_workspace)
+                    elif reduction_mode is ISISReductionMode.HAB:
+                        self.setProperty("OutputWorkspaceHABSample", output_bundle.output_workspace)
+                    else:
+                        raise RuntimeError("SANSSingleReduction: The reduction mode {0} should not"
+                                           " be set with a sample.".format(reduction_mode))
+
     def set_transmission_workspaces_on_output(self, transmission_bundles, fit_state):
         for transmission_bundle in transmission_bundles:
             fit_performed = fit_state[DataType.to_string(transmission_bundle.data_type)].fit_type != FitType.NoFit
diff --git a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANSILLReduction.py b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANSILLReduction.py
index df4a291b445f95291e4fd41785a55acabec68cd7..370270d2de7a0278fae694d164a022bada207f57 100644
--- a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANSILLReduction.py
+++ b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANSILLReduction.py
@@ -250,7 +250,7 @@ class SANSILLReduction(PythonAlgorithm):
 
     @staticmethod
     def _parallax_correction(ws):
-        formula = ws.getInstrument().getStringParameter('parallax')[0]
+        formula = ws.getInstrument().getStringParameter('parallax_old')[0]
         l2 = ws.getRun().getLogData('L2').value
         n_spectra = ws.getNumberHistograms()
         p = np.empty(n_spectra)
@@ -342,7 +342,7 @@ class SANSILLReduction(PythonAlgorithm):
                         thickness = self.getProperty('SampleThickness').value
                         NormaliseByThickness(InputWorkspace=ws, OutputWorkspace=ws, SampleThickness=thickness)
                         # parallax (gondola) effect
-                        if mtd[ws].getInstrument().hasParameter('parallax'):
+                        if mtd[ws].getInstrument().hasParameter('parallax_old'):
                             # for the moment it's only D22 that has this
                             self.log().information('Performing parallax correction')
                             self._parallax_correction(mtd[ws])
diff --git a/Framework/PythonInterface/test/python/mantid/ImportModuleTest.py b/Framework/PythonInterface/test/python/mantid/ImportModuleTest.py
index 8f8fd4015c0db626ca1197f944259695c991888a..7f7177dd07ba667ee2240c8bddd6301c1be97ed4 100644
--- a/Framework/PythonInterface/test/python/mantid/ImportModuleTest.py
+++ b/Framework/PythonInterface/test/python/mantid/ImportModuleTest.py
@@ -14,23 +14,12 @@ class ImportModuleTest(unittest.TestCase):
 
     def test_import_succeeds(self):
         import mantid
-        # Check content
         attrs = dir(mantid)
-        self.assertTrue('api' in attrs)
-        self.assertTrue('geometry' in attrs)
-        self.assertTrue('kernel' in attrs)
+        self.assertTrue('__version__' in attrs)
 
     def test_on_import_gui_flag_is_set_to_false_here(self):
         import mantid
         self.assertEquals(False, mantid.__gui__)
 
-    def test_python_algorithms_are_loaded_recursively(self):
-        """
-        Test needs improving when the old API goes to just check that everything loads okay
-        """
-        all_algs = AlgorithmFactory.getRegisteredAlgorithms(True)
-        self.assertTrue('SNSPowderReduction' in all_algs)
-        self.assertTrue('Squares' in all_algs)
-
 if __name__ == '__main__':
     unittest.main()
diff --git a/Framework/PythonInterface/test/python/mantid/SimpleAPITest.py b/Framework/PythonInterface/test/python/mantid/SimpleAPITest.py
index 8fce0e8d92bf2920378e319eda53e19db7ca6e11..a046c1c697e7b1a09f0591ad7dc237912c1a2ac6 100644
--- a/Framework/PythonInterface/test/python/mantid/SimpleAPITest.py
+++ b/Framework/PythonInterface/test/python/mantid/SimpleAPITest.py
@@ -21,8 +21,16 @@ class SimpleAPITest(unittest.TestCase):
     def tearDown(self):
         mtd.clear()
 
+    def test_python_algorithms_are_loaded_recursively(self):
+        """
+        Test needs improving when the old API goes to just check that everything loads okay
+        """
+        all_algs = AlgorithmFactory.getRegisteredAlgorithms(True)
+        self.assertTrue('SNSPowderReduction' in all_algs)
+        self.assertTrue('Squares' in all_algs)
+
     def test_version_number_equals_2(self):
-        self.assertEquals(simpleapi.apiVersion(), 2)
+        self.assertEqual(simpleapi.apiVersion(), 2)
 
     def test_module_dict_seems_to_be_correct_size(self):
         # Check that the module has at least the same number
@@ -36,7 +44,7 @@ class SimpleAPITest(unittest.TestCase):
         expected_doc = """Rebins data with new X bin boundaries. For EventWorkspaces, you can very quickly rebin in-place by keeping the same output name and PreserveEvents=true.\n\nProperty descriptions: \n\nInputWorkspace(Input:req) *MatrixWorkspace*       Workspace containing the input data\n\nOutputWorkspace(Output:req) *MatrixWorkspace*       The name to give the output workspace\n\nParams(Input:req) *dbl list*       A comma separated list of first bin boundary, width, last bin boundary. Optionally this can be followed by a comma and more widths and last boundary pairs. Optionally this can also be a single number, which is the bin width. In this case, the boundary of binning will be determined by minimum and maximum TOF values among all events, or previous binning boundary, in case of event Workspace, or non-event Workspace, respectively. Negative width values indicate logarithmic binning. \n\nPreserveEvents(Input) *boolean*       Keep the output workspace as an EventWorkspace, if the input has events. If the input and output EventWorkspace names are the same, only the X bins are set, which is very quick. If false, then the workspace gets converted to a Workspace2D histogram.\n\nFullBinsOnly(Input) *boolean*       Omit the final bin if it's width is smaller than the step size\n\nIgnoreBinErrors(Input) *boolean*       Ignore errors related to zero/negative bin widths in input/output workspaces. When ignored, the signal and errors are set to zero\n"""
         doc = simpleapi.rebin.__doc__
         self.assertTrue(len(doc) > 0)
-        self.assertEquals(doc, expected_doc)
+        self.assertEqual(doc, expected_doc)
 
     def test_function_call_executes_correct_algorithm_when_passed_correct_args(self):
         wsname = 'test_function_call_executes_correct_algorithm_when_passed_correct_args'
@@ -164,7 +172,7 @@ class SimpleAPITest(unittest.TestCase):
         self.assertTrue( wsname_box in mtd )
 
         self.assertTrue( isinstance(query, tuple) )
-        self.assertEquals( 2, len(query) )
+        self.assertEqual( 2, len(query) )
 
         self.assertTrue( isinstance(query[0], ITableWorkspace) )
         self.assertTrue( isinstance(query[1], ITableWorkspace) )
@@ -346,8 +354,8 @@ class SimpleAPITest(unittest.TestCase):
 
     def _is_initialized_test(self, alg, version, expected_class, expected_child):
         self.assertTrue(alg.isInitialized())
-        self.assertEquals(expected_child,alg.isChild())
-        self.assertEquals(alg.version(), version)
+        self.assertEqual(expected_child,alg.isChild())
+        self.assertEqual(alg.version(), version)
         self.assertTrue(isinstance(alg, expected_class))
 
     def test_validate_inputs_with_errors_stops_algorithm(self):
diff --git a/Framework/PythonInterface/test/python/mantid/api/AlgorithmFactoryTest.py b/Framework/PythonInterface/test/python/mantid/api/AlgorithmFactoryTest.py
index 36307e8d7a9f3210090020980db0d9320368c2e7..ebe3b38b33080f4d9f37956be8c39f6eb48481c6 100644
--- a/Framework/PythonInterface/test/python/mantid/api/AlgorithmFactoryTest.py
+++ b/Framework/PythonInterface/test/python/mantid/api/AlgorithmFactoryTest.py
@@ -9,20 +9,36 @@ from __future__ import (absolute_import, division, print_function)
 import unittest
 import testhelpers
 
-from mantid.api import AlgorithmFactory, PythonAlgorithm
+from mantid.api import AlgorithmFactory, FrameworkManagerImpl, PythonAlgorithm
+
 
 class IsAnAlgorithm(PythonAlgorithm):
     def PyInit(self):
         pass
 
+
 class NotAnAlgorithm(object):
     pass
 
+
 class AlgorithmFactoryTest(unittest.TestCase):
 
+    def setUp(self):
+        FrameworkManagerImpl.Instance()
+
     def test_get_algorithm_factory_does_not_return_None(self):
         self.assertTrue(AlgorithmFactory is not None )
 
+    def test_getDescriptors(self):
+
+        descriptors = AlgorithmFactory.getDescriptors(True)
+        self.assertGreater(len(descriptors), 0)
+        d = descriptors[0]
+        self.assertTrue(hasattr(d, 'name'))
+        self.assertTrue(hasattr(d, 'alias'))
+        self.assertTrue(hasattr(d, 'category'))
+        self.assertTrue(hasattr(d, 'version'))
+
     def test_exists_returns_correct_value_for_given_args(self):
         self.assertTrue(AlgorithmFactory.exists('ConvertUnits')) #any version
         self.assertTrue(AlgorithmFactory.exists('ConvertUnits', 1)) #any version
diff --git a/Framework/PythonInterface/test/python/mantid/api/AlgorithmManagerTest.py b/Framework/PythonInterface/test/python/mantid/api/AlgorithmManagerTest.py
index 2375eadc1e8b9b67f89a33e60008e9c24e0f4282..0808a286ed4c557dd1690845fbb6af29c996dca9 100644
--- a/Framework/PythonInterface/test/python/mantid/api/AlgorithmManagerTest.py
+++ b/Framework/PythonInterface/test/python/mantid/api/AlgorithmManagerTest.py
@@ -8,12 +8,17 @@ from __future__ import (absolute_import, division, print_function)
 
 import unittest
 import testhelpers
-from mantid.api import (AlgorithmManager, IAlgorithm, Algorithm, AlgorithmProxy)
+from mantid.api import (AlgorithmManager, Algorithm, AlgorithmProxy,
+                        FrameworkManagerImpl, IAlgorithm)
 
-import sys
 
 class AlgorithmManagerTest(unittest.TestCase):
 
+    @classmethod
+    def setUpClass(cls):
+        # Load the plugins
+        FrameworkManagerImpl.Instance()
+
     def test_create_default_version(self):
         alg = testhelpers.assertRaisesNothing(self, AlgorithmManager.create, "ConvertUnits")
         # Tests
diff --git a/Framework/PythonInterface/test/python/mantid/api/AlgorithmPropertyTest.py b/Framework/PythonInterface/test/python/mantid/api/AlgorithmPropertyTest.py
index f58c87814a5935008a48accb12f5586b58fbc76a..b8da42e5edb8ec970ddc00de6f41a164efce9691 100644
--- a/Framework/PythonInterface/test/python/mantid/api/AlgorithmPropertyTest.py
+++ b/Framework/PythonInterface/test/python/mantid/api/AlgorithmPropertyTest.py
@@ -7,9 +7,10 @@
 from __future__ import (absolute_import, division, print_function)
 
 import unittest
-from mantid.api import AlgorithmProperty, IAlgorithm
+from mantid.api import AlgorithmProperty, FrameworkManagerImpl, IAlgorithm
 from mantid.kernel import Direction
 
+
 class AlgorithmPropertyTest(unittest.TestCase):
 
     def test_construction_with_name_produces_input_property(self):
@@ -18,12 +19,14 @@ class AlgorithmPropertyTest(unittest.TestCase):
         self.assertEquals(Direction.Input, prop.direction)
 
     def test_value_method_returns_an_algorithm_type(self):
+        # load plugins to register CreateWorkspace
+        FrameworkManagerImpl.Instance()
         prop = AlgorithmProperty("TestProperty")
-        prop.valueAsStr = '{"name":"CreateWorkspace","paramters":{"OutputWorkspace":"ws","DataY":"1","DataX":"1","NSpec":"1"}}'
-
+        prop.valueAsStr = '{"name": "CreateWorkspace",' \
+                          '"parameters": {"OutputWorkspace": "ws", "DataY": "1", "DataX": "1","NSpec": "1"}}'
         alg = prop.value
-        self.assertTrue(isinstance(alg,IAlgorithm))
-        self.assertEquals("CreateWorkspace",alg.name())
+        self.assertTrue(isinstance(alg, IAlgorithm))
+        self.assertEqual("CreateWorkspace", alg.name())
 
 
 if __name__ == '__main__':
diff --git a/Framework/PythonInterface/test/python/mantid/api/AlgorithmTest.py b/Framework/PythonInterface/test/python/mantid/api/AlgorithmTest.py
index 2b737fc4c399d0afa59bf4b941ae6557dcf71062..85fca4c4e4aa6352b73b861adf2b56e16b606ec7 100644
--- a/Framework/PythonInterface/test/python/mantid/api/AlgorithmTest.py
+++ b/Framework/PythonInterface/test/python/mantid/api/AlgorithmTest.py
@@ -9,7 +9,7 @@ import six
 
 import unittest
 import json
-from mantid.api import AlgorithmID, AlgorithmManager
+from mantid.api import AlgorithmID, AlgorithmManager, FrameworkManagerImpl
 from testhelpers import run_algorithm
 
 class AlgorithmTest(unittest.TestCase):
@@ -17,6 +17,7 @@ class AlgorithmTest(unittest.TestCase):
     _load = None
 
     def setUp(self):
+        FrameworkManagerImpl.Instance()
         if self._load is None:
             self.__class__._load = AlgorithmManager.createUnmanaged('Load')
             self._load.initialize()
diff --git a/Framework/PythonInterface/test/python/mantid/api/AnalysisDataServiceTest.py b/Framework/PythonInterface/test/python/mantid/api/AnalysisDataServiceTest.py
index b87a6175d480b663e06ba347e206e57d9d4248d7..e2dc256c8c2126e608e833eb8ecdae64645adb3c 100644
--- a/Framework/PythonInterface/test/python/mantid/api/AnalysisDataServiceTest.py
+++ b/Framework/PythonInterface/test/python/mantid/api/AnalysisDataServiceTest.py
@@ -8,11 +8,17 @@ from __future__ import (absolute_import, division, print_function)
 
 import unittest
 from testhelpers import run_algorithm
-from mantid.api import AnalysisDataService, AnalysisDataServiceImpl, MatrixWorkspace, Workspace
+from mantid.api import (AnalysisDataService, AnalysisDataServiceImpl,
+                        FrameworkManagerImpl, MatrixWorkspace, Workspace)
 from mantid import mtd
 
+
 class AnalysisDataServiceTest(unittest.TestCase):
 
+    @classmethod
+    def setUpClass(cls):
+        FrameworkManagerImpl.Instance()
+
     def tearDown(self):
       AnalysisDataService.Instance().clear()
 
diff --git a/Framework/PythonInterface/test/python/mantid/api/CompositeFunctionTest.py b/Framework/PythonInterface/test/python/mantid/api/CompositeFunctionTest.py
index 6c79a5eecaf224b12f4cb196d2bfc67c2c1d4363..965d801071ef9e483119471c179062ce7fa4952b 100644
--- a/Framework/PythonInterface/test/python/mantid/api/CompositeFunctionTest.py
+++ b/Framework/PythonInterface/test/python/mantid/api/CompositeFunctionTest.py
@@ -7,12 +7,15 @@
 from __future__ import (absolute_import, division, print_function)
 
 import unittest
-from mantid.api import FunctionFactory, CompositeFunction, IFunction1D
-import numpy as np
+from mantid.api import FrameworkManagerImpl, FunctionFactory, CompositeFunction, IFunction1D
 
 
 class CompositeFunctionTest(unittest.TestCase):
 
+    @classmethod
+    def setUpClass(cls):
+        FrameworkManagerImpl.Instance()
+
     def test_instance_can_be_created_standalone(self):
         func = CompositeFunction()
         self.assertTrue(isinstance(func, CompositeFunction))
@@ -51,7 +54,7 @@ class CompositeFunctionTest(unittest.TestCase):
         func[1].setParameter('A0', 20.0)
         self.assertEqual(func.getParameterValue('f0.A0'), 10.0)
         self.assertEqual(func.getParameterValue('f1.A0'), 20.0)
-        
+
     def test_nested_functions(self):
         s = 'name=FlatBackground,A0=1;(name=FlatBackground,A0=2;name=FlatBackground,A0=3)'
         func = FunctionFactory.createInitialized(s)
diff --git a/Framework/PythonInterface/test/python/mantid/api/DeprecatedAlgorithmCheckerTest.py b/Framework/PythonInterface/test/python/mantid/api/DeprecatedAlgorithmCheckerTest.py
index 86cd6d8a030a7a747ed7e6db78022f9df85d1ab8..0c0fe4d216ab7a49326b7073b6567bc66b3f9622 100644
--- a/Framework/PythonInterface/test/python/mantid/api/DeprecatedAlgorithmCheckerTest.py
+++ b/Framework/PythonInterface/test/python/mantid/api/DeprecatedAlgorithmCheckerTest.py
@@ -7,10 +7,15 @@
 from __future__ import (absolute_import, division, print_function)
 
 import unittest
-from mantid.api import DeprecatedAlgorithmChecker
+from mantid.api import DeprecatedAlgorithmChecker, FrameworkManagerImpl
+
 
 class DeprecatedAlgorithmCheckerTest(unittest.TestCase):
 
+    @classmethod
+    def setUpClass(cls):
+        FrameworkManagerImpl.Instance()
+
     def test_constructor_throws_for_non_existant_algorithm(self):
         self.assertRaises(RuntimeError, DeprecatedAlgorithmChecker, "A_Very_Silly_Alg_Name",-1)
 
diff --git a/Framework/PythonInterface/test/python/mantid/api/FilePropertyTest.py b/Framework/PythonInterface/test/python/mantid/api/FilePropertyTest.py
index c534a6e1bb5bf36b886a55048fb8b8484e527954..47be4a61adcceda8bfc8c4b1450ca380260a4d31 100644
--- a/Framework/PythonInterface/test/python/mantid/api/FilePropertyTest.py
+++ b/Framework/PythonInterface/test/python/mantid/api/FilePropertyTest.py
@@ -7,11 +7,16 @@
 from __future__ import (absolute_import, division, print_function)
 
 import unittest
-from mantid.api import FileProperty, FileAction, AlgorithmManager
+from mantid.api import AlgorithmManager, FileProperty, FileAction, FrameworkManagerImpl
 from mantid.kernel import Direction
 
+
 class FilePropertyTest(unittest.TestCase):
 
+    @classmethod
+    def setUpClass(cls):
+        FrameworkManagerImpl.Instance()
+
     def test_constructor_with_name_and_default_and_action(self):
         prop = FileProperty("LoadProperty", "", FileAction.Load)
         self.assertNotEquals("", prop.isValid)
diff --git a/Framework/PythonInterface/test/python/mantid/api/FunctionFactoryTest.py b/Framework/PythonInterface/test/python/mantid/api/FunctionFactoryTest.py
index c14317e485e31f08ee6852247902c1517bd9e353..c9f16486f6d46080bbb9904aa2d321e0a19d8f53 100644
--- a/Framework/PythonInterface/test/python/mantid/api/FunctionFactoryTest.py
+++ b/Framework/PythonInterface/test/python/mantid/api/FunctionFactoryTest.py
@@ -8,7 +8,7 @@ from __future__ import (absolute_import, division, print_function)
 
 import unittest
 
-from mantid.api import IFunction1D, FunctionFactory
+from mantid.api import FrameworkManagerImpl, IFunction1D, FunctionFactory
 
 
 class TestFunctionNoAttrs(IFunction1D):
@@ -38,6 +38,10 @@ class TestFunctionCorrectForm(IFunction1D):
 
 class FunctionFactoryTest(unittest.TestCase):
 
+    @classmethod
+    def setUpClass(cls):
+        FrameworkManagerImpl.Instance()
+
     def test_get_function_factory_does_not_return_None(self):
         self.assertTrue(FunctionFactory is not None)
 
diff --git a/Framework/PythonInterface/test/python/mantid/api/FunctionPropertyTest.py b/Framework/PythonInterface/test/python/mantid/api/FunctionPropertyTest.py
index eaffcdb37cfc0922103afa8ac684d41390867624..e73376c8f79bcae6c3684a32555e580956289ad0 100644
--- a/Framework/PythonInterface/test/python/mantid/api/FunctionPropertyTest.py
+++ b/Framework/PythonInterface/test/python/mantid/api/FunctionPropertyTest.py
@@ -6,14 +6,14 @@
 # SPDX - License - Identifier: GPL - 3.0 +
 from __future__ import (absolute_import, division, print_function)
 
-from mantid.api import FunctionProperty,PythonAlgorithm, IFunction
+from mantid.api import FrameworkManagerImpl, FunctionProperty, PythonAlgorithm, IFunction
 from testhelpers import assertRaisesNothing
 import unittest
 import math
 
+
 class FunctionPropertyTest(unittest.TestCase):
 
-    #------------------------------------------------------------
     class TestFunctionPropAlg(PythonAlgorithm):
         def PyInit(self):
             self.declareProperty(FunctionProperty("fun"))
@@ -28,9 +28,11 @@ class FunctionPropertyTest(unittest.TestCase):
             height=func.getParamValue(0)
             if math.fabs(height - 1.0) > 1e-12:
                 raise RuntimeError("Height does not have the expected value")
-    #------------------------------------------------------------
 
-#---- Success cases ----
+    @classmethod
+    def setUpClass(cls):
+        FrameworkManagerImpl.Instance()
+
     def test_constructor_succeeds_with_non_empty_string_name(self):
         assertRaisesNothing(self, FunctionProperty, "Function")
 
@@ -50,11 +52,11 @@ class FunctionPropertyTest(unittest.TestCase):
         alg.setRethrows(True)
         assertRaisesNothing(self, alg.execute)
 
-#---- Error cases ----
     def test_invalid_string_value_gives_function_object_as_value(self):
         alg=self.TestFunctionPropAlg()
         alg.initialize()
         self.assertRaises(ValueError, alg.setProperty, "fun", "blah")
 
+
 if __name__ == '__main__':
     unittest.main()
diff --git a/Framework/PythonInterface/test/python/mantid/api/PythonAlgorithmTraitsTest.py b/Framework/PythonInterface/test/python/mantid/api/PythonAlgorithmTraitsTest.py
index 4a4e1ed7b147e332b0cec6f2bae8a500babc3f76..b7d6ad8e61c11f2e660e774ed467904581e5b13e 100644
--- a/Framework/PythonInterface/test/python/mantid/api/PythonAlgorithmTraitsTest.py
+++ b/Framework/PythonInterface/test/python/mantid/api/PythonAlgorithmTraitsTest.py
@@ -11,9 +11,7 @@ from __future__ import (absolute_import, division, print_function)
 
 import unittest
 import testhelpers
-import types
 
-from mantid.kernel import Direction
 from mantid.api import (PythonAlgorithm, AlgorithmProxy, Algorithm, IAlgorithm,
                         AlgorithmManager, AlgorithmFactory)
 
@@ -133,5 +131,6 @@ class PythonAlgorithmTest(unittest.TestCase):
         base_running_attr = getattr(IAlgorithm, "isRunning")
         self.assertRaises(RuntimeError, base_running_attr, alg)
 
+
 if __name__ == '__main__':
     unittest.main()
diff --git a/Framework/PythonInterface/test/python/mantid/geometry/CSGObjectTest.py b/Framework/PythonInterface/test/python/mantid/geometry/CSGObjectTest.py
index e4ccc0577ef9fdd3ecff65bdc31bc77948a12538..0c76090b59a884566a804e54175cb0e2f9674273 100644
--- a/Framework/PythonInterface/test/python/mantid/geometry/CSGObjectTest.py
+++ b/Framework/PythonInterface/test/python/mantid/geometry/CSGObjectTest.py
@@ -9,6 +9,7 @@ from __future__ import (absolute_import, division, print_function)
 import unittest
 from mantid.geometry import BoundingBox, CSGObject
 
+
 class CSGObjectTest(unittest.TestCase):
 
     _testws = None
diff --git a/Framework/PythonInterface/test/python/mantid/kernel/PropertyWithValueTest.py b/Framework/PythonInterface/test/python/mantid/kernel/PropertyWithValueTest.py
index 01d337ab14be65e7ba9abf734d47a09ed4f42c3f..3836be11ef458f84b3e804d3d0e14126f2271ea7 100644
--- a/Framework/PythonInterface/test/python/mantid/kernel/PropertyWithValueTest.py
+++ b/Framework/PythonInterface/test/python/mantid/kernel/PropertyWithValueTest.py
@@ -8,10 +8,11 @@ from __future__ import (absolute_import, division, print_function)
 
 import unittest
 from mantid.api import AlgorithmManager, MatrixWorkspace
-from testhelpers import run_algorithm
+from testhelpers import create_algorithm, run_algorithm
 import numpy as np
 import sys
 
+
 class PropertyWithValueTest(unittest.TestCase):
 
     # Integration algorithm handle
@@ -21,10 +22,10 @@ class PropertyWithValueTest(unittest.TestCase):
 
     def setUp(self):
         if self._integration is None:
-            self.__class__._integration = AlgorithmManager.createUnmanaged("Integration")
+            self.__class__._integration = create_algorithm("Integration")
             self.__class__._integration.initialize()
         if self._mask_dets is None:
-            self.__class__._mask_dets = AlgorithmManager.createUnmanaged("MaskDetectors")
+            self.__class__._mask_dets = create_algorithm("MaskDetectors")
             self.__class__._mask_dets.initialize()
 
     def test_value_setting_as_string_gives_expected_value_for_correct_type(self):
@@ -166,5 +167,6 @@ class PropertyWithValueTest(unittest.TestCase):
     def test_set_property_of_vector_int_succeeds_with_numpy_array_of_int_type(self):
         self._do_vector_int_numpy_test('WorkspaceIndexList')
 
+
 if __name__ == '__main__':
     unittest.main()
diff --git a/Framework/PythonInterface/test/python/mantid/kernel/TimeSeriesPropertyTest.py b/Framework/PythonInterface/test/python/mantid/kernel/TimeSeriesPropertyTest.py
index 419971c84d29f50d49930bd3f6befac691336bad..155c8d88de74bdb1ce86201ad63a74120e018ee3 100644
--- a/Framework/PythonInterface/test/python/mantid/kernel/TimeSeriesPropertyTest.py
+++ b/Framework/PythonInterface/test/python/mantid/kernel/TimeSeriesPropertyTest.py
@@ -9,7 +9,6 @@ from __future__ import (absolute_import, division, print_function)
 import unittest
 import numpy as np
 
-import mantid
 from mantid.kernel import (DateAndTime, BoolTimeSeriesProperty, FloatTimeSeriesProperty, Int64TimeSeriesProperty,
                            StringTimeSeriesProperty)
 from testhelpers import run_algorithm
@@ -64,33 +63,33 @@ class TimeSeriesPropertyTest(unittest.TestCase):
     def test_time_series_double_can_be_extracted(self):
         log_series = self._test_ws.getRun()["TEMP1"]
         self._check_has_time_series_attributes(log_series)
-        self.assertEquals(log_series.size(), self._ntemp)
+        self.assertEqual(log_series.size(), self._ntemp)
         self.assertAlmostEqual(log_series.nthValue(0), -0.00161)
         # Check the dtype return value
-        self.assertEquals(log_series.dtype(), "f")
+        self.assertEqual(log_series.dtype(), "f")
 
     def test_time_series_int_can_be_extracted(self):
         log_series = self._test_ws.getRun()["raw_frames"]
         self._check_has_time_series_attributes(log_series)
-        self.assertEquals(log_series.size(), self._nframes)
-        self.assertEquals(log_series.nthValue(1), 1436)
+        self.assertEqual(log_series.size(), self._nframes)
+        self.assertEqual(log_series.nthValue(1), 1436)
         # Check the dtype return value
-        self.assertEquals(log_series.dtype(), "i")
+        self.assertEqual(log_series.dtype(), "i")
 
     def test_time_series_string_can_be_extracted(self):
         log_series = self._test_ws.getRun()["icp_event"]
         self._check_has_time_series_attributes(log_series, list)
-        self.assertEquals(log_series.size(), 4)
-        self.assertEquals(log_series.nthValue(0).strip(), 'CHANGE_PERIOD 1')
+        self.assertEqual(log_series.size(), 4)
+        self.assertEqual(log_series.nthValue(0).strip(), 'CHANGE_PERIOD 1')
         # Check the dtype return value
-        self.assertEquals(log_series.dtype(), "S61")
+        self.assertEqual(log_series.dtype(), "S61")
 
     def test_time_series_bool_can_be_extracted(self):
         log_series = self._test_ws.getRun()["period 1"]
         self._check_has_time_series_attributes(log_series)
-        self.assertEquals(log_series.size(), 1)
+        self.assertEqual(log_series.size(), 1)
         # Check the dtype return value
-        self.assertEquals(log_series.dtype(), "b")
+        self.assertEqual(log_series.dtype(), "b")
 
     def _check_has_time_series_attributes(self, log, values_type=np.ndarray):
         self.assertTrue(hasattr(log, "value"))
@@ -99,7 +98,7 @@ class TimeSeriesPropertyTest(unittest.TestCase):
 
         values = log.value
         self.assertTrue(isinstance(values, values_type))
-        self.assertEquals(log.size(), len(values))
+        self.assertEqual(log.size(), len(values))
 
         # check the statistics
         stats = log.getStatistics()
diff --git a/Framework/PythonInterface/test/python/mantid/kernel/UsageServiceTest.py b/Framework/PythonInterface/test/python/mantid/kernel/UsageServiceTest.py
index 78a4ca2fed938009714235ddb24501168259b69b..66662d4261314f9b30d9125ec8c91045f5a782f0 100644
--- a/Framework/PythonInterface/test/python/mantid/kernel/UsageServiceTest.py
+++ b/Framework/PythonInterface/test/python/mantid/kernel/UsageServiceTest.py
@@ -10,6 +10,7 @@ import unittest
 
 from mantid.kernel import (UsageService, UsageServiceImpl)
 
+
 class UsageServiceTest(unittest.TestCase):
 
     def test_singleton_returns_instance_of_UsageService(self):
@@ -24,11 +25,11 @@ class UsageServiceTest(unittest.TestCase):
         self.assertEquals(UsageService.isEnabled(),False)
 
     def test_getSetApplication(self):
-        self.assertEquals(UsageService.getApplication(),"python")
-        UsageService.setApplication("python unit tests")
-        self.assertEquals(UsageService.getApplication(),"python unit tests")
-        UsageService.setApplication("python")
-        self.assertEquals(UsageService.getApplication(),"python")
+        self.assertEquals(UsageService.getApplicationName(), "python")
+        UsageService.setApplicationName("python unit tests")
+        self.assertEquals(UsageService.getApplicationName(), "python unit tests")
+        UsageService.setApplicationName("python")
+        self.assertEquals(UsageService.getApplicationName(), "python")
 
     def test_setInterval(self):
         UsageService.setEnabled(False)
@@ -42,7 +43,7 @@ class UsageServiceTest(unittest.TestCase):
     def test_registerFeatureUsage(self):
         UsageService.setEnabled(False)
         #this will do nothing as it is disabled
-        UsageService.registerFeatureUsage("Algorithm","Test.v1",True)
+        UsageService.registerFeatureUsage("Algorithm", "Test.v1", True)
 
 
     def test_Flush(self):
@@ -53,5 +54,6 @@ class UsageServiceTest(unittest.TestCase):
     def test_Shutdown(self):
         UsageService.shutdown()
 
+
 if __name__ == '__main__':
     unittest.main()
diff --git a/Framework/PythonInterface/test/python/plugins/algorithms/AbinsAdvancedParametersTest.py b/Framework/PythonInterface/test/python/plugins/algorithms/AbinsAdvancedParametersTest.py
index 7bf32aa6d278bd98e5cdabc45d2c68f32c67676d..7780ab00899e7f19b008ea85f75077469dd01b09 100644
--- a/Framework/PythonInterface/test/python/plugins/algorithms/AbinsAdvancedParametersTest.py
+++ b/Framework/PythonInterface/test/python/plugins/algorithms/AbinsAdvancedParametersTest.py
@@ -6,8 +6,7 @@
 # SPDX - License - Identifier: GPL - 3.0 +
 from __future__ import (absolute_import, division, print_function)
 import unittest
-from mantid.simpleapi import mtd
-from mantid.simpleapi import Abins, DeleteWorkspace
+from mantid.simpleapi import Abins, DeleteWorkspace, mtd
 
 from AbinsModules import AbinsParameters, AbinsTestHelpers
 
diff --git a/Framework/PythonInterface/test/python/plugins/algorithms/CMakeLists.txt b/Framework/PythonInterface/test/python/plugins/algorithms/CMakeLists.txt
index e43ec3186865819907ed935a97b3f8f9d11865a7..b4bc9506769944334f4c5ee668de905f41278ac0 100644
--- a/Framework/PythonInterface/test/python/plugins/algorithms/CMakeLists.txt
+++ b/Framework/PythonInterface/test/python/plugins/algorithms/CMakeLists.txt
@@ -118,4 +118,5 @@ set ( TEST_PY_FILES
 check_tests_valid ( ${CMAKE_CURRENT_SOURCE_DIR} ${TEST_PY_FILES} )
 
 # Prefix for test name=PythonAlgorithms
+set ( PYUNITTEST_PYTHONPATH_EXTRA ${PYTHONINTERFACE_PLUGINS_DIR}/algorithms )
 pyunittest_add_test ( ${CMAKE_CURRENT_SOURCE_DIR} python.algorithms ${TEST_PY_FILES} )
diff --git a/Framework/PythonInterface/test/python/plugins/algorithms/CheckForSampleLogsTest.py b/Framework/PythonInterface/test/python/plugins/algorithms/CheckForSampleLogsTest.py
index 9f5ef3e36519cf4309de16acda6a6fca3c22d087..3fe1a792cd56873ede46c078ed5ed69e421f2a50 100644
--- a/Framework/PythonInterface/test/python/plugins/algorithms/CheckForSampleLogsTest.py
+++ b/Framework/PythonInterface/test/python/plugins/algorithms/CheckForSampleLogsTest.py
@@ -6,19 +6,20 @@
 # SPDX - License - Identifier: GPL - 3.0 +
 from __future__ import (absolute_import, division, print_function)
 
-import unittest,os
-import mantid
+import unittest
+from mantid import simpleapi
 
 
 class CheckForSampleLogsTest(unittest.TestCase):
     def test_simple(self):
-    	w=mantid.simpleapi.Load('CNCS_7860_event.nxs')
-    	result=mantid.simpleapi.CheckForSampleLogs(w)
-    	self.assertEquals(result,'')
-    	result=mantid.simpleapi.CheckForSampleLogs(w,'Phase1')
-    	self.assertEquals(result,'')
-    	result=mantid.simpleapi.CheckForSampleLogs(w,'Phrase1')
-    	self.assertNotEquals(result,'')
+        w = simpleapi.Load('CNCS_7860_event.nxs')
+        result = simpleapi.CheckForSampleLogs(w)
+        self.assertEquals(result, '')
+        result = simpleapi.CheckForSampleLogs(w, 'Phase1')
+        self.assertEquals(result, '')
+        result = simpleapi.CheckForSampleLogs(w, 'Phrase1')
+        self.assertNotEquals(result, '')
+
 
 if __name__=="__main__":
     unittest.main()
diff --git a/Framework/PythonInterface/test/python/plugins/algorithms/DakotaChiSquaredTest.py b/Framework/PythonInterface/test/python/plugins/algorithms/DakotaChiSquaredTest.py
index 6af45362f2230b624d01c8c38861921fbbb755d9..b0147653b82d42102fbe1af8e869c91b9cf5c247 100644
--- a/Framework/PythonInterface/test/python/plugins/algorithms/DakotaChiSquaredTest.py
+++ b/Framework/PythonInterface/test/python/plugins/algorithms/DakotaChiSquaredTest.py
@@ -6,105 +6,105 @@
 # SPDX - License - Identifier: GPL - 3.0 +
 from __future__ import (absolute_import, division, print_function)
 
-import unittest,os
-import mantid
+import unittest, os
+from mantid import AnalysisDataServiceImpl, config, simpleapi
 
 
 class DakotaChiSquaredTest(unittest.TestCase):
 
     def makeFiles(self):
-    	mantid.simpleapi.CreateWorkspace(OutputWorkspace='data',DataX='1,2,3,4,5',DataY='1,0,1,4,4',DataE='1,0,1,2,2')
-    	mantid.simpleapi.CreateWorkspace(OutputWorkspace='sim',DataX='1,2,3,4,5',DataY='1,1,1,1,1',DataE='0,0,0,0,0')
-    	mantid.simpleapi.CreateWorkspace(OutputWorkspace='simwrong',DataX='1,2,3,4',DataY='1,1,1,1',DataE='0,0,0,0')
+        simpleapi.CreateWorkspace(OutputWorkspace='data', DataX='1,2,3,4,5', DataY='1,0,1,4,4', DataE='1,0,1,2,2')
+        simpleapi.CreateWorkspace(OutputWorkspace='sim', DataX='1,2,3,4,5', DataY='1,1,1,1,1', DataE='0,0,0,0,0')
+        simpleapi.CreateWorkspace(OutputWorkspace='simwrong', DataX='1,2,3,4', DataY='1,1,1,1', DataE='0,0,0,0')
 
-    	self.datafile=os.path.join(mantid.config.getString('defaultsave.directory'),'DakotaChiSquared_data.nxs')
-    	self.simfile=os.path.join(mantid.config.getString('defaultsave.directory'),'DakotaChiSquared_sim.nxs')
-    	self.simwrongfile=os.path.join(mantid.config.getString('defaultsave.directory'),'DakotaChiSquared_simwrong.nxs')
-    	self.chifile=os.path.join(mantid.config.getString('defaultsave.directory'),'DakotaChiSquared_chi.txt')
+        self.datafile = os.path.join(config.getString('defaultsave.directory'), 'DakotaChiSquared_data.nxs')
+        self.simfile = os.path.join(config.getString('defaultsave.directory'), 'DakotaChiSquared_sim.nxs')
+        self.simwrongfile = os.path.join(config.getString('defaultsave.directory'), 'DakotaChiSquared_simwrong.nxs')
+        self.chifile = os.path.join(config.getString('defaultsave.directory'), 'DakotaChiSquared_chi.txt')
 
-    	mantid.simpleapi.SaveNexus('data',self.datafile)
-    	mantid.simpleapi.SaveNexus('sim',self.simfile)
-    	mantid.simpleapi.SaveNexus('simwrong',self.simwrongfile)
-
-    	mantid.api.AnalysisDataService.remove("data")
-    	mantid.api.AnalysisDataService.remove("sim")
-    	mantid.api.AnalysisDataService.remove("simwrong")
+        simpleapi.SaveNexus('data', self.datafile)
+        simpleapi.SaveNexus('sim', self.simfile)
+        simpleapi.SaveNexus('simwrong', self.simwrongfile)
 
+        ads = AnalysisDataServiceImpl.Instance()
+        ads.remove("data")
+        ads.remove("sim")
+        ads.remove("simwrong")
 
     def cleanup(self):
-    	if os.path.exists(self.datafile):
-                	os.remove(self.datafile)
-    	if os.path.exists(self.simfile):
-                	os.remove(self.simfile)
-    	if os.path.exists(self.simwrongfile):
-                	os.remove(self.simwrongfile)
-    	if os.path.exists(self.chifile):
-                	os.remove(self.chifile)
+        if os.path.exists(self.datafile):
+            os.remove(self.datafile)
+        if os.path.exists(self.simfile):
+            os.remove(self.simfile)
+        if os.path.exists(self.simwrongfile):
+            os.remove(self.simwrongfile)
+        if os.path.exists(self.chifile):
+            os.remove(self.chifile)
 
     def test_wrongType(self):
-    	self.makeFiles()
-    	try:
-    		mantid.simpleapi.DakotaChiSquared(self.datafile,'CNCS_7860_event.nxs',self.chifile)
-    	except RuntimeError as e:
-    		self.assertNotEquals(str(e).find('Wrong workspace type for calculated file'),-1)
-    	except:
-    		assert False, "Raised the wrong exception type"
-    	else:
-    		assert False, "Didn't raise any exception"
-    	try:
-    		mantid.simpleapi.DakotaChiSquared('CNCS_7860_event.nxs',self.simfile,self.chifile)
-    	except RuntimeError as e:
-    		self.assertNotEquals(str(e).find('Wrong workspace type for data file'),-1)
-    	except:
-    		assert False, "Raised the wrong exception type"
-    	else:
-    		assert False, "Didn't raise any exception"
-    	self.cleanup()
-
+        self.makeFiles()
+        try:
+            simpleapi.DakotaChiSquared(self.datafile, 'CNCS_7860_event.nxs', self.chifile)
+        except RuntimeError as e:
+            self.assertNotEquals(str(e).find('Wrong workspace type for calculated file'), -1)
+        except:
+            assert False, "Raised the wrong exception type"
+        else:
+            assert False, "Didn't raise any exception"
+        try:
+            simpleapi.DakotaChiSquared('CNCS_7860_event.nxs', self.simfile, self.chifile)
+        except RuntimeError as e:
+            self.assertNotEquals(str(e).find('Wrong workspace type for data file'), -1)
+        except:
+            assert False, "Raised the wrong exception type"
+        else:
+            assert False, "Didn't raise any exception"
+        self.cleanup()
 
     def test_wrongSize(self):
-    	self.makeFiles()
-    	try:
-    		mantid.simpleapi.DakotaChiSquared(self.datafile,self.simwrongfile,self.chifile)
-    	except RuntimeError as e:
-    		self.assertNotEquals(str(e).find('The file sizes are different'),-1)
-    	except:
-    		assert False, "Raised the wrong exception type"
-    	else:
-    		assert False, "Didn't raise any exception"
-    	self.cleanup()
-
+        self.makeFiles()
+        try:
+            simpleapi.DakotaChiSquared(self.datafile, self.simwrongfile, self.chifile)
+        except RuntimeError as e:
+            self.assertNotEquals(str(e).find('The file sizes are different'), -1)
+        except:
+            assert False, "Raised the wrong exception type"
+        else:
+            assert False, "Didn't raise any exception"
+        self.cleanup()
 
     def test_value(self):
-    	self.makeFiles()
-    	try:
-    		mantid.simpleapi.DakotaChiSquared(self.datafile,self.simfile,self.chifile)
-    		f = open(self.chifile,'r')
-    		chistr=f.read()
-    		self.assertEquals(chistr,'4.5 obj_fn\n')
-    		f.close()
-    	except:
-    		assert False, "Raised an exception"
-    	self.cleanup()
+        self.makeFiles()
+        try:
+            simpleapi.DakotaChiSquared(self.datafile, self.simfile, self.chifile)
+            f = open(self.chifile, 'r')
+            chistr = f.read()
+            self.assertEquals(chistr, '4.5 obj_fn\n')
+            f.close()
+        except:
+            assert False, "Raised an exception"
+        self.cleanup()
 
     def test_output(self):
-    	self.makeFiles()
-    	try:
-    		alg=mantid.simpleapi.DakotaChiSquared(self.datafile,self.simfile,self.chifile)
-    		self.assertEquals(len(alg),2)
-    		self.assertEquals(alg[0],4.5)
-    		self.assertEquals(alg[1].name(),"alg")
-    		self.assertEquals(alg[1].blocksize(),5)
-    		self.assertEquals(alg[1].getNumberHistograms(),1)
-    		self.assertEquals(alg[1].dataY(0)[3],1.5)
-    		mantid.api.AnalysisDataService.remove("alg")
-    		alg1=mantid.simpleapi.DakotaChiSquared(self.datafile,self.simfile,self.chifile,ResidualsWorkspace="res")
-    		self.assertEquals(alg1[0],4.5)
-    		self.assertEquals(alg1[1].name(),"res")
-    		mantid.api.AnalysisDataService.remove("res")
-    	except:
-    		assert False, "Raised an exception"
-    	self.cleanup()
-
-if __name__=="__main__":
+        self.makeFiles()
+        try:
+            alg = simpleapi.DakotaChiSquared(self.datafile, self.simfile, self.chifile)
+            self.assertEquals(len(alg), 2)
+            self.assertEquals(alg[0], 4.5)
+            self.assertEquals(alg[1].name(), "alg")
+            self.assertEquals(alg[1].blocksize(), 5)
+            self.assertEquals(alg[1].getNumberHistograms(), 1)
+            self.assertEquals(alg[1].dataY(0)[3], 1.5)
+            ads = AnalysisDataServiceImpl.Instance()
+            ads.remove("alg")
+            alg1 = simpleapi.DakotaChiSquared(self.datafile, self.simfile, self.chifile, ResidualsWorkspace="res")
+            self.assertEquals(alg1[0], 4.5)
+            self.assertEquals(alg1[1].name(), "res")
+            ads.remove("res")
+        except:
+            assert False, "Raised an exception"
+        self.cleanup()
+
+
+if __name__ == "__main__":
     unittest.main()
diff --git a/Framework/PythonInterface/test/python/plugins/algorithms/SortByQVectorsTest.py b/Framework/PythonInterface/test/python/plugins/algorithms/SortByQVectorsTest.py
index f4b7d5291918215d2e08d98a472ec91b7fa57287..d61834e7b2dafe4aba17342297a21fc44e24dd6e 100644
--- a/Framework/PythonInterface/test/python/plugins/algorithms/SortByQVectorsTest.py
+++ b/Framework/PythonInterface/test/python/plugins/algorithms/SortByQVectorsTest.py
@@ -6,21 +6,21 @@
 # SPDX - License - Identifier: GPL - 3.0 +
 from __future__ import (absolute_import, division, print_function)
 
-import unittest, os
-import mantid
+import unittest
+from mantid import AnalysisDataServiceImpl, simpleapi
 
 
 class SortByQVectorsTest(unittest.TestCase):
     def test_output(self):
-        ws = mantid.simpleapi.LoadSassena("outputSassena_1.4.1.h5", TimeUnit=1.0)
-        mantid.simpleapi.SortByQVectors('ws')
+        ws = simpleapi.LoadSassena("outputSassena_1.4.1.h5", TimeUnit=1.0)
+        simpleapi.SortByQVectors('ws')
         self.assertAlmostEqual(ws[0].getNumberHistograms(), 5)
         self.assertAlmostEqual(ws[0].dataY(0)[0], 0.0)
         self.assertAlmostEqual(ws[0].dataY(1)[0], 0.00600600591861)
         self.assertAlmostEqual(ws[0].dataY(2)[0], 0.0120120118372)
         self.assertAlmostEqual(ws[0].dataY(3)[0], 0.0180180184543)
         self.assertAlmostEqual(ws[0].dataY(4)[0], 0.0240240236744)
-        mantid.api.AnalysisDataService.remove("ws")
+        AnalysisDataServiceImpl.Instance().remove("ws")
 
 
 if __name__ == "__main__":
diff --git a/Framework/PythonInterface/test/python/plugins/algorithms/StringToPngTest.py b/Framework/PythonInterface/test/python/plugins/algorithms/StringToPngTest.py
index 890bd55bafa2782aeff579c980fedc78414635e0..0853ccd8ff884ac677a52d4d352d964d265e4f97 100644
--- a/Framework/PythonInterface/test/python/plugins/algorithms/StringToPngTest.py
+++ b/Framework/PythonInterface/test/python/plugins/algorithms/StringToPngTest.py
@@ -6,34 +6,35 @@
 # SPDX - License - Identifier: GPL - 3.0 +
 from __future__ import (absolute_import, division, print_function)
 
-import unittest,os
-import mantid
+import unittest, os
+from mantid import AnalysisDataServiceImpl, config, simpleapi
 
-class StringToPngTest(unittest.TestCase):
 
-    plotfile=os.path.join(mantid.config.getString('defaultsave.directory'),"StringToPngTest.png")
+class StringToPngTest(unittest.TestCase):
+    plotfile = os.path.join(config.getString('defaultsave.directory'), "StringToPngTest.png")
 
     def cleanup(self):
         if os.path.exists(self.plotfile):
             os.remove(self.plotfile)
 
     def testPlot(self):
-        to_plot='This is a string\nAnd this is a second line'
-        ok2run=''
+        to_plot = 'This is a string\nAnd this is a second line'
+        ok2run = ''
         try:
             import matplotlib
             from distutils.version import LooseVersion
-            if LooseVersion(matplotlib.__version__)<LooseVersion("1.2.0"):
-                ok2run='Wrong version of matplotlib. Required >= 1.2.0'
+            if LooseVersion(matplotlib.__version__) < LooseVersion("1.2.0"):
+                ok2run = 'Wrong version of matplotlib. Required >= 1.2.0'
             else:
                 matplotlib.use("agg")
                 import matplotlib.pyplot as plt
         except:
-            ok2run='Problem importing matplotlib'
-        if ok2run=='':
-            mantid.simpleapi.StringToPng(String=to_plot,OutputFilename=self.plotfile)
-            self.assertTrue(os.path.getsize(self.plotfile)>1e3)
+            ok2run = 'Problem importing matplotlib'
+        if ok2run == '':
+            simpleapi.StringToPng(String=to_plot, OutputFilename=self.plotfile)
+            self.assertTrue(os.path.getsize(self.plotfile) > 1e3)
         self.cleanup()
 
-if __name__=="__main__":
+
+if __name__ == "__main__":
     unittest.main()
diff --git a/Framework/PythonInterface/test/python/plugins/algorithms/SuggestTibCNCSTest.py b/Framework/PythonInterface/test/python/plugins/algorithms/SuggestTibCNCSTest.py
index 4833129337652e9416bb16f80c6d1bb631755992..7c5702b36d37242b1b837ab009dbc3e2f3528a30 100644
--- a/Framework/PythonInterface/test/python/plugins/algorithms/SuggestTibCNCSTest.py
+++ b/Framework/PythonInterface/test/python/plugins/algorithms/SuggestTibCNCSTest.py
@@ -6,27 +6,28 @@
 # SPDX - License - Identifier: GPL - 3.0 +
 from __future__ import (absolute_import, division, print_function)
 
-import unittest,os
-import mantid
+import unittest
+from mantid import simpleapi
 import numpy
 
 
 class SuggestTibCNCSTest(unittest.TestCase):
     def test_simple(self):
-        result=mantid.simpleapi.SuggestTibCNCS(3.)
-        self.assertAlmostEqual(result[0]*0.1,4491.5,0)
-        self.assertAlmostEqual(result[1]*0.1,4731.5,0)
-        result=mantid.simpleapi.SuggestTibCNCS(1.)
-        self.assertAlmostEqual(result[0]*0.1,9562.1,0)
-        self.assertAlmostEqual(result[1]*0.1,9902.1,0)
-        result=mantid.simpleapi.SuggestTibCNCS(6.)
-        self.assertAlmostEqual(result[0]*0.1,2983.3,0)
-        self.assertAlmostEqual(result[1]*0.1,3323.3,0)
+        result = simpleapi.SuggestTibCNCS(3.)
+        self.assertAlmostEqual(result[0] * 0.1, 4491.5, 0)
+        self.assertAlmostEqual(result[1] * 0.1, 4731.5, 0)
+        result = simpleapi.SuggestTibCNCS(1.)
+        self.assertAlmostEqual(result[0] * 0.1, 9562.1, 0)
+        self.assertAlmostEqual(result[1] * 0.1, 9902.1, 0)
+        result = simpleapi.SuggestTibCNCS(6.)
+        self.assertAlmostEqual(result[0] * 0.1, 2983.3, 0)
+        self.assertAlmostEqual(result[1] * 0.1, 3323.3, 0)
 
     def test_someresult(self):
-        for en in numpy.arange(1.,30.,0.1):
-            result=mantid.simpleapi.SuggestTibCNCS(en)
-            self.assertTrue(result[1]-result[0]>1000.)
+        for en in numpy.arange(1., 30., 0.1):
+            result = simpleapi.SuggestTibCNCS(en)
+            self.assertTrue(result[1] - result[0] > 1000.)
 
-if __name__=="__main__":
+
+if __name__ == "__main__":
     unittest.main()
diff --git a/Framework/PythonInterface/test/python/plugins/algorithms/SuggestTibHYSPECTest.py b/Framework/PythonInterface/test/python/plugins/algorithms/SuggestTibHYSPECTest.py
index 222c8150d20a7322fcf88c561ea8eb03a6a8a975..feddaaa75f0058263ac0f30e005e8b9f1a947dcc 100644
--- a/Framework/PythonInterface/test/python/plugins/algorithms/SuggestTibHYSPECTest.py
+++ b/Framework/PythonInterface/test/python/plugins/algorithms/SuggestTibHYSPECTest.py
@@ -6,18 +6,19 @@
 # SPDX - License - Identifier: GPL - 3.0 +
 from __future__ import (absolute_import, division, print_function)
 
-import unittest,os
-import mantid
+import unittest
+from mantid import simpleapi
 
 
 class SuggestTibHYSPECTest(unittest.TestCase):
     def test_simple(self):
-    	result=mantid.simpleapi.SuggestTibHYSPEC(5.)
-    	self.assertAlmostEqual(result[0]*.1,3951.5,0)
-    	self.assertAlmostEqual(result[1]*.1,4151.5,0)
-    	result=mantid.simpleapi.SuggestTibHYSPEC(40.)
-    	self.assertAlmostEqual(result[0]*.1,1189.8,0)
-    	self.assertAlmostEqual(result[1]*.1,1389.8,0)
+        result = simpleapi.SuggestTibHYSPEC(5.)
+        self.assertAlmostEqual(result[0] * .1, 3951.5, 0)
+        self.assertAlmostEqual(result[1] * .1, 4151.5, 0)
+        result = simpleapi.SuggestTibHYSPEC(40.)
+        self.assertAlmostEqual(result[0] * .1, 1189.8, 0)
+        self.assertAlmostEqual(result[1] * .1, 1389.8, 0)
 
-if __name__=="__main__":
+
+if __name__ == "__main__":
     unittest.main()
diff --git a/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/CMakeLists.txt b/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/CMakeLists.txt
index 004444cd49a60a8e39413c959705b92bbba7c3f5..a164ade4d65e699d5cd103d8dda7df8fb6006fef 100644
--- a/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/CMakeLists.txt
+++ b/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/CMakeLists.txt
@@ -69,5 +69,6 @@ set ( TEST_PY_FILES
 check_tests_valid ( ${CMAKE_CURRENT_SOURCE_DIR} ${TEST_PY_FILES} )
 
 # Prefix for test name=PythonWorkflowAlgorithms
+set ( PYUNITTEST_PYTHONPATH_EXTRA ${PYTHONINTERFACE_PLUGINS_DIR}/algorithms/WorkflowAlgorithms )
 pyunittest_add_test ( ${CMAKE_CURRENT_SOURCE_DIR} python.WorkflowAlgorithms ${TEST_PY_FILES} )
 add_subdirectory( sans )
diff --git a/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/EnergyWindowScanTest.py b/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/EnergyWindowScanTest.py
index 5bcf829c098f6fcea4ab149d79880410d291dafb..469f00cdfb2892b9fca96552dce51b169dd2f5bd 100644
--- a/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/EnergyWindowScanTest.py
+++ b/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/EnergyWindowScanTest.py
@@ -26,15 +26,17 @@ class EnergyWindowScanTest(unittest.TestCase):
                          InelasticRange='0, 1.5', GroupingMethod='All')
 
         self.assertEqual(round(mtd['Scan_el_eq1'].readY(0)[0], 7), 0)
-        self.assertEqual(round(mtd['Scan_inel_eq2'].readY(0)[0], 7), -6.8454638)
+        self.assertEqual(
+            round(
+                mtd['Scan_inel_eq2'].readY(0)[0], 7), -6.8454638)
 
     def test_MSDFit(self):
         EnergyWindowScan(InputFiles="IRS26176.RAW", Instrument='IRIS', Analyser='graphite',
                          Reflection='002', SpectraRange='3, 50', ElasticRange='-0.5, 0',
                          InelasticRange='0, 0.5', GroupingMethod='Individual', MSDFit=True)
-        scan_ws = mtd['Scan_el_eq1_Gauss_0_Workspace']
-        self.assertEqual(round(scan_ws.readY(0)[0], 7), 0.0844171)
-        self.assertEqual(round(scan_ws.readY(0)[1], 7), 0.0963181)
+        scan_ws = mtd['Scan_msd_fit']
+        self.assertEqual(round(scan_ws[0].readY(0)[0], 7), 0.0844171)
+        self.assertEqual(round(scan_ws[0].readY(0)[1], 7), 0.0963181)
 
 
 if __name__ == '__main__':
diff --git a/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/ResNorm2Test.py b/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/ResNorm2Test.py
index 3c58c25e12d5e124e9b935a64f082165aa6ad913..79fa570f225c484149316a6d6552bd2e386bb736 100644
--- a/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/ResNorm2Test.py
+++ b/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/ResNorm2Test.py
@@ -19,9 +19,9 @@ class ResNorm2Test(unittest.TestCase):
 
     def setUp(self):
         self._res_ws = Load(Filename='irs26173_graphite002_res.nxs',
-                            OutputWorkspace='__ResNormTest_Resolution')
+                            OutputWorkspace='irs26173_graphite002_res')
         self._van_ws = Load(Filename='irs26173_graphite002_red.nxs',
-                            OutputWorkspace='__ResNormTest_Vanadium')
+                            OutputWorkspace='irs26173_graphite002_red')
 
 
     def _validate_result(self, result):
diff --git a/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/SANSDarkRunBackgroundCorrectionTest.py b/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/SANSDarkRunBackgroundCorrectionTest.py
index 701a05443891cfcf1457d8f6bcbfc9a178acc29a..3d192f849ae11b796faa574150b58dc76ddd5dc0 100644
--- a/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/SANSDarkRunBackgroundCorrectionTest.py
+++ b/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/SANSDarkRunBackgroundCorrectionTest.py
@@ -16,6 +16,7 @@ from SANSDarkRunBackgroundCorrection import DarkRunMonitorAndDetectorRemover
 from SANSDarkRunBackgroundCorrection import SANSDarkRunBackgroundCorrection
 from six.moves import range
 
+
 class SANSDarkRunBackgroundCorrectionTest(unittest.TestCase):
     #-----
     # Workspace2D tests
@@ -58,7 +59,7 @@ class SANSDarkRunBackgroundCorrectionTest(unittest.TestCase):
 
         # Assert
         # We should sum up all bins in the dark run (all y values, hence bin_boundaries_dark_run - 1).
-        # Then multpliy by the normalization ratio 
+        # Then multpliy by the normalization ratio
         # Then divide by the bins in the scatterer.
         expected_integration = y_value_dark_run* float(bin_boundaries_dark_run - 1)
         expected_correction_value = (normalization_ratio*expected_integration/float(bin_boundaries_scatter - 1))
@@ -86,7 +87,7 @@ class SANSDarkRunBackgroundCorrectionTest(unittest.TestCase):
         bin_boundaries_dark_run = 20
         y_value_spectra_even_dark_run = [0.3 for element in range(bin_boundaries_dark_run - 1)]
         y_value_spectra_odd_dark_run = [0.2 for element in range(bin_boundaries_dark_run - 1)]
-        y_value_dark_run = (y_value_spectra_even_dark_run + y_value_spectra_odd_dark_run + 
+        y_value_dark_run = (y_value_spectra_even_dark_run + y_value_spectra_odd_dark_run +
                             y_value_spectra_even_dark_run + y_value_spectra_odd_dark_run)
         e_value_dark_run = 0
         name_dark_run = "_dark_run_SANS_test"
@@ -157,16 +158,16 @@ class SANSDarkRunBackgroundCorrectionTest(unittest.TestCase):
                     DarkRun = name_dark_run,
                     Mean = mean,
                     Uniform =uniform,
-                    NormalizationRatio=normalization_ratio, 
+                    NormalizationRatio=normalization_ratio,
                     OutputWorkspace = out_ws_name,
                     ApplyToDetectors = True,
                     ApplyToMonitors = False,
                     SelectedMonitors = [],
                     rethrow = True)
-        
+
         # Assert
         # We should sum up all bins in the dark run (all y values, hence bin_boundaries_dark_run - 1).
-        # Then multpliy by the normalization ratio 
+        # Then multpliy by the normalization ratio
         # Then divide by the bins in the scatterer.
         expected_correction_value = normalization_ratio
         self.assertTrue(AnalysisDataService.doesExist(out_ws_name))
@@ -246,7 +247,7 @@ class SANSDarkRunBackgroundCorrectionTest(unittest.TestCase):
         applyToMonitors = True
         applyToDetectors = False
         out_ws_name = "out_test"
-        selected_monitor = [2] 
+        selected_monitor = [2]
         # Act
         ws = self._do_run_dark_subtraction(scatter_ws, dark_run, mean, uniform, normalization_ratio,
                                       out_ws_name, applyToMonitors, applyToDetectors, selected_monitor)
@@ -401,7 +402,8 @@ class SANSDarkRunBackgroundCorrectionTest(unittest.TestCase):
 
         AnalysisDataService.add(scatter_name, scatter_ws)
         AnalysisDataService.add(dark_name, dark_run)
-        self.assertRaises(RuntimeError, SANSDarkRunBackgroundCorrection, **kwds)
+        self.assertRaises(RuntimeError, run_algorithm, 'SANSDarkRunBackgroundCorrection',
+                          rethrow=True, **kwds)
 
         # Clean up
         ws_to_clean = [scatter_name, dark_name]
@@ -446,7 +448,8 @@ class SANSDarkRunBackgroundCorrectionTest(unittest.TestCase):
 
         AnalysisDataService.add(scatter_name, scatter_ws)
         AnalysisDataService.add(dark_name, dark_run)
-        self.assertRaises(RuntimeError, SANSDarkRunBackgroundCorrection, **kwds)
+        self.assertRaises(RuntimeError, run_algorithm, 'SANSDarkRunBackgroundCorrection',
+                          rethrow=True, **kwds)
 
         # Clean up
         ws_to_clean = [scatter_name, dark_name]
@@ -526,7 +529,7 @@ class SANSDarkRunBackgroundCorrectionTest(unittest.TestCase):
         out_ws_name = "sans_workspace_test"
         if as_dark_run:
             out_ws_name = "dark_run_workspace_test"
-        
+
         alg_load  = AlgorithmManager.createUnmanaged("LoadNexusProcessed")
         alg_load.initialize()
         alg_load.setChild(True)
diff --git a/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/SavePlot1DTest.py b/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/SavePlot1DTest.py
index 11783386f32cfceb363c1683b8701969b9fc88b2..a0f0393885efe24395411e4a61941f4eac56fffe 100644
--- a/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/SavePlot1DTest.py
+++ b/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/SavePlot1DTest.py
@@ -6,40 +6,46 @@
 # SPDX - License - Identifier: GPL - 3.0 +
 from __future__ import (absolute_import, division, print_function)
 
-import unittest,os
-import mantid
+import unittest, os
+from mantid import AnalysisDataServiceImpl, config, simpleapi
+
 
 class SavePlot1DTest(unittest.TestCase):
     def makeWs(self):
-        mantid.simpleapi.CreateWorkspace(OutputWorkspace='test1',DataX='1,2,3,4,5,1,2,3,4,5',DataY='1,2,3,4,2,3,4,5',DataE='1,2,3,4,2,3,4,5',NSpec='2',UnitX='TOF',Distribution='1',YUnitlabel="S(q)")
-        mantid.simpleapi.CreateWorkspace(OutputWorkspace='test2',DataX='1,2,3,4,5,1,2,3,4,5',DataY='1,2,3,4,2,3,4,5',DataE='1,2,3,4,2,3,4,5',NSpec='2',
-                                         UnitX='Momentum',VerticalAxisUnit='TOF',VerticalAxisValues='1,2',Distribution='1',YUnitLabel='E',WorkspaceTitle='x')
-        mantid.simpleapi.GroupWorkspaces("test1,test2",OutputWorkspace="group")
-        self.plotfile=os.path.join(mantid.config.getString('defaultsave.directory'),'plot.png')
+        simpleapi.CreateWorkspace(OutputWorkspace='test1', DataX='1,2,3,4,5,1,2,3,4,5', DataY='1,2,3,4,2,3,4,5',
+                                  DataE='1,2,3,4,2,3,4,5', NSpec='2', UnitX='TOF', Distribution='1', YUnitlabel="S(q)")
+        simpleapi.CreateWorkspace(OutputWorkspace='test2', DataX='1,2,3,4,5,1,2,3,4,5', DataY='1,2,3,4,2,3,4,5',
+                                  DataE='1,2,3,4,2,3,4,5', NSpec='2',
+                                  UnitX='Momentum', VerticalAxisUnit='TOF', VerticalAxisValues='1,2', Distribution='1',
+                                  YUnitLabel='E', WorkspaceTitle='x')
+        simpleapi.GroupWorkspaces("test1,test2", OutputWorkspace="group")
+        self.plotfile = os.path.join(config.getString('defaultsave.directory'), 'plot.png')
 
     def cleanup(self):
-        mantid.api.AnalysisDataService.remove("group")
-        mantid.api.AnalysisDataService.remove("test1")
-        mantid.api.AnalysisDataService.remove("test2")
+        ads = AnalysisDataServiceImpl.Instance()
+        ads.remove("group")
+        ads.remove("test1")
+        ads.remove("test2")
         if os.path.exists(self.plotfile):
             os.remove(self.plotfile)
 
     def testPlot(self):
         self.makeWs()
-        ok2run=''
+        ok2run = ''
         try:
             import matplotlib
             from distutils.version import LooseVersion
-            if LooseVersion(matplotlib.__version__)<LooseVersion("1.2.0"):
-                ok2run='Wrong version of matplotlib. Required >= 1.2.0'
+            if LooseVersion(matplotlib.__version__) < LooseVersion("1.2.0"):
+                ok2run = 'Wrong version of matplotlib. Required >= 1.2.0'
             matplotlib.use("agg")
             import matplotlib.pyplot as plt
         except:
-            ok2run='Problem importing matplotlib'
-        if ok2run=='':
-            mantid.simpleapi.SavePlot1D("group",self.plotfile)
-            self.assertGreater(os.path.getsize(self.plotfile),1e4)
+            ok2run = 'Problem importing matplotlib'
+        if ok2run == '':
+            simpleapi.SavePlot1D("group", self.plotfile)
+            self.assertGreater(os.path.getsize(self.plotfile), 1e4)
         self.cleanup()
 
-if __name__=="__main__":
+
+if __name__ == "__main__":
     unittest.main()
diff --git a/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/sans/SANSCalculateTransmissionTest.py b/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/sans/SANSCalculateTransmissionTest.py
index 2621be81638033fce4a5bcfe2d640bc3e7ef0636..4d396e96bd8122ff51d86784794cc5abdcc2e13e 100644
--- a/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/sans/SANSCalculateTransmissionTest.py
+++ b/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/sans/SANSCalculateTransmissionTest.py
@@ -6,7 +6,7 @@
 # SPDX - License - Identifier: GPL - 3.0 +
 from __future__ import (absolute_import, division, print_function)
 import unittest
-import mantid
+from mantid.api import FrameworkManager
 import numpy as np
 from sans.test_helper.test_director import TestDirector
 from sans.state.calculate_transmission import get_calculate_transmission_builder
@@ -63,6 +63,10 @@ class SANSCalculateTransmissionTest(unittest.TestCase):
     sample_workspace = None
     sample_workspace_2 = None
 
+    @classmethod
+    def setUpClass(cls):
+        FrameworkManager.Instance()
+
     @staticmethod
     def _load_workspace(file_name):
         load_name = "Load"
diff --git a/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/sans/SANSConvertToQTest.py b/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/sans/SANSConvertToQTest.py
index af20737f2cf08466308f148b6931cf1d977323f7..31570e466232a973ef8ea1ee53de9d1330b82d86 100644
--- a/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/sans/SANSConvertToQTest.py
+++ b/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/sans/SANSConvertToQTest.py
@@ -6,7 +6,7 @@
 # SPDX - License - Identifier: GPL - 3.0 +
 from __future__ import (absolute_import, division, print_function)
 import unittest
-import mantid
+from mantid.api import FrameworkManager
 
 from sans.common.general_functions import (create_unmanaged_algorithm)
 from sans.common.constants import EMPTY_NAME
@@ -18,6 +18,11 @@ from sans.test_helper.file_information_mock import SANSFileInformationMock
 
 
 class SANSConvertToQTest(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        FrameworkManager.Instance()
+
     @staticmethod
     def _get_workspace(x_unit="Wavelength", is_adjustment=False):
         bank_pixel_width = 1 if is_adjustment else 2
diff --git a/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/sans/SANSConvertToWavelengthTest.py b/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/sans/SANSConvertToWavelengthTest.py
index 687c84b27a0025ce2b548eee16d9a88471b54729..c46d526617dea8097540a6be4cfd5062cca48c24 100644
--- a/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/sans/SANSConvertToWavelengthTest.py
+++ b/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/sans/SANSConvertToWavelengthTest.py
@@ -6,8 +6,8 @@
 # SPDX - License - Identifier: GPL - 3.0 +
 from __future__ import (absolute_import, division, print_function)
 import unittest
-import mantid
 
+from mantid.api import FrameworkManager
 from mantid.dataobjects import EventWorkspace
 from sans.common.general_functions import (create_unmanaged_algorithm)
 from sans.common.constants import EMPTY_NAME
@@ -30,6 +30,11 @@ def provide_workspace(is_event=True):
 
 
 class SANSSConvertToWavelengthImplementationTest(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        FrameworkManager.Instance()
+
     def test_that_event_workspace_and_interpolating_rebin_raises(self):
         workspace = provide_workspace(is_event=True)
         convert_options = {"InputWorkspace": workspace,
diff --git a/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/sans/SANSCreateAdjustmentWorkspacesTest.py b/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/sans/SANSCreateAdjustmentWorkspacesTest.py
index 820e230736decfb130e98d673f8b1825c3aa21a7..9963034b4ba6747248b8bb163f4447e6af09519b 100644
--- a/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/sans/SANSCreateAdjustmentWorkspacesTest.py
+++ b/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/sans/SANSCreateAdjustmentWorkspacesTest.py
@@ -6,7 +6,7 @@
 # SPDX - License - Identifier: GPL - 3.0 +
 from __future__ import (absolute_import, division, print_function)
 import unittest
-import mantid
+from mantid.api import FrameworkManager
 from sans.test_helper.test_director import TestDirector
 from sans.common.general_functions import create_unmanaged_algorithm
 from sans.common.constants import EMPTY_NAME
@@ -22,6 +22,10 @@ class SANSCreateAdjustmentWorkspacesTest(unittest.TestCase):
     test_wav_max = 11.
     test_wav_width = 2.
 
+    @classmethod
+    def setUpClass(cls):
+        FrameworkManager.Instance()
+
     @staticmethod
     def _get_state():
         test_director = TestDirector()
diff --git a/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/sans/SANSCreateWavelengthAndPixelAdjustmentTest.py b/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/sans/SANSCreateWavelengthAndPixelAdjustmentTest.py
index d28d24d7f8c8b3c3e7b7d869da7320b162fc9067..e24f76f23a1ca4be6e19293dfe0a341ad72c7f58 100644
--- a/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/sans/SANSCreateWavelengthAndPixelAdjustmentTest.py
+++ b/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/sans/SANSCreateWavelengthAndPixelAdjustmentTest.py
@@ -6,7 +6,8 @@
 # SPDX - License - Identifier: GPL - 3.0 +
 from __future__ import (absolute_import, division, print_function)
 import unittest
-import mantid
+from mantid.api import FrameworkManager
+from mantid.kernel import config
 
 import os
 import numpy as np
@@ -18,6 +19,11 @@ from sans.common.constants import EMPTY_NAME
 
 
 class SANSCalculateTransmissionTest(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        FrameworkManager.Instance()
+
     @staticmethod
     def _create_test_wavelength_adjustment_file(file_name):
         test_file = ("  Tue 24-MAR-2015 00:02 Workspace: directbeam_new_hist\n"
@@ -32,7 +38,7 @@ class SANSCalculateTransmissionTest(unittest.TestCase):
                      "     9.00000    5.000000e-01    5.000000e-01\n"
                      "    11.00000    5.000000e-01    5.000000e-01\n")
 
-        full_file_path = os.path.join(mantid.config.getString('defaultsave.directory'), file_name)
+        full_file_path = os.path.join(config.getString('defaultsave.directory'), file_name)
         if os.path.exists(full_file_path):
             os.remove(full_file_path)
 
diff --git a/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/sans/SANSNormalizeToMonitorTest.py b/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/sans/SANSNormalizeToMonitorTest.py
index b59fadc3b34f9c4cc74ded5fbc23060f98f8ed1b..b374265a10ffce018285d6dcff4168b0155d6353 100644
--- a/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/sans/SANSNormalizeToMonitorTest.py
+++ b/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/sans/SANSNormalizeToMonitorTest.py
@@ -6,7 +6,7 @@
 # SPDX - License - Identifier: GPL - 3.0 +
 from __future__ import (absolute_import, division, print_function)
 import unittest
-import mantid
+from mantid.api import FrameworkManager
 
 from sans.test_helper.test_director import TestDirector
 from sans.state.normalize_to_monitor import get_normalize_to_monitor_builder
@@ -45,6 +45,10 @@ def get_expected_for_spectrum_1_case(monitor_workspace, selected_detector):
 
 class SANSNormalizeToMonitorTest(unittest.TestCase):
 
+    @classmethod
+    def setUpClass(cls):
+        FrameworkManager.Instance()
+
     @staticmethod
     def _get_monitor_workspace(data=None):
         create_name = "CreateSampleWorkspace"
diff --git a/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/sans/SANSScaleTest.py b/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/sans/SANSScaleTest.py
index 843ec870816ad6cb48f3391e0a4ee75c2fbb7e9c..be7e864d5de29359777b942f5d6a747e594d7160 100644
--- a/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/sans/SANSScaleTest.py
+++ b/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/sans/SANSScaleTest.py
@@ -6,7 +6,7 @@
 # SPDX - License - Identifier: GPL - 3.0 +
 from __future__ import (absolute_import, division, print_function)
 import unittest
-import mantid
+from mantid.api import FrameworkManager
 import math
 
 from sans.common.general_functions import (create_unmanaged_algorithm)
@@ -19,6 +19,11 @@ from sans.test_helper.file_information_mock import SANSFileInformationMock
 
 
 class SANSScaleTest(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        FrameworkManager.Instance()
+
     @staticmethod
     def _get_workspace():
         sample_name = "CreateSampleWorkspace"
diff --git a/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/sans/SANSSliceEventTest.py b/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/sans/SANSSliceEventTest.py
index 6ba19de33c4d41b27a744b9aa21cb3a318766602..982b51ae158ff60003900fd052c4130bdd4ac17b 100644
--- a/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/sans/SANSSliceEventTest.py
+++ b/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/sans/SANSSliceEventTest.py
@@ -6,8 +6,8 @@
 # SPDX - License - Identifier: GPL - 3.0 +
 from __future__ import (absolute_import, division, print_function)
 import unittest
-import mantid
 
+from mantid.api import FrameworkManager
 from mantid.kernel import DateAndTime
 from mantid.dataobjects import Workspace2D
 from sans.common.general_functions import (create_unmanaged_algorithm)
@@ -46,6 +46,11 @@ def provide_workspace_with_proton_charge(is_event=True):
 
 
 class SANSSliceEventTest(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        FrameworkManager.Instance()
+
     @staticmethod
     def _provide_workspaces(is_event=True):
         workspace = provide_workspace_with_proton_charge(is_event=is_event)
diff --git a/Framework/PythonInterface/test/testhelpers/__init__.py b/Framework/PythonInterface/test/testhelpers/__init__.py
index de00ffc779168ac1f48ea0e9201623d95bde3376..7adc48f9c59ca43919e51140d8b34371ddc8e784 100644
--- a/Framework/PythonInterface/test/testhelpers/__init__.py
+++ b/Framework/PythonInterface/test/testhelpers/__init__.py
@@ -10,11 +10,9 @@ are for use in unit tests only!
 from __future__ import (absolute_import, division,
                         print_function)
 
-from six import iteritems
-# Define all mantid exported classes first
-import mantid
-
-#Add workspace creation namespace
+# Import mantid to set MANTIDPATH for any ConfigService call that may be done
+import mantid  # noqa
+# Add workspace creation namespace
 from . import WorkspaceCreationHelper
 
 # Define some pure-Python functions to add to the mix
@@ -46,6 +44,8 @@ def create_algorithm(name, **kwargs):
         kwargs - A dictionary of property name:value pairs
     @returns The algorithm handle
     """
+    # Initialize the whole framework
+    import mantid.simpleapi  # noqa
     if 'Version' in kwargs:
         alg = mantid.api.AlgorithmManager.createUnmanaged(name, kwargs['Version'])
         del kwargs['Version']
diff --git a/Framework/PythonInterface/test/testhelpers/testrunner.py b/Framework/PythonInterface/test/testhelpers/testrunner.py
index 3b0ceb75cd05c9862a571152fe6a66b507a015ef..64edae69cbfba0e1a074a7b6349cdc7b4dfb8819 100644
--- a/Framework/PythonInterface/test/testhelpers/testrunner.py
+++ b/Framework/PythonInterface/test/testhelpers/testrunner.py
@@ -149,9 +149,4 @@ def result_class(pathname):
 
 
 if __name__ == "__main__":
-    if "TESTRUNNER_IMPORT_MANTID" in os.environ:
-        # Import mantid so that it sets up the additional paths to scripts etc
-        # It would be good to try & remove this to soften the impact on tests
-        # that don't require importing mantid at all
-        import mantid  # noqa
     main(sys.argv)
diff --git a/Framework/WorkflowAlgorithms/CMakeLists.txt b/Framework/WorkflowAlgorithms/CMakeLists.txt
index 52ca1389217a820e50b581e99895d85fe42ac1ec..49c85f2b98968c7dc872893dcf93ee590ee5714c 100644
--- a/Framework/WorkflowAlgorithms/CMakeLists.txt
+++ b/Framework/WorkflowAlgorithms/CMakeLists.txt
@@ -35,7 +35,6 @@ set ( SRC_FILES
 	src/SANSSolidAngleCorrection.cpp
 	src/SetupEQSANSReduction.cpp
 	src/SetupHFIRReduction.cpp
-	src/SetupILLD33Reduction.cpp
 	src/SofTwoThetaTOF.cpp
 	src/StepScan.cpp
 	src/WorkflowAlgorithmHelpers.cpp
@@ -81,7 +80,6 @@ set ( INC_FILES
 	inc/MantidWorkflowAlgorithms/SANSSolidAngleCorrection.h
 	inc/MantidWorkflowAlgorithms/SetupEQSANSReduction.h
 	inc/MantidWorkflowAlgorithms/SetupHFIRReduction.h
-	inc/MantidWorkflowAlgorithms/SetupILLD33Reduction.h
 	inc/MantidWorkflowAlgorithms/SofTwoThetaTOF.h
 	inc/MantidWorkflowAlgorithms/StepScan.h
 	inc/MantidWorkflowAlgorithms/WorkflowAlgorithmHelpers.h
diff --git a/Framework/WorkflowAlgorithms/inc/MantidWorkflowAlgorithms/SetupILLD33Reduction.h b/Framework/WorkflowAlgorithms/inc/MantidWorkflowAlgorithms/SetupILLD33Reduction.h
deleted file mode 100644
index be7a08dfd19857e7e4fb0db5b411e73be0cae76f..0000000000000000000000000000000000000000
--- a/Framework/WorkflowAlgorithms/inc/MantidWorkflowAlgorithms/SetupILLD33Reduction.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// Mantid Repository : https://github.com/mantidproject/mantid
-//
-// Copyright &copy; 2011 ISIS Rutherford Appleton Laboratory UKRI,
-//     NScD Oak Ridge National Laboratory, European Spallation Source
-//     & Institut Laue - Langevin
-// SPDX - License - Identifier: GPL - 3.0 +
-#ifndef MANTID_ALGORITHMS_SETUPEQSANSREDUCTION_H_
-#define MANTID_ALGORITHMS_SETUPEQSANSREDUCTION_H_
-
-//----------------------------------------------------------------------
-// Includes
-//----------------------------------------------------------------------
-#include "MantidAPI/Algorithm.h"
-#include "MantidAPI/DeprecatedAlgorithm.h"
-#include "MantidAPI/MatrixWorkspace_fwd.h"
-#include "MantidDataObjects/EventWorkspace.h"
-#include "MantidKernel/PropertyManager.h"
-
-namespace Mantid {
-namespace WorkflowAlgorithms {
-/**
-    Set up the reduction options for ILL D33 reduction.
-*/
-
-class DLLExport SetupILLD33Reduction : public API::Algorithm,
-                                       public API::DeprecatedAlgorithm {
-public:
-  /// Algorithm's name
-  const std::string name() const override { return "SetupILLD33Reduction"; }
-  /// Summary of algorithms purpose
-  const std::string summary() const override {
-    return "Set up ILL D33 SANS reduction options.";
-  }
-
-  /// Algorithm's version
-  int version() const override { return (1); }
-  /// Algorithm's category for identification
-  const std::string category() const override { return "Workflow\\SANS"; }
-
-private:
-  /// Initialisation code
-  void init() override;
-  /// Execution code
-  void exec() override;
-  std::string _findFile(std::string dataRun);
-  void
-  setupSensitivity(boost::shared_ptr<Kernel::PropertyManager> reductionManager);
-  void setupTransmission(
-      boost::shared_ptr<Kernel::PropertyManager> reductionManager);
-  void
-  setupBackground(boost::shared_ptr<Kernel::PropertyManager> reductionManager);
-};
-
-} // namespace WorkflowAlgorithms
-} // namespace Mantid
-
-#endif /*MANTID_ALGORITHMS_SETUPEQSANSREDUCTION_H_*/
diff --git a/Framework/WorkflowAlgorithms/src/SetupILLD33Reduction.cpp b/Framework/WorkflowAlgorithms/src/SetupILLD33Reduction.cpp
deleted file mode 100644
index e62aa2b2530a147874db1a12f35ad62fe4f84bed..0000000000000000000000000000000000000000
--- a/Framework/WorkflowAlgorithms/src/SetupILLD33Reduction.cpp
+++ /dev/null
@@ -1,950 +0,0 @@
-// Mantid Repository : https://github.com/mantidproject/mantid
-//
-// Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
-//     NScD Oak Ridge National Laboratory, European Spallation Source
-//     & Institut Laue - Langevin
-// SPDX - License - Identifier: GPL - 3.0 +
-//----------------------------------------------------------------------
-// Includes
-//----------------------------------------------------------------------
-#include "MantidWorkflowAlgorithms/SetupILLD33Reduction.h"
-#include "MantidAPI/AlgorithmProperty.h"
-#include "MantidAPI/FileProperty.h"
-#include "MantidKernel/ArrayProperty.h"
-#include "MantidKernel/BoundedValidator.h"
-#include "MantidKernel/EnabledWhenProperty.h"
-#include "MantidKernel/ListValidator.h"
-#include "MantidKernel/PropertyManager.h"
-#include "MantidKernel/PropertyManagerDataService.h"
-#include "MantidKernel/RebinParamsValidator.h"
-#include "MantidKernel/VisibleWhenProperty.h"
-#include "Poco/NumberFormatter.h"
-
-namespace Mantid {
-namespace WorkflowAlgorithms {
-
-// Register the algorithm into the AlgorithmFactory
-DECLARE_ALGORITHM(SetupILLD33Reduction)
-
-using namespace Kernel;
-using namespace API;
-using namespace Geometry;
-
-void SetupILLD33Reduction::init() {
-  // Load options
-  std::string load_grp = "Load Options";
-
-  declareProperty(
-      "SolidAngleCorrection", true,
-      "If true, the solide angle correction will be applied to the data");
-  declareProperty(
-      "DetectorTubes", false,
-      "If true, the solid angle correction for tube detectors will be applied");
-
-  // -- Define group --
-  setPropertyGroup("SolidAngleCorrection", load_grp);
-  setPropertyGroup("DetectorTubes", load_grp);
-
-  // Beam center
-  std::string center_grp = "Beam Center";
-  std::vector<std::string> centerOptions{"None", "Value", "DirectBeam",
-                                         "Scattering"};
-
-  declareProperty("BeamCenterMethod", "None",
-                  boost::make_shared<StringListValidator>(centerOptions),
-                  "Method for determining the data beam center");
-
-  //    Option 1: Set beam center by hand
-  declareProperty("BeamCenterX", EMPTY_DBL(),
-                  "Position of the beam center, in pixel");
-  declareProperty("BeamCenterY", EMPTY_DBL(),
-                  "Position of the beam center, in pixel");
-  setPropertySettings("BeamCenterX",
-                      make_unique<VisibleWhenProperty>("BeamCenterMethod",
-                                                       IS_EQUAL_TO, "Value"));
-  setPropertySettings("BeamCenterY",
-                      make_unique<VisibleWhenProperty>("BeamCenterMethod",
-                                                       IS_EQUAL_TO, "Value"));
-
-  //    Option 2: Find it (expose properties from FindCenterOfMass)
-  declareProperty(
-      make_unique<API::FileProperty>(
-          "BeamCenterFile", "", API::FileProperty::OptionalLoad, "_event.nxs"),
-      "The name of the input event Nexus file to load");
-  setPropertySettings("BeamCenterFile",
-                      make_unique<VisibleWhenProperty>(
-                          "BeamCenterMethod", IS_NOT_EQUAL_TO, "None"));
-
-  auto positiveDouble = boost::make_shared<BoundedValidator<double>>();
-  positiveDouble->setLower(0);
-  declareProperty(
-      "BeamRadius", EMPTY_DBL(),
-      "Radius of the beam area used the exclude the beam when calculating "
-      "the center of mass of the scattering pattern [pixels]. Default=3.0");
-  setPropertySettings("BeamRadius",
-                      make_unique<VisibleWhenProperty>(
-                          "BeamCenterMethod", IS_EQUAL_TO, "Scattering"));
-
-  // -- Define group --
-  setPropertyGroup("BeamCenterMethod", center_grp);
-  setPropertyGroup("BeamCenterX", center_grp);
-  setPropertyGroup("BeamCenterY", center_grp);
-  setPropertyGroup("BeamCenterFile", center_grp);
-  setPropertyGroup("BeamRadius", center_grp);
-
-  // Normalisation
-  std::string norm_grp = "Normalisation";
-  std::vector<std::string> incidentBeamNormOptions;
-  incidentBeamNormOptions.emplace_back("None");
-  // The data will be normalised to the monitor counts
-  incidentBeamNormOptions.emplace_back("Monitor");
-  // The data will be normalised to the total charge only (no beam profile)
-  incidentBeamNormOptions.emplace_back("Timer");
-  this->declareProperty(
-      "Normalisation", "None",
-      boost::make_shared<StringListValidator>(incidentBeamNormOptions),
-      "Options for data normalisation");
-
-  setPropertyGroup("Normalisation", norm_grp);
-
-  // Dark current
-  declareProperty(
-      make_unique<API::FileProperty>(
-          "DarkCurrentFile", "", API::FileProperty::OptionalLoad, "_event.nxs"),
-      "The name of the input event Nexus file to load as dark current.");
-
-  // Sensitivity
-  std::string eff_grp = "Sensitivity";
-  declareProperty(
-      make_unique<API::FileProperty>(
-          "SensitivityFile", "", API::FileProperty::OptionalLoad, "_event.nxs"),
-      "Flood field or sensitivity file.");
-  declareProperty(
-      "MinEfficiency", EMPTY_DBL(), positiveDouble,
-      "Minimum efficiency for a pixel to be considered (default: no minimum).");
-  declareProperty(
-      "MaxEfficiency", EMPTY_DBL(), positiveDouble,
-      "Maximum efficiency for a pixel to be considered (default: no maximum).");
-  declareProperty("UseDefaultDC", true,
-                  "If true, the dark current subtracted "
-                  "from the sample data will also be "
-                  "subtracted from the flood field.");
-  declareProperty(make_unique<API::FileProperty>(
-                      "SensitivityDarkCurrentFile", "",
-                      API::FileProperty::OptionalLoad, "_event.nxs"),
-                  "The name of the input file to load as dark current.");
-  // - sensitivity beam center
-  declareProperty("SensitivityBeamCenterMethod", "None",
-                  boost::make_shared<StringListValidator>(centerOptions),
-                  "Method for determining the sensitivity data beam center");
-
-  //    Option 1: Set beam center by hand
-  declareProperty("SensitivityBeamCenterX", EMPTY_DBL(),
-                  "Sensitivity beam center location in X [pixels]");
-  setPropertySettings("SensitivityBeamCenterX",
-                      make_unique<VisibleWhenProperty>(
-                          "SensitivityBeamCenterMethod", IS_EQUAL_TO, "Value"));
-
-  declareProperty("SensitivityBeamCenterY", EMPTY_DBL(),
-                  "Sensitivity beam center location in Y [pixels]");
-  setPropertySettings("SensitivityBeamCenterY",
-                      make_unique<VisibleWhenProperty>(
-                          "SensitivityBeamCenterMethod", IS_EQUAL_TO, "Value"));
-
-  //    Option 2: Find it (expose properties from FindCenterOfMass)
-  declareProperty(
-      make_unique<API::FileProperty>("SensitivityBeamCenterFile", "",
-                                     API::FileProperty::OptionalLoad, ".xml"),
-      "The name of the input data file to load");
-  setPropertySettings(
-      "SensitivityBeamCenterFile",
-      make_unique<VisibleWhenProperty>("SensitivityBeamCenterMethod",
-                                       IS_NOT_EQUAL_TO, "None"));
-
-  declareProperty(
-      "SensitivityBeamCenterRadius", EMPTY_DBL(),
-      "Radius of the beam area used the exclude the beam when calculating "
-      "the center of mass of the scattering pattern [pixels]. Default=3.0");
-  setPropertySettings("SensitivityBeamCenterRadius",
-                      make_unique<VisibleWhenProperty>(
-                          "BeamCenterMethod", IS_EQUAL_TO, "Scattering"));
-
-  declareProperty("OutputSensitivityWorkspace", "",
-                  "Name to give the sensitivity workspace");
-
-  // -- Define group --
-  setPropertyGroup("SensitivityFile", eff_grp);
-  setPropertyGroup("MinEfficiency", eff_grp);
-  setPropertyGroup("MaxEfficiency", eff_grp);
-  setPropertyGroup("UseDefaultDC", eff_grp);
-  setPropertyGroup("SensitivityDarkCurrentFile", eff_grp);
-  setPropertyGroup("SensitivityBeamCenterMethod", eff_grp);
-  setPropertyGroup("SensitivityBeamCenterX", eff_grp);
-  setPropertyGroup("SensitivityBeamCenterY", eff_grp);
-  setPropertyGroup("SensitivityBeamCenterFile", eff_grp);
-  setPropertyGroup("SensitivityBeamCenterRadius", eff_grp);
-  setPropertyGroup("OutputSensitivityWorkspace", eff_grp);
-
-  // Transmission
-  std::string trans_grp = "Transmission";
-  std::vector<std::string> transOptions{"Value", "DirectBeam"};
-  declareProperty("TransmissionMethod", "Value",
-                  boost::make_shared<StringListValidator>(transOptions),
-                  "Transmission determination method");
-
-  // - Transmission value entered by hand
-  declareProperty("TransmissionValue", EMPTY_DBL(), positiveDouble,
-                  "Transmission value.");
-  setPropertySettings("TransmissionValue",
-                      make_unique<VisibleWhenProperty>("TransmissionMethod",
-                                                       IS_EQUAL_TO, "Value"));
-  declareProperty("TransmissionError", EMPTY_DBL(), positiveDouble,
-                  "Transmission error.");
-  setPropertySettings("TransmissionError",
-                      make_unique<VisibleWhenProperty>("TransmissionMethod",
-                                                       IS_EQUAL_TO, "Value"));
-
-  // - Direct beam method transmission calculation
-  declareProperty(
-      "TransmissionBeamRadius", 3.0,
-      "Radius of the beam area used to compute the transmission [pixels]");
-  setPropertySettings("TransmissionBeamRadius",
-                      make_unique<VisibleWhenProperty>(
-                          "TransmissionMethod", IS_EQUAL_TO, "DirectBeam"));
-  declareProperty(
-      make_unique<API::FileProperty>("TransmissionSampleDataFile", "",
-                                     API::FileProperty::OptionalLoad, ".xml"),
-      "Sample data file for transmission calculation");
-  setPropertySettings("TransmissionSampleDataFile",
-                      make_unique<VisibleWhenProperty>(
-                          "TransmissionMethod", IS_EQUAL_TO, "DirectBeam"));
-  declareProperty(
-      make_unique<API::FileProperty>("TransmissionEmptyDataFile", "",
-                                     API::FileProperty::OptionalLoad, ".xml"),
-      "Empty data file for transmission calculation");
-  setPropertySettings("TransmissionEmptyDataFile",
-                      make_unique<VisibleWhenProperty>(
-                          "TransmissionMethod", IS_EQUAL_TO, "DirectBeam"));
-
-  // - transmission beam center
-  declareProperty("TransmissionBeamCenterMethod", "None",
-                  boost::make_shared<StringListValidator>(centerOptions),
-                  "Method for determining the transmission data beam center");
-  setPropertySettings("TransmissionBeamCenterMethod",
-                      make_unique<VisibleWhenProperty>(
-                          "TransmissionMethod", IS_EQUAL_TO, "DirectBeam"));
-
-  //    Option 1: Set beam center by hand
-  declareProperty("TransmissionBeamCenterX", EMPTY_DBL(),
-                  "Transmission beam center location in X [pixels]");
-  setPropertySettings("TransmissionBeamCenterX",
-                      make_unique<VisibleWhenProperty>(
-                          "TransmissionMethod", IS_EQUAL_TO, "DirectBeam"));
-  declareProperty("TransmissionBeamCenterY", EMPTY_DBL(),
-                  "Transmission beam center location in Y [pixels]");
-  setPropertySettings("TransmissionBeamCenterY",
-                      make_unique<VisibleWhenProperty>(
-                          "TransmissionMethod", IS_EQUAL_TO, "DirectBeam"));
-
-  //    Option 2: Find it (expose properties from FindCenterOfMass)
-  declareProperty(
-      make_unique<API::FileProperty>("TransmissionBeamCenterFile", "",
-                                     API::FileProperty::OptionalLoad, ".xml"),
-      "The name of the input data file to load");
-  setPropertySettings("TransmissionBeamCenterFile",
-                      make_unique<VisibleWhenProperty>(
-                          "TransmissionMethod", IS_EQUAL_TO, "DirectBeam"));
-
-  declareProperty(
-      make_unique<API::FileProperty>("TransmissionDarkCurrentFile", "",
-                                     API::FileProperty::OptionalLoad, ".xml"),
-      "The name of the input data file to load as transmission dark current.");
-  setPropertySettings("TransmissionDarkCurrentFile",
-                      make_unique<VisibleWhenProperty>(
-                          "TransmissionMethod", IS_NOT_EQUAL_TO, "Value"));
-
-  declareProperty(
-      "TransmissionUseSampleDC", true,
-      "If true, the sample dark current will be used IF a dark current file is"
-      "not set.");
-  setPropertySettings("TransmissionUseSampleDC",
-                      make_unique<VisibleWhenProperty>(
-                          "TransmissionMethod", IS_NOT_EQUAL_TO, "Value"));
-
-  declareProperty(
-      "ThetaDependentTransmission", true,
-      "If true, a theta-dependent transmission correction will be applied.");
-
-  // -- Define group --
-  setPropertyGroup("TransmissionMethod", trans_grp);
-  setPropertyGroup("TransmissionValue", trans_grp);
-  setPropertyGroup("TransmissionError", trans_grp);
-  setPropertyGroup("TransmissionBeamRadius", trans_grp);
-  setPropertyGroup("TransmissionSampleDataFile", trans_grp);
-  setPropertyGroup("TransmissionEmptyDataFile", trans_grp);
-  setPropertyGroup("TransmissionBeamCenterMethod", trans_grp);
-  setPropertyGroup("TransmissionBeamCenterX", trans_grp);
-  setPropertyGroup("TransmissionBeamCenterY", trans_grp);
-  setPropertyGroup("TransmissionBeamCenterFile", trans_grp);
-
-  setPropertyGroup("TransmissionDarkCurrentFile", trans_grp);
-  setPropertyGroup("TransmissionUseSampleDC", trans_grp);
-  setPropertyGroup("ThetaDependentTransmission", trans_grp);
-
-  // Background options
-  std::string bck_grp = "Background";
-  declareProperty("BackgroundFiles", "", "Background data files");
-  declareProperty("BckTransmissionMethod", "Value",
-                  boost::make_shared<StringListValidator>(transOptions),
-                  "Transmission determination method");
-
-  // - Transmission value entered by hand
-  declareProperty("BckTransmissionValue", EMPTY_DBL(), positiveDouble,
-                  "Transmission value.");
-  setPropertySettings("BckTransmissionValue",
-                      make_unique<VisibleWhenProperty>("BckTransmissionMethod",
-                                                       IS_EQUAL_TO, "Value"));
-
-  declareProperty("BckTransmissionError", EMPTY_DBL(), positiveDouble,
-                  "Transmission error.");
-  setPropertySettings("BckTransmissionError",
-                      make_unique<VisibleWhenProperty>("BckTransmissionMethod",
-                                                       IS_EQUAL_TO, "Value"));
-
-  // - Direct beam method transmission calculation
-  declareProperty(
-      "BckTransmissionBeamRadius", 3.0,
-      "Radius of the beam area used to compute the transmission [pixels]");
-  setPropertySettings("BckTransmissionBeamRadius",
-                      make_unique<VisibleWhenProperty>(
-                          "BckTransmissionMethod", IS_EQUAL_TO, "DirectBeam"));
-  declareProperty(
-      make_unique<API::FileProperty>("BckTransmissionSampleDataFile", "",
-                                     API::FileProperty::OptionalLoad, ".xml"),
-      "Sample data file for transmission calculation");
-  setPropertySettings("BckTransmissionSampleDataFile",
-                      make_unique<VisibleWhenProperty>(
-                          "BckTransmissionMethod", IS_EQUAL_TO, "DirectBeam"));
-  declareProperty(
-      make_unique<API::FileProperty>("BckTransmissionEmptyDataFile", "",
-                                     API::FileProperty::OptionalLoad, ".xml"),
-      "Empty data file for transmission calculation");
-  setPropertySettings("BckTransmissionEmptyDataFile",
-                      make_unique<VisibleWhenProperty>(
-                          "BckTransmissionMethod", IS_EQUAL_TO, "DirectBeam"));
-
-  // - transmission beam center
-  declareProperty("BckTransmissionBeamCenterMethod", "None",
-                  boost::make_shared<StringListValidator>(centerOptions),
-                  "Method for determining the transmission data beam center");
-  setPropertySettings("BckTransmissionBeamCenterMethod",
-                      make_unique<VisibleWhenProperty>(
-                          "BckTransmissionMethod", IS_EQUAL_TO, "DirectBeam"));
-  //    Option 1: Set beam center by hand
-  declareProperty("BckTransmissionBeamCenterX", EMPTY_DBL(),
-                  "Transmission beam center location in X [pixels]");
-  setPropertySettings("BckTransmissionBeamCenterX",
-                      make_unique<VisibleWhenProperty>(
-                          "BckTransmissionMethod", IS_EQUAL_TO, "DirectBeam"));
-  declareProperty("BckTransmissionBeamCenterY", EMPTY_DBL(),
-                  "Transmission beam center location in Y [pixels]");
-  //    Option 2: Find it (expose properties from FindCenterOfMass)
-  setPropertySettings("BckTransmissionBeamCenterY",
-                      make_unique<VisibleWhenProperty>(
-                          "BckTransmissionMethod", IS_EQUAL_TO, "DirectBeam"));
-  declareProperty(
-      make_unique<API::FileProperty>("BckTransmissionBeamCenterFile", "",
-                                     API::FileProperty::OptionalLoad, ".xml"),
-      "The name of the input data file to load");
-  setPropertySettings("BckTransmissionBeamCenterFile",
-                      make_unique<VisibleWhenProperty>(
-                          "BckTransmissionMethod", IS_EQUAL_TO, "DirectBeam"));
-
-  declareProperty(
-      make_unique<API::FileProperty>("BckTransmissionDarkCurrentFile", "",
-                                     API::FileProperty::OptionalLoad, ".xml"),
-      "The name of the input data file to load as background "
-      "transmission dark current.");
-  setPropertySettings("BckTransmissionDarkCurrentFile",
-                      make_unique<VisibleWhenProperty>("BckTransmissionMethod",
-                                                       IS_EQUAL_TO,
-                                                       "BeamSpreader"));
-
-  declareProperty(
-      "BckThetaDependentTransmission", true,
-      "If true, a theta-dependent transmission correction will be applied.");
-
-  setPropertyGroup("BackgroundFiles", bck_grp);
-  setPropertyGroup("BckTransmissionMethod", bck_grp);
-  setPropertyGroup("BckTransmissionValue", bck_grp);
-  setPropertyGroup("BckTransmissionError", bck_grp);
-  setPropertyGroup("BckTransmissionBeamRadius", bck_grp);
-  setPropertyGroup("BckTransmissionSampleDataFile", bck_grp);
-  setPropertyGroup("BckTransmissionEmptyDataFile", bck_grp);
-  setPropertyGroup("BckTransmissionBeamCenterMethod", bck_grp);
-  setPropertyGroup("BckTransmissionBeamCenterX", bck_grp);
-  setPropertyGroup("BckTransmissionBeamCenterY", bck_grp);
-  setPropertyGroup("BckTransmissionBeamCenterFile", bck_grp);
-  setPropertyGroup("BckTransmissionDarkCurrentFile", bck_grp);
-  setPropertyGroup("BckThetaDependentTransmission", bck_grp);
-
-  // Geometry correction
-  declareProperty("SampleThickness", EMPTY_DBL(), "Sample thickness [cm]");
-
-  // Masking
-  std::string mask_grp = "Mask";
-  declareProperty(make_unique<ArrayProperty<int>>("MaskedDetectorList"),
-                  "List of detector IDs to be masked");
-  declareProperty(
-      make_unique<ArrayProperty<int>>("MaskedEdges"),
-      "Number of pixels to mask on the edges: X-low, X-high, Y-low, Y-high");
-  std::vector<std::string> maskOptions{"None", "Front", "Back"};
-  declareProperty("MaskedSide", "None",
-                  boost::make_shared<StringListValidator>(maskOptions),
-                  "Mask one side of the detector");
-
-  setPropertyGroup("MaskedDetectorList", mask_grp);
-  setPropertyGroup("MaskedEdges", mask_grp);
-  setPropertyGroup("MaskedSide", mask_grp);
-
-  // Absolute scale
-  std::string abs_scale_grp = "Absolute Scale";
-  std::vector<std::string> scaleOptions;
-  scaleOptions.emplace_back("None");
-  scaleOptions.emplace_back("Value");
-  scaleOptions.emplace_back("ReferenceData");
-  declareProperty("AbsoluteScaleMethod", "None",
-                  boost::make_shared<StringListValidator>(scaleOptions),
-                  "Absolute scale correction method");
-  declareProperty("AbsoluteScalingFactor", 1.0, "Absolute scaling factor");
-  setPropertySettings("AbsoluteScalingFactor",
-                      make_unique<VisibleWhenProperty>("AbsoluteScaleMethod",
-                                                       IS_EQUAL_TO, "Value"));
-
-  declareProperty(
-      make_unique<API::FileProperty>("AbsoluteScalingReferenceFilename", "",
-                                     API::FileProperty::OptionalLoad, ".xml"));
-  setPropertySettings("AbsoluteScalingReferenceFilename",
-                      make_unique<VisibleWhenProperty>(
-                          "AbsoluteScaleMethod", IS_EQUAL_TO, "ReferenceData"));
-  declareProperty(
-      "AbsoluteScalingBeamDiameter", 0.0,
-      "Beamstop diameter for computing the absolute scale factor [mm]. "
-      "Read from file if not supplied.");
-  setPropertySettings("AbsoluteScalingBeamDiameter",
-                      make_unique<VisibleWhenProperty>(
-                          "AbsoluteScaleMethod", IS_EQUAL_TO, "ReferenceData"));
-  declareProperty(
-      "AbsoluteScalingAttenuatorTrans", 1.0,
-      "Attenuator transmission value for computing the absolute scale factor");
-  setPropertySettings("AbsoluteScalingAttenuatorTrans",
-                      make_unique<VisibleWhenProperty>(
-                          "AbsoluteScaleMethod", IS_EQUAL_TO, "ReferenceData"));
-  declareProperty("AbsoluteScalingApplySensitivity", false,
-                  "Apply sensitivity correction to the reference data "
-                  "when computing the absolute scale factor");
-  setPropertySettings("AbsoluteScalingApplySensitivity",
-                      make_unique<VisibleWhenProperty>(
-                          "AbsoluteScaleMethod", IS_EQUAL_TO, "ReferenceData"));
-
-  setPropertyGroup("AbsoluteScaleMethod", abs_scale_grp);
-  setPropertyGroup("AbsoluteScalingFactor", abs_scale_grp);
-  setPropertyGroup("AbsoluteScalingReferenceFilename", abs_scale_grp);
-  setPropertyGroup("AbsoluteScalingBeamDiameter", abs_scale_grp);
-  setPropertyGroup("AbsoluteScalingAttenuatorTrans", abs_scale_grp);
-  setPropertyGroup("AbsoluteScalingApplySensitivity", abs_scale_grp);
-
-  // I(Q) calculation
-  std::string iq1d_grp = "I(q) Calculation";
-  declareProperty("DoAzimuthalAverage", true);
-  auto positiveInt = boost::make_shared<BoundedValidator<int>>();
-  positiveInt->setLower(0);
-  declareProperty("IQNumberOfBins", 100, positiveInt,
-                  "Number of I(q) bins when binning is not specified");
-  declareProperty("IQLogBinning", false,
-                  "I(q) log binning when binning is not specified");
-  declareProperty("ComputeResolution", false,
-                  "If true the Q resolution will be computed");
-
-  declareProperty("Do2DReduction", true);
-  declareProperty("IQ2DNumberOfBins", 100, positiveInt,
-                  "Number of I(qx,qy) bins.");
-
-  // -- Define group --
-  setPropertyGroup("DoAzimuthalAverage", iq1d_grp);
-  setPropertyGroup("IQNumberOfBins", iq1d_grp);
-  setPropertyGroup("IQLogBinning", iq1d_grp);
-  setPropertyGroup("ComputeResolution", iq1d_grp);
-  setPropertyGroup("Do2DReduction", iq1d_grp);
-  setPropertyGroup("IQ2DNumberOfBins", iq1d_grp);
-
-  // Outputs
-  declareProperty("ProcessInfo", "", "Additional process information");
-  declareProperty("OutputDirectory", "",
-                  "Directory to put the output files in");
-  declareProperty("OutputMessage", "", Direction::Output);
-  declareProperty("ReductionProperties", "__sans_reduction_properties",
-                  Direction::Input);
-}
-
-void SetupILLD33Reduction::exec() {
-  // Reduction property manager
-  const std::string reductionManagerName = getProperty("ReductionProperties");
-  if (reductionManagerName.empty()) {
-    g_log.error() << "ERROR: Reduction Property Manager name is empty\n";
-    return;
-  }
-  boost::shared_ptr<PropertyManager> reductionManager =
-      boost::make_shared<PropertyManager>();
-  PropertyManagerDataService::Instance().addOrReplace(reductionManagerName,
-                                                      reductionManager);
-
-  // Store name of the instrument
-  reductionManager->declareProperty(
-      make_unique<PropertyWithValue<std::string>>("InstrumentName", "D33"));
-
-  // Store additional (and optional) process information
-  const std::string processInfo = getProperty("ProcessInfo");
-  reductionManager->declareProperty(
-      Kernel::make_unique<PropertyWithValue<std::string>>("ProcessInfo",
-                                                          processInfo));
-
-  // Store the output directory
-  const std::string outputDirectory = getProperty("OutputDirectory");
-  reductionManager->declareProperty(
-      Kernel::make_unique<PropertyWithValue<std::string>>("OutputDirectory",
-                                                          outputDirectory));
-
-  // Store normalization algorithm
-  const std::string normalization = getProperty("Normalisation");
-
-  if (!boost::contains(normalization, "None")) {
-    // If we normalize to monitor, force the loading of monitor data
-    IAlgorithm_sptr normAlg = createChildAlgorithm("HFIRSANSNormalise");
-    normAlg->setProperty("NormalisationType", normalization);
-    // normAlg->setPropertyValue("ReductionProperties", reductionManagerName);
-    auto algProp = make_unique<AlgorithmProperty>("NormaliseAlgorithm");
-    algProp->setValue(normAlg->toString());
-    reductionManager->declareProperty(std::move(algProp));
-  }
-
-  // Load algorithm
-  // IAlgorithm_sptr loadAlg = createChildAlgorithm("EQSANSLoad");
-  // TODO : It looks like properties need cleanup
-  IAlgorithm_sptr loadAlg = createChildAlgorithm("LoadILLSANS");
-
-  auto algProp = make_unique<AlgorithmProperty>("LoadAlgorithm");
-  algProp->setValue(loadAlg->toString());
-  reductionManager->declareProperty(std::move(algProp));
-
-  // Store dark current algorithm
-  const std::string darkCurrentFile = getPropertyValue("DarkCurrentFile");
-  if (!darkCurrentFile.empty()) {
-    IAlgorithm_sptr darkAlg =
-        createChildAlgorithm("EQSANSDarkCurrentSubtraction");
-    darkAlg->setProperty("Filename", darkCurrentFile);
-    darkAlg->setProperty("OutputDarkCurrentWorkspace", "");
-    darkAlg->setPropertyValue("ReductionProperties", reductionManagerName);
-    auto dcalgProp = make_unique<AlgorithmProperty>("DarkCurrentAlgorithm");
-    dcalgProp->setValue(darkAlg->toString());
-    reductionManager->declareProperty(std::move(dcalgProp));
-  }
-
-  // Store default dark current algorithm
-  IAlgorithm_sptr darkDefaultAlg =
-      createChildAlgorithm("EQSANSDarkCurrentSubtraction");
-  darkDefaultAlg->setProperty("OutputDarkCurrentWorkspace", "");
-  darkDefaultAlg->setPropertyValue("ReductionProperties", reductionManagerName);
-  auto ddcalgProp =
-      make_unique<AlgorithmProperty>("DefaultDarkCurrentAlgorithm");
-  ddcalgProp->setValue(darkDefaultAlg->toString());
-  reductionManager->declareProperty(std::move(ddcalgProp));
-
-  // Solid angle correction
-  const bool solidAngleCorrection = getProperty("SolidAngleCorrection");
-  if (solidAngleCorrection) {
-    const bool detectorTubes = getProperty("DetectorTubes");
-    IAlgorithm_sptr solidAlg = createChildAlgorithm("SANSSolidAngleCorrection");
-    solidAlg->setProperty("DetectorTubes", detectorTubes);
-    auto ssaalgProp =
-        make_unique<AlgorithmProperty>("SANSSolidAngleCorrection");
-    ssaalgProp->setValue(solidAlg->toString());
-    reductionManager->declareProperty(std::move(ssaalgProp));
-  }
-
-  // Beam center
-  const double beamCenterX = getProperty("BeamCenterX");
-  const double beamCenterY = getProperty("BeamCenterY");
-  const std::string centerMethod = getPropertyValue("BeamCenterMethod");
-
-  // Beam center option for transmission data
-  if (boost::iequals(centerMethod, "Value")) {
-    if (!isEmpty(beamCenterX) && !isEmpty(beamCenterY)) {
-      reductionManager->declareProperty(make_unique<PropertyWithValue<double>>(
-          "LatestBeamCenterX", beamCenterX));
-      reductionManager->declareProperty(make_unique<PropertyWithValue<double>>(
-          "LatestBeamCenterY", beamCenterY));
-    }
-  } else if (!boost::iequals(centerMethod, "None")) {
-    bool useDirectBeamMethod = true;
-    if (!boost::iequals(centerMethod, "DirectBeam"))
-      useDirectBeamMethod = false;
-    const std::string beamCenterFile = getProperty("BeamCenterFile");
-    if (!beamCenterFile.empty()) {
-      const double beamRadius = getProperty("BeamRadius");
-
-      IAlgorithm_sptr ctrAlg = createChildAlgorithm("SANSBeamFinder");
-      ctrAlg->setProperty("Filename", beamCenterFile);
-      ctrAlg->setProperty("UseDirectBeamMethod", useDirectBeamMethod);
-      if (!isEmpty(beamRadius))
-        ctrAlg->setProperty("BeamRadius", beamRadius);
-      ctrAlg->setPropertyValue("ReductionProperties", reductionManagerName);
-
-      auto algProp = make_unique<AlgorithmProperty>("SANSBeamFinderAlgorithm");
-      algProp->setValue(ctrAlg->toString());
-      reductionManager->declareProperty(std::move(algProp));
-    } else {
-      g_log.error() << "ERROR: Beam center determination was required"
-                       " but no file was provided\n";
-    }
-  }
-
-  // Sensitivity correction, transmission and background
-  setupSensitivity(reductionManager);
-  setupTransmission(reductionManager);
-  setupBackground(reductionManager);
-
-  // Geometry correction
-  const double thickness = getProperty("SampleThickness");
-  if (!isEmpty(thickness)) {
-    IAlgorithm_sptr thickAlg = createChildAlgorithm("NormaliseByThickness");
-    thickAlg->setProperty("SampleThickness", thickness);
-
-    auto geomalgProp = make_unique<AlgorithmProperty>("GeometryAlgorithm");
-    geomalgProp->setValue(thickAlg->toString());
-    reductionManager->declareProperty(std::move(geomalgProp));
-  }
-
-  // Mask
-  const std::string maskDetList = getPropertyValue("MaskedDetectorList");
-  const std::string maskEdges = getPropertyValue("MaskedEdges");
-  const std::string maskSide = getProperty("MaskedSide");
-
-  IAlgorithm_sptr maskAlg = createChildAlgorithm("SANSMask");
-  // The following is broken, try PropertyValue
-  maskAlg->setPropertyValue("Facility", "SNS");
-  maskAlg->setPropertyValue("MaskedDetectorList", maskDetList);
-  maskAlg->setPropertyValue("MaskedEdges", maskEdges);
-  maskAlg->setProperty("MaskedSide", maskSide);
-  auto maskalgProp = make_unique<AlgorithmProperty>("MaskAlgorithm");
-  maskalgProp->setValue(maskAlg->toString());
-  reductionManager->declareProperty(std::move(maskalgProp));
-
-  // Absolute scaling
-  const std::string absScaleMethod = getProperty("AbsoluteScaleMethod");
-  if (boost::iequals(absScaleMethod, "Value")) {
-    const double absScaleFactor = getProperty("AbsoluteScalingFactor");
-
-    IAlgorithm_sptr absAlg = createChildAlgorithm("SANSAbsoluteScale");
-    absAlg->setProperty("Method", absScaleMethod);
-    absAlg->setProperty("ScalingFactor", absScaleFactor);
-    absAlg->setPropertyValue("ReductionProperties", reductionManagerName);
-    auto absScaleAlgProp =
-        make_unique<AlgorithmProperty>("AbsoluteScaleAlgorithm");
-    absScaleAlgProp->setValue(absAlg->toString());
-    reductionManager->declareProperty(std::move(absScaleAlgProp));
-  } else if (boost::iequals(absScaleMethod, "ReferenceData")) {
-    const std::string absRefFile =
-        getPropertyValue("AbsoluteScalingReferenceFilename");
-    const double beamDiam = getProperty("AbsoluteScalingBeamDiameter");
-    const double attTrans = getProperty("AbsoluteScalingAttenuatorTrans");
-    const bool applySensitivity =
-        getProperty("AbsoluteScalingApplySensitivity");
-
-    IAlgorithm_sptr absAlg = createChildAlgorithm("SANSAbsoluteScale");
-    absAlg->setProperty("Method", absScaleMethod);
-    absAlg->setProperty("ReferenceDataFilename", absRefFile);
-    absAlg->setProperty("BeamstopDiameter", beamDiam);
-    absAlg->setProperty("AttenuatorTransmission", attTrans);
-    absAlg->setProperty("ApplySensitivity", applySensitivity);
-    absAlg->setPropertyValue("ReductionProperties", reductionManagerName);
-    auto refscalealgProp =
-        make_unique<AlgorithmProperty>("AbsoluteScaleAlgorithm");
-    refscalealgProp->setValue(absAlg->toString());
-    reductionManager->declareProperty(std::move(refscalealgProp));
-  }
-
-  // Azimuthal averaging
-  const bool doAveraging = getProperty("DoAzimuthalAverage");
-  if (doAveraging) {
-    const std::string nBins = getPropertyValue("IQNumberOfBins");
-    const bool logBinning = getProperty("IQLogBinning");
-    const bool computeResolution = getProperty("ComputeResolution");
-
-    IAlgorithm_sptr iqAlg = createChildAlgorithm("SANSAzimuthalAverage1D");
-    iqAlg->setPropertyValue("NumberOfBins", nBins);
-    iqAlg->setProperty("LogBinning", logBinning);
-    iqAlg->setProperty("ComputeResolution", computeResolution);
-    iqAlg->setPropertyValue("ReductionProperties", reductionManagerName);
-
-    auto iqalgProp = make_unique<AlgorithmProperty>("IQAlgorithm");
-    iqalgProp->setValue(iqAlg->toString());
-    reductionManager->declareProperty(std::move(iqalgProp));
-  }
-
-  // 2D reduction
-  const bool do2DReduction = getProperty("Do2DReduction");
-  if (do2DReduction) {
-    const std::string n_bins = getPropertyValue("IQ2DNumberOfBins");
-    IAlgorithm_sptr iqAlg = createChildAlgorithm("EQSANSQ2D");
-    iqAlg->setPropertyValue("NumberOfBins", n_bins);
-    auto iqxyalgProp = make_unique<AlgorithmProperty>("IQXYAlgorithm");
-    iqxyalgProp->setValue(iqAlg->toString());
-    reductionManager->declareProperty(std::move(iqxyalgProp));
-  }
-  setPropertyValue("OutputMessage", "EQSANS reduction options set");
-}
-
-void SetupILLD33Reduction::setupSensitivity(
-    boost::shared_ptr<PropertyManager> reductionManager) {
-  const std::string reductionManagerName = getProperty("ReductionProperties");
-
-  const std::string sensitivityFile = getPropertyValue("SensitivityFile");
-  if (!sensitivityFile.empty()) {
-    const bool useSampleDC = getProperty("UseDefaultDC");
-    const std::string sensitivityDarkCurrentFile =
-        getPropertyValue("SensitivityDarkCurrentFile");
-    const std::string outputSensitivityWS =
-        getPropertyValue("OutputSensitivityWorkspace");
-    const double minEff = getProperty("MinEfficiency");
-    const double maxEff = getProperty("MaxEfficiency");
-    const double sensitivityBeamCenterX = getProperty("SensitivityBeamCenterX");
-    const double sensitivityBeamCenterY = getProperty("SensitivityBeamCenterY");
-
-    IAlgorithm_sptr effAlg = createChildAlgorithm("SANSSensitivityCorrection");
-    effAlg->setProperty("Filename", sensitivityFile);
-    effAlg->setProperty("UseSampleDC", useSampleDC);
-    effAlg->setProperty("DarkCurrentFile", sensitivityDarkCurrentFile);
-    effAlg->setProperty("MinEfficiency", minEff);
-    effAlg->setProperty("MaxEfficiency", maxEff);
-
-    // Beam center option for sensitivity data
-    const std::string centerMethod =
-        getPropertyValue("SensitivityBeamCenterMethod");
-    if (boost::iequals(centerMethod, "Value")) {
-      if (!isEmpty(sensitivityBeamCenterX) &&
-          !isEmpty(sensitivityBeamCenterY)) {
-        effAlg->setProperty("BeamCenterX", sensitivityBeamCenterX);
-        effAlg->setProperty("BeamCenterY", sensitivityBeamCenterY);
-      }
-    } else if (boost::iequals(centerMethod, "DirectBeam") ||
-               boost::iequals(centerMethod, "Scattering")) {
-      const std::string beamCenterFile =
-          getProperty("SensitivityBeamCenterFile");
-      const double sensitivityBeamRadius =
-          getProperty("SensitivityBeamCenterRadius");
-      bool useDirectBeam = boost::iequals(centerMethod, "DirectBeam");
-      if (!beamCenterFile.empty()) {
-        IAlgorithm_sptr ctrAlg = createChildAlgorithm("SANSBeamFinder");
-        ctrAlg->setProperty("Filename", beamCenterFile);
-        ctrAlg->setProperty("UseDirectBeamMethod", useDirectBeam);
-        ctrAlg->setProperty("PersistentCorrection", false);
-        if (useDirectBeam && !isEmpty(sensitivityBeamRadius))
-          ctrAlg->setProperty("BeamRadius", sensitivityBeamRadius);
-        ctrAlg->setPropertyValue("ReductionProperties", reductionManagerName);
-
-        auto sbcalgProp =
-            make_unique<AlgorithmProperty>("SensitivityBeamCenterAlgorithm");
-        sbcalgProp->setValue(ctrAlg->toString());
-        reductionManager->declareProperty(std::move(sbcalgProp));
-      } else {
-        g_log.error()
-            << "ERROR: Sensitivity beam center determination was required"
-               " but no file was provided\n";
-      }
-    }
-
-    effAlg->setPropertyValue("OutputSensitivityWorkspace", outputSensitivityWS);
-    effAlg->setPropertyValue("ReductionProperties", reductionManagerName);
-
-    auto sensalgProp = make_unique<AlgorithmProperty>("SensitivityAlgorithm");
-    sensalgProp->setValue(effAlg->toString());
-    reductionManager->declareProperty(std::move(sensalgProp));
-  }
-}
-void SetupILLD33Reduction::setupTransmission(
-    boost::shared_ptr<PropertyManager> reductionManager) {
-  const std::string reductionManagerName = getProperty("ReductionProperties");
-  // Transmission options
-  const bool thetaDependentTrans = getProperty("ThetaDependentTransmission");
-  const std::string transMethod = getProperty("TransmissionMethod");
-  const std::string darkCurrent =
-      getPropertyValue("TransmissionDarkCurrentFile");
-  const bool useSampleDC = getProperty("TransmissionUseSampleDC");
-
-  // Transmission is entered by hand
-  if (boost::iequals(transMethod, "Value")) {
-    const double transValue = getProperty("TransmissionValue");
-    const double transError = getProperty("TransmissionError");
-    if (!isEmpty(transValue) && !isEmpty(transError)) {
-      IAlgorithm_sptr transAlg =
-          createChildAlgorithm("ApplyTransmissionCorrection");
-      transAlg->setProperty("TransmissionValue", transValue);
-      transAlg->setProperty("TransmissionError", transError);
-      transAlg->setProperty("ThetaDependent", thetaDependentTrans);
-
-      auto transalgProp =
-          make_unique<AlgorithmProperty>("TransmissionAlgorithm");
-      transalgProp->setValue(transAlg->toString());
-      reductionManager->declareProperty(std::move(transalgProp));
-    } else {
-      g_log.information(
-          "SetupILLD33Reduction [TransmissionAlgorithm]:"
-          "expected transmission/error values and got empty values");
-    }
-  }
-  // Direct beam method for transmission determination
-  else if (boost::iequals(transMethod, "DirectBeam")) {
-    const std::string sampleFilename =
-        getPropertyValue("TransmissionSampleDataFile");
-    const std::string emptyFilename =
-        getPropertyValue("TransmissionEmptyDataFile");
-    const double beamRadius = getProperty("TransmissionBeamRadius");
-    const double beamX = getProperty("TransmissionBeamCenterX");
-    const double beamY = getProperty("TransmissionBeamCenterY");
-    const std::string centerMethod =
-        getPropertyValue("TransmissionBeamCenterMethod");
-
-    IAlgorithm_sptr transAlg =
-        createChildAlgorithm("SANSDirectBeamTransmission");
-    transAlg->setProperty("SampleDataFilename", sampleFilename);
-    transAlg->setProperty("EmptyDataFilename", emptyFilename);
-    transAlg->setProperty("BeamRadius", beamRadius);
-    transAlg->setProperty("DarkCurrentFilename", darkCurrent);
-    transAlg->setProperty("UseSampleDarkCurrent", useSampleDC);
-
-    // Beam center option for transmission data
-    if (boost::iequals(centerMethod, "Value") && !isEmpty(beamX) &&
-        !isEmpty(beamY)) {
-      transAlg->setProperty("BeamCenterX", beamX);
-      transAlg->setProperty("BeamCenterY", beamY);
-    } else if (boost::iequals(centerMethod, "DirectBeam")) {
-      const std::string beamCenterFile =
-          getProperty("TransmissionBeamCenterFile");
-      if (!beamCenterFile.empty()) {
-        IAlgorithm_sptr ctrAlg = createChildAlgorithm("SANSBeamFinder");
-        ctrAlg->setProperty("Filename", beamCenterFile);
-        ctrAlg->setProperty("UseDirectBeamMethod", true);
-        ctrAlg->setProperty("PersistentCorrection", false);
-        ctrAlg->setPropertyValue("ReductionProperties", reductionManagerName);
-
-        auto tbcalgProp =
-            make_unique<AlgorithmProperty>("TransmissionBeamCenterAlgorithm");
-        tbcalgProp->setValue(ctrAlg->toString());
-        reductionManager->declareProperty(std::move(tbcalgProp));
-      } else {
-        g_log.error()
-            << "ERROR: Transmission beam center determination was required"
-               " but no file was provided\n";
-      }
-    }
-    transAlg->setProperty("ThetaDependent", thetaDependentTrans);
-    auto transmissionalgProp =
-        make_unique<AlgorithmProperty>("TransmissionAlgorithm");
-    transmissionalgProp->setValue(transAlg->toString());
-    reductionManager->declareProperty(std::move(transmissionalgProp));
-  }
-}
-
-void SetupILLD33Reduction::setupBackground(
-    boost::shared_ptr<PropertyManager> reductionManager) {
-  const std::string reductionManagerName = getProperty("ReductionProperties");
-  // Background
-  const std::string backgroundFile = getPropertyValue("BackgroundFiles");
-  if (!backgroundFile.empty())
-    reductionManager->declareProperty(
-        Kernel::make_unique<PropertyWithValue<std::string>>("BackgroundFiles",
-                                                            backgroundFile));
-  else
-    return;
-
-  const std::string darkCurrent =
-      getPropertyValue("BckTransmissionDarkCurrentFile");
-  const bool bckThetaDependentTrans =
-      getProperty("BckThetaDependentTransmission");
-  const std::string bckTransMethod = getProperty("BckTransmissionMethod");
-  if (boost::iequals(bckTransMethod, "Value")) {
-    const double transValue = getProperty("BckTransmissionValue");
-    const double transError = getProperty("BckTransmissionError");
-    if (!isEmpty(transValue) && !isEmpty(transError)) {
-      IAlgorithm_sptr transAlg =
-          createChildAlgorithm("ApplyTransmissionCorrection");
-      transAlg->setProperty("TransmissionValue", transValue);
-      transAlg->setProperty("TransmissionError", transError);
-      transAlg->setProperty("ThetaDependent", bckThetaDependentTrans);
-
-      auto bckTransAlgProp =
-          make_unique<AlgorithmProperty>("BckTransmissionAlgorithm");
-      bckTransAlgProp->setValue(transAlg->toString());
-      reductionManager->declareProperty(std::move(bckTransAlgProp));
-    } else {
-      g_log.information(
-          "SetupILLD33Reduction [BckTransmissionAlgorithm]: "
-          "expected transmission/error values and got empty values");
-    }
-  } else if (boost::iequals(bckTransMethod, "DirectBeam")) {
-    const std::string sampleFilename =
-        getPropertyValue("BckTransmissionSampleDataFile");
-    const std::string emptyFilename =
-        getPropertyValue("BckTransmissionEmptyDataFile");
-    const double beamRadius = getProperty("BckTransmissionBeamRadius");
-    const double beamX = getProperty("BckTransmissionBeamCenterX");
-    const double beamY = getProperty("BckTransmissionBeamCenterY");
-    const bool thetaDependentTrans =
-        getProperty("BckThetaDependentTransmission");
-    const bool useSampleDC = getProperty("TransmissionUseSampleDC");
-
-    IAlgorithm_sptr transAlg =
-        createChildAlgorithm("SANSDirectBeamTransmission");
-    transAlg->setProperty("SampleDataFilename", sampleFilename);
-    transAlg->setProperty("EmptyDataFilename", emptyFilename);
-    transAlg->setProperty("BeamRadius", beamRadius);
-    transAlg->setProperty("DarkCurrentFilename", darkCurrent);
-    transAlg->setProperty("UseSampleDarkCurrent", useSampleDC);
-
-    // Beam center option for transmission data
-    const std::string centerMethod =
-        getPropertyValue("BckTransmissionBeamCenterMethod");
-    if (boost::iequals(centerMethod, "Value") && !isEmpty(beamX) &&
-        !isEmpty(beamY)) {
-      transAlg->setProperty("BeamCenterX", beamX);
-      transAlg->setProperty("BeamCenterY", beamY);
-    } else if (boost::iequals(centerMethod, "DirectBeam")) {
-      const std::string beamCenterFile =
-          getProperty("BckTransmissionBeamCenterFile");
-      if (!beamCenterFile.empty()) {
-        IAlgorithm_sptr ctrAlg = createChildAlgorithm("SANSBeamFinder");
-        ctrAlg->setProperty("Filename", beamCenterFile);
-        ctrAlg->setProperty("UseDirectBeamMethod", true);
-        ctrAlg->setProperty("PersistentCorrection", false);
-        ctrAlg->setPropertyValue("ReductionProperties", reductionManagerName);
-
-        auto btbcAlgProp = make_unique<AlgorithmProperty>(
-            "BckTransmissionBeamCenterAlgorithm");
-        btbcAlgProp->setValue(ctrAlg->toString());
-        reductionManager->declareProperty(std::move(btbcAlgProp));
-      } else {
-        g_log.error() << "ERROR: Beam center determination was required"
-                         " but no file was provided\n";
-      }
-    }
-    transAlg->setProperty("DarkCurrentFilename", darkCurrent);
-    transAlg->setProperty("ThetaDependent", thetaDependentTrans);
-    auto btransAlgProp =
-        make_unique<AlgorithmProperty>("BckTransmissionAlgorithm");
-    btransAlgProp->setValue(transAlg->toString());
-    reductionManager->declareProperty(std::move(btransAlgProp));
-  }
-}
-} // namespace WorkflowAlgorithms
-} // namespace Mantid
diff --git a/MantidPlot/src/ApplicationWindow.cpp b/MantidPlot/src/ApplicationWindow.cpp
index 495b55f43d2f22a23363305b1a6ad63dcadaf92e..4cb0fbb82362fa6d70866ae5f4ac133cb17402be 100644
--- a/MantidPlot/src/ApplicationWindow.cpp
+++ b/MantidPlot/src/ApplicationWindow.cpp
@@ -16834,10 +16834,7 @@ bool ApplicationWindow::saveProjectRecovery(std::string destination) {
 void ApplicationWindow::checkForProjectRecovery() {
   m_projectRecoveryRunOnStart = true;
 
-  m_projectRecovery.removeOlderCheckpoints();
-
-  // Mantid crashed during writing to this checkpoint so remove it
-  m_projectRecovery.removeLockedCheckpoints();
+  m_projectRecovery.repairCheckpointDirectory();
 
   if (!m_projectRecovery.checkForRecovery()) {
     m_projectRecovery.startProjectSaving();
diff --git a/MantidPlot/src/Mantid/MantidApplication.cpp b/MantidPlot/src/Mantid/MantidApplication.cpp
index b37e859a26a3f2deed47ec7758f3f08dbcfbd0d9..b439635135cafcdb3be217c532fdcd13a8d95626 100644
--- a/MantidPlot/src/Mantid/MantidApplication.cpp
+++ b/MantidPlot/src/Mantid/MantidApplication.cpp
@@ -29,7 +29,7 @@ Mantid::Kernel::Logger g_log("MantidApplication");
 MantidApplication::MantidApplication(int &argc, char **argv)
     : QApplication(argc, argv) {
   try {
-    Mantid::Kernel::UsageService::Instance().setApplication("mantidplot");
+    Mantid::Kernel::UsageService::Instance().setApplicationName("mantidplot");
   } catch (std::runtime_error &rexc) {
     g_log.error() << "Failed to initialize the Mantid usage service. This "
                      "is probably a sign that this Mantid is not fully or "
diff --git a/MantidPlot/src/Mantid/MantidPlotUtilities.cpp b/MantidPlot/src/Mantid/MantidPlotUtilities.cpp
index 1cdf82fda8baf477f4251484004b299d0e7228f7..1e4dc6bb65e71e529a0ca60540f01e74c378109d 100644
--- a/MantidPlot/src/Mantid/MantidPlotUtilities.cpp
+++ b/MantidPlot/src/Mantid/MantidPlotUtilities.cpp
@@ -11,6 +11,7 @@
 #include "MantidAPI/WorkspaceFactory.h"
 #include "MantidAPI/WorkspaceGroup.h"
 #include "MantidGeometry/MDGeometry/IMDDimension.h"
+#include "MantidKernel/TimeSeriesProperty.h"
 #include <MantidQtWidgets/Common/MantidDisplayBase.h>
 
 using namespace MantidQt::MantidWidgets;
@@ -48,15 +49,8 @@ double getSingleWorkspaceLogValue(
     return static_cast<double>(wsIndex); // cast for plotting
 
   // MatrixWorkspace is an ExperimentInfo
-  auto log = matrixWS->run().getLogData(logName.toStdString());
-  if (!log)
-    throw std::invalid_argument("Log not present in workspace");
-  if (dynamic_cast<Mantid::Kernel::PropertyWithValue<int> *>(log) ||
-      dynamic_cast<Mantid::Kernel::PropertyWithValue<double> *>(log))
-    return std::stod(log->value());
-
-  throw std::invalid_argument(
-      "Log is of wrong type (expected single numeric value");
+  return matrixWS->run().getLogAsSingleValue(
+      logName.toStdString(), Mantid::Kernel::Math::TimeAveragedMean);
 }
 
 /**
diff --git a/MantidPlot/src/ProjectRecovery.cpp b/MantidPlot/src/ProjectRecovery.cpp
index 3f67c8bb20cc82a93f6a8bfa36d0ec0086cd4c28..179c92099f58334473f64a6103236f99cfdf1d76 100644
--- a/MantidPlot/src/ProjectRecovery.cpp
+++ b/MantidPlot/src/ProjectRecovery.cpp
@@ -145,9 +145,19 @@ std::vector<int> orderProcessIDs(std::vector<Poco::Path> paths) {
               // Last modified is first!
               return a1.getLastModified() > b1.getLastModified();
             });
-
-  for (auto c : paths) {
-    returnValues.emplace_back(std::stoi(c.directory(c.depth() - 1)));
+  for (const auto &c : paths) {
+    try {
+      returnValues.emplace_back(std::stoi(c.directory(c.depth() - 1)));
+    } catch (std::invalid_argument &) {
+      // The folder or file here is not a number (So shouldn't exist) so delete
+      // it recursively. However perform a sanity check as recursively removing
+      // files is dangerous
+      const auto sanityCheckPath(getRecoveryFolderCheck());
+      if (sanityCheckPath ==
+          Poco::Path(c.toString()).popDirectory().toString()) {
+        Poco::File(c).remove(true);
+      }
+    }
   }
   return returnValues;
 }
@@ -654,61 +664,6 @@ void ProjectRecovery::saveWsHistories(const Poco::Path &historyDestFolder) {
   }
 }
 
-void ProjectRecovery::removeOlderCheckpoints() {
-  // Currently set to a month in microseconds
-  const int64_t timeToDeleteAfter = 2592000000000;
-  std::string recoverFolder = getRecoveryFolderCheck();
-  // Get the PIDS
-  std::vector<Poco::Path> possiblePidsPaths =
-      getListOfFoldersInDirectory(recoverFolder);
-  // Order pids based on date last modified descending
-  std::vector<int> possiblePids = orderProcessIDs(possiblePidsPaths);
-  // check if pid exists
-  std::vector<std::string> folderPaths;
-  for (auto i = 0u; i < possiblePids.size(); ++i) {
-    if (!isPIDused(possiblePids[i])) {
-      std::string folder = recoverFolder;
-      folder.append(std::to_string(possiblePids[i]) + "/");
-      if (olderThanAGivenTime(Poco::Path(folder), timeToDeleteAfter)) {
-        folderPaths.emplace_back(folder);
-      }
-    }
-  }
-
-  bool recurse = true;
-  for (size_t i = 0; i < folderPaths.size(); i++) {
-    Poco::File(folderPaths[i]).remove(recurse);
-  }
-}
-
-void ProjectRecovery::removeLockedCheckpoints() {
-  std::string recoverFolder = getRecoveryFolderCheck();
-  // Get the PIDS
-  std::vector<Poco::Path> possiblePidsPaths =
-      getListOfFoldersInDirectory(recoverFolder);
-  // Order pids based on date last modified descending
-  std::vector<int> possiblePids = orderProcessIDs(possiblePidsPaths);
-  // check if pid exists
-  std::vector<Poco::Path> files;
-  for (auto i = 0u; i < possiblePids.size(); ++i) {
-    if (!isPIDused(possiblePids[i])) {
-      std::string folder = recoverFolder;
-      folder.append(std::to_string(possiblePids[i]) + "/");
-      auto checkpointsInsidePIDs = getListOfFoldersInDirectory(folder);
-      for (auto c : checkpointsInsidePIDs) {
-        if (Poco::File(c.setFileName(LOCK_FILE_NAME)).exists()) {
-          files.emplace_back(c.setFileName(""));
-        }
-      }
-    }
-  }
-
-  bool recurse = true;
-  for (auto c : files) {
-    Poco::File(c).remove(recurse);
-  }
-}
-
 bool ProjectRecovery::olderThanAGivenTime(const Poco::Path &path,
                                           int64_t elapsedTime) {
   return Poco::File(path).getLastModified().isElapsed(elapsedTime);
@@ -767,4 +722,105 @@ std::vector<Poco::Path> ProjectRecovery::getRecoveryFolderCheckpointsPR(
   return getRecoveryFolderCheckpoints(recoveryFolderPath);
 }
 
+std::vector<std::string>
+ProjectRecovery::findOlderCheckpoints(const std::string &recoverFolder,
+                                      const std::vector<int> &possiblePids) {
+  // Currently set to a month in microseconds
+  const Poco::Timestamp::TimeDiff timeToDeleteAfter(2592000000000);
+  std::vector<std::string> folderPaths;
+  for (auto i = 0u; i < possiblePids.size(); ++i) {
+    std::string folder = recoverFolder;
+    folder.append(std::to_string(possiblePids[i]) + "/");
+    // check if the checkpoint is too old
+    if (olderThanAGivenTime(Poco::Path(folder), timeToDeleteAfter)) {
+      folderPaths.emplace_back(folder);
+    }
+  }
+  return folderPaths;
+}
+
+std::vector<std::string>
+ProjectRecovery::findLockedCheckpoints(const std::string &recoverFolder,
+                                       const std::vector<int> &possiblePids) {
+  std::vector<std::string> files;
+  for (auto i = 0u; i < possiblePids.size(); ++i) {
+    std::string folder = recoverFolder;
+    folder.append(std::to_string(possiblePids[i]) + "/");
+    auto checkpointsInsidePIDs = getListOfFoldersInDirectory(folder);
+    for (auto c : checkpointsInsidePIDs) {
+      if (Poco::File(c.setFileName(LOCK_FILE_NAME)).exists()) {
+        files.emplace_back(c.setFileName("").toString());
+      }
+    }
+  }
+  return files;
+}
+
+std::vector<std::string> ProjectRecovery::findLegacyCheckpoints(
+    const std::vector<Poco::Path> &checkpoints) {
+  std::vector<std::string> vectorToDelete;
+  for (auto c : checkpoints) {
+    const std::string PID = c.directory(c.depth() - 1);
+    // If char 11 is a T then it is a date saved as a PID which is a legacy
+    // checkpoint
+    if (PID.size() >= 11 && PID[10] == 'T') {
+      vectorToDelete.emplace_back(c.toString());
+    }
+  }
+  return vectorToDelete;
+}
+
+void ProjectRecovery::checkPIDsAreNotInUse(std::vector<int> &possiblePids) {
+  for (auto i = 0u; i < possiblePids.size(); ++i) {
+    if (isPIDused(possiblePids[i])) {
+      possiblePids.erase(possiblePids.begin() + i);
+    }
+  }
+}
+
+void ProjectRecovery::repairCheckpointDirectory() {
+  const std::string recoverFolder = getRecoveryFolderCheck();
+  std::vector<std::string> vectorToDelete;
+  std::vector<Poco::Path> checkpoints =
+      getListOfFoldersInDirectory(recoverFolder);
+  try {
+    // Grab Unused PIDs from retrieved directories
+    std::vector<int> possiblePids = orderProcessIDs(checkpoints);
+    checkPIDsAreNotInUse(possiblePids);
+
+    std::vector<std::string> tempVec = findLegacyCheckpoints(checkpoints);
+    vectorToDelete.insert(vectorToDelete.end(), tempVec.begin(), tempVec.end());
+
+    tempVec = findLockedCheckpoints(recoverFolder, possiblePids);
+    vectorToDelete.insert(vectorToDelete.end(), tempVec.begin(), tempVec.end());
+
+    tempVec = findOlderCheckpoints(recoverFolder, possiblePids);
+    vectorToDelete.insert(vectorToDelete.end(), tempVec.begin(), tempVec.end());
+
+  } catch (...) {
+    // Errors here are likely caused by older versions of mantid having crashed
+    // previously (Even though removeLegacyCheckpoints() should handle this)
+    g_log.debug("Project Recovery: During repair of checkpoint directory, "
+                "mantid has been unable to successfully handle repair so "
+                "checkpoints may be invalid");
+  }
+
+  for (auto c : vectorToDelete) {
+    // Remove c recursively
+    const auto sanityCheckPath(getRecoveryFolderCheck());
+    if (sanityCheckPath == Poco::Path(c).popDirectory().toString()) {
+      Poco::File(c).remove(true);
+    }
+  }
+
+  if (vectorToDelete.size() > 0) {
+    g_log.information("Project Recovery: A repair of the checkpoint directory "
+                      "has been perfomed");
+  }
+
+  // Handle removing empty checkpoint folders
+  checkpoints = getListOfFoldersInDirectory(recoverFolder);
+  removeEmptyFolders(checkpoints);
+}
+
 } // namespace MantidQt
diff --git a/MantidPlot/src/ProjectRecovery.h b/MantidPlot/src/ProjectRecovery.h
index fb9d666cfef401204abcaf459578b7f2414d479f..c1af1ebb79ebb9d4d7c185df19278b8057e59d49 100644
--- a/MantidPlot/src/ProjectRecovery.h
+++ b/MantidPlot/src/ProjectRecovery.h
@@ -59,9 +59,6 @@ public:
   /// Stops the background thread
   void stopProjectSaving();
 
-  /// Removes checkpoints should they be older than a month old.
-  void removeOlderCheckpoints();
-
   /// Saves a project recovery checkpoint
   void saveAll(bool autoSave = true);
 
@@ -88,8 +85,9 @@ public:
   /// Open a recovery checkpoint in the scripting window
   void openInEditor(const Poco::Path &inputFolder,
                     const Poco::Path &historyDest);
-  /// Remove checkpoints if it has lock file
-  void removeLockedCheckpoints();
+
+  /// Looks at the recovery checkpoints and repairs some faults
+  void repairCheckpointDirectory();
 
 private:
   friend class RecoveryThread;
@@ -123,9 +121,29 @@ private:
   /// Saves the current workspace's histories from Mantid
   void saveWsHistories(const Poco::Path &projectDestFile);
 
-  // Return true if the folder at the end of the path is older than a month.
+  /// Return true if the folder at the end of the path is older than a month.
   bool olderThanAGivenTime(const Poco::Path &path, int64_t elapsedTime);
 
+  /// Finds any checkpoints older than a time defined inside the function and
+  /// returns paths to them
+  std::vector<std::string>
+  findOlderCheckpoints(const std::string &recoverFolder,
+                       const std::vector<int> &possiblePids);
+
+  /// Finds any checkpoints containing a Locked file and returns paths to them
+  std::vector<std::string>
+  findLockedCheckpoints(const std::string &recoverFolder,
+                        const std::vector<int> &possiblePids);
+
+  /// Finds any checkpoints believed to be from previous versions of project
+  /// recovery and returns them
+  std::vector<std::string>
+  findLegacyCheckpoints(const std::vector<Poco::Path> &checkpoints);
+
+  /// Takes a list of potentially used PIDs and removes any used PIDs from that
+  /// list
+  void checkPIDsAreNotInUse(std::vector<int> &possiblePids);
+
   /// Background thread which runs the saving body
   std::thread m_backgroundSavingThread;
 
diff --git a/MantidPlot/src/ProjectSerialiser.cpp b/MantidPlot/src/ProjectSerialiser.cpp
index 5458948111a9fdd8c06f9a5b1e9b174aa0eb4a90..1780da0ed033afc156251067a469b48ca90d2873 100644
--- a/MantidPlot/src/ProjectSerialiser.cpp
+++ b/MantidPlot/src/ProjectSerialiser.cpp
@@ -130,7 +130,7 @@ constexpr auto PY_INTERFACE_SECTION = "pythoninterface";
 //     w = MainWindow()
 //     w.show()
 //
-QStringList SERIALISABLE_PY_INTERFACES;
+QStringList SERIALISABLE_PY_INTERFACES = {"Muon_Analysis_2"};
 
 } // namespace
 
diff --git a/Testing/Data/SystemTest/ILL/001420.nxs.md5 b/Testing/Data/SystemTest/ILL/001420.nxs.md5
deleted file mode 100644
index cfaa07241720a8aec7dfd27a3f124409fcbd2d03..0000000000000000000000000000000000000000
--- a/Testing/Data/SystemTest/ILL/001420.nxs.md5
+++ /dev/null
@@ -1 +0,0 @@
-3d7981dda48aed0b6b4abf1e8e03b118
\ No newline at end of file
diff --git a/Testing/Data/SystemTest/ILL/001422.nxs.md5 b/Testing/Data/SystemTest/ILL/001422.nxs.md5
deleted file mode 100644
index 9c7c1302b97b5de8a93ba9d84e2f4de60fbdea03..0000000000000000000000000000000000000000
--- a/Testing/Data/SystemTest/ILL/001422.nxs.md5
+++ /dev/null
@@ -1 +0,0 @@
-5dee58331051229ab32045bfb5dd19b5
\ No newline at end of file
diff --git a/Testing/Data/SystemTest/ILL/001425.nxs.md5 b/Testing/Data/SystemTest/ILL/001425.nxs.md5
deleted file mode 100644
index 79c129065e6888de9645ca2b46222f71a001b450..0000000000000000000000000000000000000000
--- a/Testing/Data/SystemTest/ILL/001425.nxs.md5
+++ /dev/null
@@ -1 +0,0 @@
-a5c7449af9f0d4a46dd06b5074d9da60
\ No newline at end of file
diff --git a/Testing/Data/SystemTest/ILL/001427.nxs.md5 b/Testing/Data/SystemTest/ILL/001427.nxs.md5
deleted file mode 100644
index 9c968d06f28f879716a5fb3a9c24887657d26c15..0000000000000000000000000000000000000000
--- a/Testing/Data/SystemTest/ILL/001427.nxs.md5
+++ /dev/null
@@ -1 +0,0 @@
-687284902a5639ad30a9fbf392531521
\ No newline at end of file
diff --git a/Testing/Data/SystemTest/ILL/001428.nxs.md5 b/Testing/Data/SystemTest/ILL/001428.nxs.md5
deleted file mode 100644
index 6afbbc85b13c8a8242d9675f041fa570236ebee5..0000000000000000000000000000000000000000
--- a/Testing/Data/SystemTest/ILL/001428.nxs.md5
+++ /dev/null
@@ -1 +0,0 @@
-899de6ba09da9137a863d420d6b5da7a
\ No newline at end of file
diff --git a/Testing/Data/SystemTest/ILL/001431.nxs.md5 b/Testing/Data/SystemTest/ILL/001431.nxs.md5
deleted file mode 100644
index 1017fc28e91af616069a465ee84dfe5b426fdb16..0000000000000000000000000000000000000000
--- a/Testing/Data/SystemTest/ILL/001431.nxs.md5
+++ /dev/null
@@ -1 +0,0 @@
-37685dac3fc533fe858ea492ad08f586
\ No newline at end of file
diff --git a/Testing/Data/UnitTest/LaB6_10kev_35deg.bin.md5 b/Testing/Data/UnitTest/LaB6_10kev_35deg.bin.md5
new file mode 100644
index 0000000000000000000000000000000000000000..9f7288e848deb84f4be176cb05ac24d4f58e059d
--- /dev/null
+++ b/Testing/Data/UnitTest/LaB6_10kev_35deg.bin.md5
@@ -0,0 +1 @@
+589a35e60d17e38fc613dcd4009c84ce
diff --git a/Testing/SystemTests/tests/analysis/GUIStartupTest.py b/Testing/SystemTests/tests/analysis/GUIStartupTest.py
new file mode 100644
index 0000000000000000000000000000000000000000..8b0eea635a971f9a447d6b01ceebb6820cf165df
--- /dev/null
+++ b/Testing/SystemTests/tests/analysis/GUIStartupTest.py
@@ -0,0 +1,70 @@
+# Mantid Repository : https://github.com/mantidproject/mantid
+#
+# Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
+#     NScD Oak Ridge National Laboratory, European Spallation Source
+#     & Institut Laue - Langevin
+# SPDX - License - Identifier: GPL - 3.0 +
+from __future__ import (absolute_import, division, print_function)
+import os
+import platform
+import subprocess
+import systemtesting
+import mantid
+
+
+class GUIStartupTest(systemtesting.MantidSystemTest):
+    '''This test is supposed to start MantidPlot, and execute a simple
+    script If the script is run correctly, the "Hello Mantid" will be
+    printed to stdout If the script fails, there will be a fatal error
+    mesage in stderr
+    '''
+
+    def __init__(self):
+        super(GUIStartupTest, self).__init__()
+        self.maxDiff = None
+        # create simple script
+        self.script = os.path.join(mantid.config.getString('defaultsave.directory'),
+                                   'GUIStartupTest_script.py')
+        with open(self.script, 'w') as f:
+            f.write('import mantid\n'
+                    'print("Hello Mantid")\n')
+        # get the mantidplot executable
+        current_platform = platform.platform()
+        mantid_init_dirname = os.path.dirname(os.path.abspath(mantid.__file__))
+        if 'Linux' in current_platform:
+            mantid_exe = os.path.join(mantid_init_dirname, "../launch_mantidplot.sh")
+        elif 'Darwin' in current_platform:
+            mantid_exe = os.path.join(mantid_init_dirname, "../MantidPlot")
+        elif 'Windows' in current_platform:
+            mantid_exe = 'wscript.exe {}'.format(os.path.join(mantid_init_dirname,
+                                                              "../bin/launch_mantidplot.vbs"))
+        else:
+            raise RuntimeError("Unknown operating system")
+
+        # verify the launch script exists
+        if os.path.exists(mantid_exe):
+            self.cmd = '{0} -xq {1}'.format(mantid_exe, self.script)
+        else:
+            self.cmd = None
+
+    def skipTests(self):
+        return self.cmd is None
+
+    def cleanup(self):
+        if os.path.exists(self.script):
+            os.remove(self.script)
+
+    def runTest(self):
+        # good startup
+        p = subprocess.Popen(self.cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
+        out, err = p.communicate()
+        self.assertEquals(out, b'Hello Mantid\n')
+
+        # failing script
+        with open(self.script, 'a') as f:
+            f.write('raise RuntimeError("GUITest")')
+        p = subprocess.Popen(self.cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
+        out, err = p.communicate()
+        out += err
+        self.assertTrue(b'Hello Mantid\n' in out)
+        self.assertTrue(b'RuntimeError: GUITest' in out)
diff --git a/Testing/SystemTests/tests/analysis/ILLD33Test.py b/Testing/SystemTests/tests/analysis/ILLD33Test.py
deleted file mode 100644
index 61ad85c60f7babe5b09de8ebe8c540647afcb51c..0000000000000000000000000000000000000000
--- a/Testing/SystemTests/tests/analysis/ILLD33Test.py
+++ /dev/null
@@ -1,120 +0,0 @@
-# Mantid Repository : https://github.com/mantidproject/mantid
-#
-# Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
-#     NScD Oak Ridge National Laboratory, European Spallation Source
-#     & Institut Laue - Langevin
-# SPDX - License - Identifier: GPL - 3.0 +
-#pylint: disable=no-init
-import systemtesting
-
-from mantid.api import mtd
-import mantid.simpleapi as ms
-
-import unittest
-
-
-class ILLD33SANSTest(unittest.TestCase):
-
-    prefix = "D33"
-
-    def tearDown(self):
-        for wsName in mtd.getObjectNames():
-            if wsName.startswith(self.prefix):
-                mtd.remove(wsName)
-
-    def test_all(self):
-
-        ms.SetupILLD33Reduction(
-            # Beam center shouldn't work
-            #BeamCenterMethod="None",
-            MaskedDetectorList=[14709,14710,14711,14712,14713,14714,14715,14716,14717,14718,14719,
-                                14720,14721,14722,14723,14724,14725,14726,14727,14728,14729,14730,
-                                14731,14732,14733,14734,14735,14965,14966,14967,14968,14969,14970,
-                                14971,14972,14973,14974,14975,14976,14977,14978,14979,14980,14981,
-                                14982,14983,14984,14985,14986,14987,14988,14989,14990,14991,15221,
-                                15222,15223,15224,15225,15226,15227,15228,15229,15230,15231,15232,
-                                15233,15234,15235,15236,15237,15238,15239,15240,15241,15242,15243,
-                                15244,15245,15246,15247,15477,15478,15479,15480,15481,15482,15483,
-                                15484,15485,15486,15487,15488,15489,15490,15491,15492,15493,15494,
-                                15495,15496,15497,15498,15499,15500,15501,15502,15503,15733,15734,
-                                15735,15736,15737,15738,15739,15740,15741,15742,15743,15744,15745,
-                                15746,15747,15748,15749,15750,15751,15752,15753,15754,15755,15756,
-                                15757,15758,15759,15989,15990,15991,15992,15993,15994,15995,15996,
-                                15997,15998,15999,16000,16001,16002,16003,16004,16005,16006,16007,
-                                16008,16009,16010,16011,16012,16013,16014,16015,16245,16246,16247,
-                                16248,16249,16250,16251,16252,16253,16254,16255,16256,16257,16258,
-                                16259,16260,16261,16262,16263,16264,16265,16266,16267,16268,16269,
-                                16270,16271,16501,16502,16503,16504,16505,16506,16507,16508,16509,
-                                16510,16511,16512,16513,16514,16515,16516,16517,16518,16519,16520,
-                                16521,16522,16523,16524,16525,16526,16527,16757,16758,16759,16760,
-                                16761,16762,16763,16764,16765,16766,16767,16768,16769,16770,16771,
-                                16772,16773,16774,16775,16776,16777,16778,16779,16780,16781,16782,
-                                16783,17013,17014,17015,17016,17017,17018,17019,17020,17021,17022,
-                                17023,17024,17025,17026,17027,17028,17029,17030,17031,17032,17033,
-                                17034,17035,17036,17037,17038,17039,17269,17270,17271,17272,17273,
-                                17274,17275,17276,17277,17278,17279,17280,17281,17282,17283,17284,
-                                17285,17286,17287,17288,17289,17290,17291,17292,17293,17294,17295,
-                                17525,17526,17527,17528,17529,17530,17531,17532,17533,17534,17535,
-                                17536,17537,17538,17539,17540,17541,17542,17543,17544,17545,17546,
-                                17547,17548,17549,17550,17551],
-            BeamCenterMethod="DirectBeam",
-            BeamCenterFile='ILL/001427.nxs',
-            Normalisation="Timer",
-            DarkCurrentFile= 'ILL/001420.nxs',
-            TransmissionMethod="DirectBeam",
-            TransmissionSampleDataFile= 'ILL/001431.nxs',
-            TransmissionEmptyDataFile= 'ILL/001427.nxs',
-            BckTransmissionEmptyDataFile= 'ILL/001427.nxs',
-            TransmissionBeamRadius = 3,
-            TransmissionUseSampleDC=False,
-            BackgroundFiles='ILL/001422.nxs',
-            BckTransmissionSampleDataFile='ILL/001428.nxs',
-            DoAzimuthalAverage=False,
-            Do2DReduction=False,
-            ComputeResolution=True,
-            ReductionProperties=self.prefix + "props")
-
-        ms.SANSReduction(Filename='ILL/001425.nxs', ReductionProperties=self.prefix + "props",
-                         OutputWorkspace=self.prefix + "out")
-        ms.Rebin(InputWorkspace=self.prefix + 'out',OutputWorkspace=self.prefix + 'out_rebin',
-                 Params='4,0.1,15')
-        ms.SANSAzimuthalAverage1D(InputWorkspace=self.prefix + 'out_rebin',Binning='0.001,0.0002,0.03',
-                                  OutputWorkspace=self.prefix + 'final')
-
-        # Check some data
-        wsOut = mtd[self.prefix + 'out']
-        self.assertEqual(wsOut.getNumberHistograms(), 65538)
-        wsOut = mtd[self.prefix + 'out_rebin']
-        self.assertEqual(wsOut.getNumberHistograms(), 65538)
-        wsOut = mtd[self.prefix + 'final']
-        self.assertEqual(wsOut.getNumberHistograms(), 1)
-
-    #================== Failure cases ================================
-
-    # TODO
-
-
-#====================================================================================
-
-class ILLD33Test(systemtesting.MantidSystemTest):
-    _success = False
-
-    def requiredMemoryMB(self):
-        """Set a limit of 2.5Gb to avoid 32-bit environment"""
-        return 2500
-
-    def runTest(self):
-        self._success = False
-        # Custom code to create and run this single test suite
-        suite = unittest.TestSuite()
-        suite.addTest( unittest.makeSuite(ILLD33SANSTest, "test") )
-        runner = unittest.TextTestRunner()
-        # Run using either runner
-        res = runner.run(suite)
-        if res.wasSuccessful():
-            self._success = True
-        else:
-            self._success = False
-
-    def validate(self):
-        return self._success
diff --git a/Testing/SystemTests/tests/analysis/MagnetismReflectometryReductionTest.py b/Testing/SystemTests/tests/analysis/MagnetismReflectometryReductionTest.py
index 041c7b90bb20938d07352d29bcd6fbfb7b02e188..f6f5129c423b505c9c21ee3ebcd74c1071ed32cd 100644
--- a/Testing/SystemTests/tests/analysis/MagnetismReflectometryReductionTest.py
+++ b/Testing/SystemTests/tests/analysis/MagnetismReflectometryReductionTest.py
@@ -78,6 +78,38 @@ class MagnetismReflectometryReductionConstQTest(systemtesting.MantidSystemTest):
         return math.fabs(refl[1] - 0.648596877775159) < 0.002
 
 
+class MagnetismReflectometryReductionSkipRebinTest(systemtesting.MantidSystemTest):
+    def runTest(self):
+        wsg = MRFilterCrossSections(Filename="REF_M_24949")
+        MagnetismReflectometryReduction(InputWorkspace=wsg[0],
+                                        NormalizationRunNumber=24945,
+                                        SignalPeakPixelRange=[125, 129],
+                                        SubtractSignalBackground=True,
+                                        SignalBackgroundPixelRange=[15, 105],
+                                        ApplyNormalization=True,
+                                        NormPeakPixelRange=[201, 205],
+                                        SubtractNormBackground=True,
+                                        NormBackgroundPixelRange=[10,127],
+                                        CutLowResDataAxis=True,
+                                        LowResDataAxisPixelRange=[91, 161],
+                                        CutLowResNormAxis=True,
+                                        LowResNormAxisPixelRange=[86, 174],
+                                        CutTimeAxis=True,
+                                        UseWLTimeAxis=False,
+                                        FinalRebin=False,
+                                        QMin=0.005,
+                                        QStep=-0.01,
+                                        TimeAxisStep=40,
+                                        TimeAxisRange=[25000, 54000],
+                                        SpecularPixel=126.9,
+                                        ConstantQBinning=False,
+                                        OutputWorkspace="r_24949")
+
+    def validate(self):
+        q_values = mtd["r_24949"].dataX(0)
+        return math.fabs(q_values[0] - 0.005) > 0.001
+
+
 class MagnetismReflectometryReductionConstQWLCutTest(systemtesting.MantidSystemTest):
     def runTest(self):
         wsg = MRFilterCrossSections(Filename="REF_M_24949")
diff --git a/Testing/SystemTests/tests/analysis/SANSILLReductionTest.py b/Testing/SystemTests/tests/analysis/SANSILLReductionTest.py
index baed1c054ced1745f9398fc0b9d37f543ce55303..8b233744a0a2d903b8a61638c6ac1c700aa2e117 100644
--- a/Testing/SystemTests/tests/analysis/SANSILLReductionTest.py
+++ b/Testing/SystemTests/tests/analysis/SANSILLReductionTest.py
@@ -26,6 +26,7 @@ class ILL_D11_Test(systemtesting.MantidSystemTest):
 
     def validate(self):
         self.tolerance = 1e-5
+        self.disableChecking = ['Instrument']
         return ['iq', 'ILL_SANS_D11_IQ.nxs']
 
     def runTest(self):
@@ -85,6 +86,7 @@ class ILL_D22_Test(systemtesting.MantidSystemTest):
 
     def validate(self):
         self.tolerance = 1e-5
+        self.disableChecking = ['Instrument']
         return ['iq', 'ILL_SANS_D22_IQ.nxs']
 
     def runTest(self):
@@ -145,6 +147,7 @@ class ILL_D33_VTOF_Test(systemtesting.MantidSystemTest):
 
     def validate(self):
         self.tolerance = 1e-5
+        self.disableChecking = ['Instrument']
         return ['iq', 'ILL_SANS_D33_VTOF_IQ.nxs']
 
     def runTest(self):
@@ -188,6 +191,7 @@ class ILL_D33_LTOF_Test(systemtesting.MantidSystemTest):
 
     def validate(self):
         self.tolerance = 1e-5
+        self.disableChecking = ['Instrument']
         return ['iq', 'ILL_SANS_D33_LTOF_IQ.nxs']
 
     def runTest(self):
@@ -232,6 +236,7 @@ class ILL_D33_Test(systemtesting.MantidSystemTest):
 
     def validate(self):
         self.tolerance = 1e-5
+        self.disableChecking = ['Instrument']
         return ['iq', 'ILL_SANS_D33_IQ.nxs']
 
     def runTest(self):
diff --git a/buildconfig/CMake/FindPyUnitTest.cmake b/buildconfig/CMake/FindPyUnitTest.cmake
index df9ad62931979d5f26a48b9e7840c96b936998aa..5ac987e2e6291f92d6adc0e70198daeafd859e0e 100644
--- a/buildconfig/CMake/FindPyUnitTest.cmake
+++ b/buildconfig/CMake/FindPyUnitTest.cmake
@@ -2,6 +2,8 @@
 # PYUNITTEST_ADD_TEST (public macro to add unit tests)
 #   Adds a set of python tests based upon the unittest module
 #
+#   The variable PYUNITTEST_PYTHONPATH_EXTRA can be defined with
+#   extra paths to add to PYTHONPATH during the tests
 #   Parameters:
 #       _test_src_dir_base :: A base directory when added to the relative test paths gives
 #                             an absolute path to that test. This directory is added to the
@@ -17,27 +19,27 @@ function ( PYUNITTEST_ADD_TEST _test_src_dir _testname_prefix )
   else()
     set ( _module_dir ${CMAKE_BINARY_DIR}/bin )
   endif()
+
   set ( _test_runner ${_module_dir}/mantidpython )
   if ( WIN32 )
     set ( _test_runner ${_test_runner}.bat )
   endif ()
   set ( _test_runner_module ${CMAKE_SOURCE_DIR}/Framework/PythonInterface/test/testhelpers/testrunner.py )
+
   # Environment
   if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
-    set ( _python_path ${PYTHON_XMLRUNNER_DIR};${_test_src_dir};$ENV{PYTHONPATH} )
+    set ( _python_path ${PYTHON_XMLRUNNER_DIR};${_test_src_dir};${PYUNITTEST_PYTHONPATH_EXTRA}$ENV{PYTHONPATH} )
     # cmake list separator and Windows environment separator are the same so escape the cmake one
     string ( REPLACE ";" "\\;" _python_path "${_python_path}" )
   else()
-    set ( _python_path ${PYTHON_XMLRUNNER_DIR}:${_test_src_dir}:$ENV{PYTHONPATH} )
+    string ( REPLACE ";" ":" _python_path "${PYUNITTEST_PYTHONPATH_EXTRA}" )
+    set ( _python_path ${PYTHON_XMLRUNNER_DIR}:${_test_src_dir}:${_python_path}:$ENV{PYTHONPATH} )
   endif()
   # Define the environment
   list ( APPEND _test_environment "PYTHONPATH=${_python_path}" )
   if ( PYUNITTEST_QT_API )
     list ( APPEND _test_environment "QT_API=${PYUNITTEST_QT_API}" )
   endif()
-  if ( PYUNITTEST_TESTRUNNER_IMPORT_MANTID )
-    list ( APPEND _test_environment "TESTRUNNER_IMPORT_MANTID=1" )
-  endif()
 
   # Add all of the individual tests so that they can be run in parallel
   foreach ( part ${ARGN} )
diff --git a/buildconfig/CMake/LinuxPackageScripts.cmake b/buildconfig/CMake/LinuxPackageScripts.cmake
index 8fb1be25b6be2633e98510ccc7f2dab2ece4510a..7c48de4f85345676a1ec33872bf06c6ecc4b994e 100644
--- a/buildconfig/CMake/LinuxPackageScripts.cmake
+++ b/buildconfig/CMake/LinuxPackageScripts.cmake
@@ -36,24 +36,21 @@ set ( CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION /opt /usr/share/applications
 # default shell (bash-like)
 file ( WRITE ${CMAKE_CURRENT_BINARY_DIR}/mantid.sh
   "#!/bin/sh\n"
-  "MANTIDPATH=${CMAKE_INSTALL_PREFIX}/${BIN_DIR}\n"
   "PV_PLUGIN_PATH=${CMAKE_INSTALL_PREFIX}/${PVPLUGINS_DIR}\n"
-  "PATH=$PATH:$MANTIDPATH\n"
+  "PATH=$PATH:${CMAKE_INSTALL_PREFIX}/${BIN_DIR}\n"
 
-  "export MANTIDPATH PV_PLUGIN_PATH PATH\n"
+  "export PV_PLUGIN_PATH PATH\n"
 )
 
 # c-shell
 file ( WRITE ${CMAKE_CURRENT_BINARY_DIR}/mantid.csh
   "#!/bin/csh\n"
-  "setenv MANTIDPATH \"${CMAKE_INSTALL_PREFIX}/${BIN_DIR}\"\n"
   "setenv PV_PLUGIN_PATH \"${CMAKE_INSTALL_PREFIX}/${PVPLUGINS_DIR}\"\n"
-  "setenv PATH \"\${PATH}:\${MANTIDPATH}\"\n"
+  "setenv PATH \"\${PATH}:${CMAKE_INSTALL_PREFIX}/${BIN_DIR}\"\n"
 )
 
 install ( PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/mantid.sh
   ${CMAKE_CURRENT_BINARY_DIR}/mantid.csh
-  ${CMAKE_CURRENT_BINARY_DIR}/mantid.pth
   DESTINATION ${ETC_DIR}
 )
 
@@ -66,10 +63,15 @@ print(sc.get_python_lib(plat_specific=True))"
   OUTPUT_VARIABLE PYTHON_SITE
   OUTPUT_STRIP_TRAILING_WHITESPACE)
 
-file ( WRITE ${CMAKE_CURRENT_BINARY_DIR}/mantid.pth
+file ( WRITE ${CMAKE_CURRENT_BINARY_DIR}/mantid.pth.install
   "${CMAKE_INSTALL_PREFIX}/${BIN_DIR}\n"
 )
 
+install ( FILES ${CMAKE_CURRENT_BINARY_DIR}/mantid.pth.install
+  DESTINATION ${ETC_DIR}
+  RENAME mantid.pth
+)
+
 ############################################################################
 # Setup file variables for pre/post installation
 # These are very different depending on the distribution so are contained
@@ -181,9 +183,6 @@ else
     TCM_REPORT=\${TCMALLOC_LARGE_ALLOC_REPORT_THRESHOLD}
 fi" )
 
-# chunk of code for fixing MANTIDPATH
-set ( MTD_PATH_DEFINITION "MANTIDPATH=\${INSTALLDIR}/bin" )
-
 # chunk of code for launching gdb
 set ( GDB_DEFINITIONS
 "# run with gdb THIS OPTION MUST BE SUPPLIED FIRST
diff --git a/buildconfig/CMake/Packaging/launch_mantidplot.bat b/buildconfig/CMake/Packaging/launch_mantidplot.bat
index 8a0c0ca176dcd98f6b7cbc04fc1be183ea0c6940..872f71d7fa6c6733d7cb763a7e2d130eb5fb9bea 100755
--- a/buildconfig/CMake/Packaging/launch_mantidplot.bat
+++ b/buildconfig/CMake/Packaging/launch_mantidplot.bat
@@ -2,7 +2,7 @@
 setlocal enableextensions
 :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
 :: Launch script for MantidPlot
-:: 
+::
 :: Sets the required environment variables for MantidPlot to run correctly.
 :: All variables that are passed to this script are passed directly to
 :: MantidPlot.exe
@@ -24,7 +24,6 @@ set _EXTRA_PATH_DIRS=%_INSTALL_DIR%\bin;%_INSTALL_DIR%\plugins\qt4
 :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
 :: Required environment variables for Mantid
 :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
-set MANTIDPATH=%_BIN_DIR%
 set PATH=%_EXTRA_PATH_DIRS%;%PATH%
 set PV_PLUGIN_PATH=%_INSTALL_DIR%\plugins\paraview\qt4
 :: Matplotlib backend should default to Qt if not set (requires matplotlib >= 1.5)
@@ -44,4 +43,4 @@ start "MantidPlot" /B /WAIT %_BIN_DIR%\MantidPlot.exe %*
 :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
 if %errorlevel% NEQ 0 (
 python %_INSTALL_DIR%\scripts\ErrorReporter\error_dialog_app.py --exitcode=%1 --directory=%_BIN_DIR%
-)
\ No newline at end of file
+)
diff --git a/buildconfig/CMake/Packaging/launch_mantidplot.sh.in b/buildconfig/CMake/Packaging/launch_mantidplot.sh.in
index dc4c9d534d9ecfa1500ea81080660b4553a1328a..0ba17b9ad15420e63018f7fd27d534876159d876 100644
--- a/buildconfig/CMake/Packaging/launch_mantidplot.sh.in
+++ b/buildconfig/CMake/Packaging/launch_mantidplot.sh.in
@@ -9,8 +9,6 @@ THISFILE=$(readlink -f "$0")
 INSTALLDIR=$(dirname $THISFILE)   # directory of executable
 INSTALLDIR=$(dirname $INSTALLDIR) # root install directory
 
-@MTD_PATH_DEFINITION@
-
 @TCMALLOC_DEFINITIONS@
 
 @VIRTUAL_GL_WRAPPER@
diff --git a/buildconfig/CMake/Packaging/launch_mantidworkbench.sh.in b/buildconfig/CMake/Packaging/launch_mantidworkbench.sh.in
index 25a5d1354b675f266e61a519bb321768cf9d066f..476e533daac4a1a18b2615862db746548e937faf 100644
--- a/buildconfig/CMake/Packaging/launch_mantidworkbench.sh.in
+++ b/buildconfig/CMake/Packaging/launch_mantidworkbench.sh.in
@@ -9,8 +9,6 @@ THISFILE=$(readlink -f "$0")
 INSTALLDIR=$(dirname $THISFILE)   # directory of executable
 INSTALLDIR=$(dirname $INSTALLDIR) # root install directory
 
-@MTD_PATH_DEFINITION@
-
 @TCMALLOC_DEFINITIONS@
 
 @VIRTUAL_GL_WRAPPER@
diff --git a/buildconfig/CMake/Packaging/mantidpython.bat.in b/buildconfig/CMake/Packaging/mantidpython.bat.in
index 6359d97741b8b2cfaaeb2ec2717514788512ba79..4a3d510d80548fdf90430ee2447d0907a7a15c65 100644
--- a/buildconfig/CMake/Packaging/mantidpython.bat.in
+++ b/buildconfig/CMake/Packaging/mantidpython.bat.in
@@ -2,7 +2,7 @@
 setlocal enableextensions
 :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
 :: Launch script for command line python
-:: 
+::
 :: Sets the required environment variables for the Python to run correctly.
 :: All variables that are passed to this script are passed directly to
 :: python.exe
@@ -14,8 +14,7 @@ set _BIN_DIR=%_SCRIPT_DIR:~,-1%
 :: Set paths for dependencies, including Python
 @MANTIDPYTHON_PREAMBLE@
 
-set MANTIDPATH=%_BIN_DIR%
-set PYTHONPATH=%MANTIDPATH%;%PYTHONPATH%@PARAVIEW_PYTHON_PATHS@
+set PYTHONPATH=%_BIN_DIR%;%PYTHONPATH%@PARAVIEW_PYTHON_PATHS@
 
 :: Python drivers
 set _PYTHON_EXE=%PYTHONHOME%\python.exe
diff --git a/buildconfig/CMake/Packaging/mantidpython.in b/buildconfig/CMake/Packaging/mantidpython.in
index 5904a86f1ec2092e12cc1bb43dbb1af8fd03bb3e..c96304ffa464f047d88ec95a74f6dde1d20230bd 100755
--- a/buildconfig/CMake/Packaging/mantidpython.in
+++ b/buildconfig/CMake/Packaging/mantidpython.in
@@ -9,8 +9,6 @@ THISFILE=$(readlink -f "$0")
 INSTALLDIR=$(dirname $THISFILE)   # directory of executable
 INSTALLDIR=$(dirname $INSTALLDIR) # root install directory
 
-@MTD_PATH_DEFINITION@
-
 @TCMALLOC_DEFINITIONS@
 
 LOCAL_LDPATH=@EXTRA_LDPATH@
diff --git a/buildconfig/CMake/Packaging/osx/mantidpython.in b/buildconfig/CMake/Packaging/osx/mantidpython.in
index 51b9910c5d9054519344194894fcb9cc942829d1..4c758b0af004bbe9e7372733deb12f66720a3889 100755
--- a/buildconfig/CMake/Packaging/osx/mantidpython.in
+++ b/buildconfig/CMake/Packaging/osx/mantidpython.in
@@ -25,22 +25,15 @@ if [ -n "${PYTHONPATH}" ]; then
 	LOCAL_PYTHONPATH=${LOCAL_PYTHONPATH}:${PYTHONPATH}
 fi
 
-# Define MANTIDPATH
-MANTIDPATH="${INSTALLDIR}"
 if [ -n "$1" ] && [ "$1" = "--classic" ]; then
     shift
-
     set -- @WRAPPER_PREFIX@@PYTHON_EXECUTABLE@ $*@WRAPPER_POSTFIX@
-
 else
     IPYTHON_STARTUP="import IPython;IPython.start_ipython()"
-
     set -- @WRAPPER_PREFIX@@PYTHON_EXECUTABLE@ -c "${IPYTHON_STARTUP}" $*@WRAPPER_POSTFIX@
-
 fi
 
 
 PV_PLUGIN_PATH=${PV_PLUGIN_PATH} \
-  MANTIDPATH=${MANTIDPATH} \
   PYTHONPATH=${LOCAL_PYTHONPATH} \
   exec "$@"
diff --git a/buildconfig/CMake/WindowsNSIS.cmake b/buildconfig/CMake/WindowsNSIS.cmake
index 0a3a5210c8945df391167f9f6b3a0af142b5b14e..ce43ee33addeee0b2eb717ff5e69458278832480 100644
--- a/buildconfig/CMake/WindowsNSIS.cmake
+++ b/buildconfig/CMake/WindowsNSIS.cmake
@@ -75,9 +75,11 @@ endforeach()
 # Other third party dependencies
 set ( BOOST_DIST_DLLS
     boost_date_time-mt.dll
+    boost_filesystem-mt.dll
     boost_python27-mt.dll
     boost_regex-mt.dll
     boost_serialization-mt.dll
+    boost_system-mt.dll
 )
 set ( POCO_DIST_DLLS
     PocoCrypto64.dll
@@ -248,4 +250,4 @@ if ( PACKAGE_WORKBENCH )
     Delete \\\"$DESKTOP\\\\${MANTIDWORKBENCH_LINK_NAME}\\\"
     ${CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS}
   ")
-endif ()
\ No newline at end of file
+endif ()
diff --git a/buildconfig/Jenkins/buildscript b/buildconfig/Jenkins/buildscript
index e71ef1ce819b997ba276b9f47d7618493cb18fe6..60e94054cede663f25cdbe4f0d4c1ed87273fbd8 100755
--- a/buildconfig/Jenkins/buildscript
+++ b/buildconfig/Jenkins/buildscript
@@ -17,11 +17,9 @@ SCRIPT_DIR=$(dirname "$0")
 USE_CORE_DUMPS=true
 if [[ ${NODE_LABELS} == *rhel7* ]] || [[ ${NODE_LABELS} == *centos7* ]] || [[ ${NODE_LABELS} == *scilin7* ]]; then
   ON_RHEL7=true
-fi
-if [[ ${NODE_LABELS} == *ubuntu* ]]; then
+elif [[ ${NODE_LABELS} == *ubuntu* ]]; then
   ON_UBUNTU=true
-fi
-if [[ ${NODE_LABELS} == *osx* ]]; then
+elif [[ ${NODE_LABELS} == *osx* ]]; then
   ON_MACOS=true
   USE_CORE_DUMPS=false
 fi
@@ -42,6 +40,17 @@ trap onexit INT TERM EXIT
 ###############################################################################
 export PARAVIEW_DIR=${PARAVIEW_DIR}
 
+###############################################################################
+# Wraps calls for gui emulation
+###############################################################################
+if [ $(command -v xvfb-run) ]; then
+  X11_RUNNER="xvfb-run"
+  X11_RUNNER_ARGS=--server-args="-screen 0 640x480x24"
+else
+  X11_RUNNER="eval"
+  X11_RUNNER_ARGS=""
+fi
+
 ###############################################################################
 # Print out the versions of things we are using
 ###############################################################################
@@ -347,7 +356,7 @@ userprops_file=$userconfig_dir/Mantid.user.properties
 echo MultiThreaded.MaxCores=2 > $userprops_file
 
 if [[ ${DO_UNITTESTS} == true ]]; then
-  $CTEST_EXE -j${BUILD_THREADS:?} --schedule-random --output-on-failure
+  ${X11_RUNNER} "${X11_RUNNER_ARGS}" $CTEST_EXE -j${BUILD_THREADS:?} --schedule-random --output-on-failure
 fi
 
 ###############################################################################
@@ -362,8 +371,8 @@ if [[ ${DO_DOCTESTS_USER} == true ]]; then
     rm -fr $BUILD_DIR/docs/doctrees/*
   fi
   # Build HTML to verify that no referencing errors have crept in.
-  ${CMAKE_EXE} --build . --target docs-html
-  ${CMAKE_EXE} --build . --target docs-test
+  ${X11_RUNNER} "${X11_RUNNER_ARGS}" ${CMAKE_EXE} --build . --target docs-html
+  ${X11_RUNNER} "${X11_RUNNER_ARGS}" ${CMAKE_EXE} --build . --target docs-test
 fi
 
 ###############################################################################
@@ -382,12 +391,7 @@ fi
 # documentation
 ###############################################################################
 if [[ ${DO_BUILD_PKG} == true ]]; then
-  # Workaround so that the target can find the properties file
-  # CMake doesn't easily allow environment variables on custom targets
-  if [[ ${ON_MACOS} == true ]]; then
-    export MANTIDPATH=$PWD/bin
-  fi
-  ${CMAKE_EXE} --build . --target docs-qthelp
+  ${X11_RUNNER} "${X11_RUNNER_ARGS}" ${CMAKE_EXE} --build . --target docs-qthelp
   ${CPACK_EXE}
 
   # Source tarball on clean build (arbitrarily choose rhel7)
@@ -395,7 +399,7 @@ if [[ ${DO_BUILD_PKG} == true ]]; then
   # and labelled by the commit id it was built with. This assumes the Jenkins git plugin
   # has set the GIT_COMMIT environment variable
   if [[ ${CLEANBUILD} == true && ${ON_RHEL7} == true ]]; then
-    ${CMAKE_EXE} --build . --target docs-html
+    ${X11_RUNNER} "${X11_RUNNER_ARGS}" ${CMAKE_EXE} --build . --target docs-html
     tar -cjf mantiddocs-g${GIT_COMMIT:0:7}.tar.bz2 --exclude='*.buildinfo' --exclude="MantidProject.q*" docs/html
     # The ..._PREFIX argument avoids opt/Mantid directories at the top of the tree
     ${CPACK_EXE} --config CPackSourceConfig.cmake -D CPACK_PACKAGING_INSTALL_PREFIX=
diff --git a/buildconfig/Jenkins/systemtests b/buildconfig/Jenkins/systemtests
index 5e76c08ac789fea00e9bed334899cb94eed393ab..fe04694e0c2436bc9593f4188a4afb29beae0213 100755
--- a/buildconfig/Jenkins/systemtests
+++ b/buildconfig/Jenkins/systemtests
@@ -18,6 +18,17 @@ else
 fi
 $CMAKE_EXE --version
 
+###############################################################################
+# Wraps calls for gui emulation
+###############################################################################
+if [ $(command -v xvfb-run) ]; then
+  X11_RUNNER="xvfb-run"
+  X11_RUNNER_ARGS=--server-args="-screen 0 640x480x24"
+else
+  X11_RUNNER="eval"
+  X11_RUNNER_ARGS=""
+fi
+
 ###############################################################################
 # Set up the location for the local object store outside of the build and
 # source tree, which can be shared by multiple builds.
@@ -72,4 +83,4 @@ find ${default_save_directory} -name "*-mismatch*" -delete
 
 # Run
 PKGDIR=${WORKSPACE}/build
-python $WORKSPACE/Testing/SystemTests/scripts/InstallerTests.py -o -d $PKGDIR -j ${BUILD_THREADS:?} $EXTRA_ARGS
+${X11_RUNNER} "${X11_RUNNER_ARGS}" python $WORKSPACE/Testing/SystemTests/scripts/InstallerTests.py -o -d $PKGDIR -j ${BUILD_THREADS:?} $EXTRA_ARGS
diff --git a/dev-docs/source/JenkinsConfiguration.rst b/dev-docs/source/JenkinsConfiguration.rst
index c578d67f7b32b0c60a3e9723de292bc927444b92..ddc5cb2573651aaedc76896ae0f086df7564e2cd 100644
--- a/dev-docs/source/JenkinsConfiguration.rst
+++ b/dev-docs/source/JenkinsConfiguration.rst
@@ -101,14 +101,9 @@ Service" has completed you should
 Linux
 -----
 
-Install an ssh server.
+Install an ssh server, ``ccache``, ``curl`` and ``xvfb``.
 
-Install ``ccache``. After installing run ``ccache --max-size=20G`` from the ``builder`` account.
-
-Install a vnc server and from the ``builder`` account run ``vncpasswd`` to set a password on the VNC server. It
-can be any password.
-
-Ensure ``curl`` is installed
+From the ``builder`` account run ``ccache --max-size=20G``.
 
 Any machines acting as performance test servers will require ``mysqldb`` to be installed.
 
diff --git a/docs/runsphinx.py.in b/docs/runsphinx.py.in
index 3ee4654fae408bf969f62a0b301a2434003498e8..f1dd3e7075ccfa825ff719ddb8cdc7f3c7c38cf4 100644
--- a/docs/runsphinx.py.in
+++ b/docs/runsphinx.py.in
@@ -47,9 +47,6 @@ def main(sysarg):
         raise RuntimeError("Unexpected command line arguments: %s. "
                            "Use -h for help" % ' '.join(args))
 
-    # Update sys path if we need to
-    update_path()
-
     # Find test files
     testpaths = find_test_files(CONF_DIR, opts.testinclude)
 
@@ -68,8 +65,8 @@ def main(sysarg):
     doctree_dir = pathjoin(SPHINX_BUILD_DIR, "doctrees")
     argv = []
     if LooseVersion(get_sphinx_version()) < LooseVersion("1.7.0"):
-        # prior to v1.7.0 paths positional argument required       
-        argv = [sys.executable] 
+        # prior to v1.7.0 paths positional argument required
+        argv = [sys.executable]
     argv += ["-N",
             "-b", BUILDER,
             "-d", doctree_dir]
@@ -107,7 +104,6 @@ def main(sysarg):
         RegexLexer.get_tokens_unprocessed = pygments_highlighter.get_tokens_unprocessed
     sys.exit(return_value)
 
-#-----------------------------------------------------------------------------------------------
 
 def parseargs(arglist):
     """
@@ -123,45 +119,6 @@ def parseargs(arglist):
                       "filename when considering whether to run a test.")
     return parser.parse_args(arglist[1:]) # hack off filename
 
-#-----------------------------------------------------------------------------------------------
-
-def update_path():
-    """
-    If not inside MantidPlot (current check is whether we can import _qti)
-    then insert given path as first directory in sys.path
-    """
-    try:
-        import _qti
-        gui = True
-    except ImportError:
-        gui = False
-
-
-    # If it's MantidPlot then we already know what our paths should be so ignore it
-    if gui:
-        return
-
-    # the python wrapper sets this environment variable
-    mantidpath = os.environ.get('MANTIDPATH', None)
-
-    # otherwise try to expand based off of information in cmake
-    if mantidpath is None or len(mantidpath) == 0:
-        mantidpath = BUILD_DIR
-        if os.path.split(mantidpath)[-1] != 'bin' and \
-           os.path.isdir(os.path.join(mantidpath, 'bin')):
-            mantidpath = os.path.join(mantidpath, 'bin')
-
-    # check for directory
-    if not os.path.isdir(os.path.join(mantidpath, "mantid")):
-        raise ValueError("Unable to find mantid package in '%s'" % mantidpath)
-    # is package valid
-    if not os.path.isfile(os.path.join(mantidpath, "mantid", "__init__.py")):
-        raise ValueError("Invalid mantid package. No __init__ found in '%s' %s")
-
-    # Update sys.path
-    sys.path.insert(0, mantidpath)
-
-#-----------------------------------------------------------------------------------------------
 
 def find_test_files(src_dir, name_re):
     """
@@ -206,7 +163,7 @@ def find_matching_tests(filenames, name_re):
 
     return testfiles
 
-#-----------------------------------------------------------------------------------------------
+
 def pathjoin(left, *args):
     """
     Similar to os.path.join but just uses "/" as it's populated with CMake-style paths that are
@@ -214,10 +171,6 @@ def pathjoin(left, *args):
     """
     return left + "/" + "/".join(args)
 
-#-----------------------------------------------------------------------------------------------
-
-
-##################################################################################
 
 if __name__ == "__main__":
     main(sys.argv)
diff --git a/docs/source/algorithms/ConjoinXRuns-v1.rst b/docs/source/algorithms/ConjoinXRuns-v1.rst
index 11ffcd0a020b75fa954e7aeeee319a66ae2671be..e79469a3fd9ee4d0b1056bc291769dde0ccc3f6a 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 overridden by providing input to the relevant optional properties of the algorithm. This algorithm joins Dx values, if present.
+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>` but all parameter names must have the prefix `conjoin_`. Furthermore, that behaviour can be overridden 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/DeadTimeCorrection-v1.rst b/docs/source/algorithms/DeadTimeCorrection-v1.rst
new file mode 100644
index 0000000000000000000000000000000000000000..94f7445af71b63d8ea7e2569bbd7a5f260a8c674
--- /dev/null
+++ b/docs/source/algorithms/DeadTimeCorrection-v1.rst
@@ -0,0 +1,55 @@
+
+.. algorithm::
+
+.. summary::
+
+.. relatedalgorithms::
+
+.. properties::
+
+Description
+-----------
+
+This algorithm corrects for detector dead time. The multiplicative correction is defined as:
+
+.. math:: C = \frac{1}{1-\tau * R}
+
+where :math:`\tau` is the dead time coefficient in :math:`[sec]` and :math:`R` is the total count rate in :math:`[sec^{-1}]`.
+
+The correction can be calculated for groups of pixels specified. Check the **GroupingPattern** in :ref:`GroupDetectors <algm-GroupDetectors>`.
+
+If no grouping is specified, correction will be calculated on pixel by pixel bases.
+
+First the counts are integrated over all the time-of-flight channels, if there are more than one.
+
+Then for each group the counts of the pixels are summed.
+
+This results in a total count rate for the group, which is then put in the formula to calculate the correction.
+
+Then counts in each pixel are scaled up by the correction corresponding to the group that the pixels are in.
+
+Note that the input workspace must be normalised by acquisition time before passing to the algorithm.
+
+If saturation is achieved, i.e. :math:`R \geq \frac{1}{\tau}`, the correction is set to infinity.
+
+Usage
+-----
+
+**Example - DeadTimeCorrection**
+
+.. testcode:: DeadTimeCorrectionExample
+
+  CreateSampleWorkspace(OutputWorkspace='in', Function="Powder Diffraction")
+  DeadTimeCorrection(InputWorkspace='in', OutputWorkspace='out', Tau=0.0001, GroupingPattern='0-99,100-199')
+  Divide(LHSWorkspace='out', RHSWorkspace='in', OutputWorkspace='corr')
+  print("Correction is {0:.3f}".format(mtd['corr'].readY(0)[0]))
+
+Output:
+
+.. testoutput:: DeadTimeCorrectionExample
+
+  Correction is 1.376
+
+.. categories::
+
+.. sourcelink::
diff --git a/docs/source/algorithms/FitPeaks-v1.rst b/docs/source/algorithms/FitPeaks-v1.rst
index 13ae63122e895b635c29fc997521ef34c5472691..544c7f8ba92e2df26fb61cb4ff017690b4a430ae 100644
--- a/docs/source/algorithms/FitPeaks-v1.rst
+++ b/docs/source/algorithms/FitPeaks-v1.rst
@@ -262,8 +262,11 @@ Algorithm ``FitPeaks`` is designed for various purposes including but not limite
 On the other hand, due to the complexity in peak fitting, users prefer to check the fitting results.
 Therefore, ``FitPeaks`` supports various fexible and informative outputs.
 
+OutputWorkspaces
+################
+
 OutputWorkspace
-###############
+===============
 
 It is a :py:obj:`MatrixWorkspace <mantid.api.MatrixWorkspace>` containing the peak positions expected and fitted.
 
@@ -278,13 +281,34 @@ It is a :py:obj:`MatrixWorkspace <mantid.api.MatrixWorkspace>` containing the pe
   - -4: TODO : find out the answer
   - -5: TODO : find out the answer
 
+OutputPeakParametersWorkspace
+=============================
+
+It is an output :py:obj:`MatrixWorkspace <mantid.api.ITableWorkspace>` containing function parameters' fitted values 
+for all peaks that are specified to fit. 
+The order of the peaks will be exactly the sequence of peaks as the order of the given positions of peaks.
 
+If user specifies a subset of spectra to fit, this TableWorksapce will only contain function 
+parameters' value that corresponds to the spectra that are fitted.
 
-It is a TableWorkspace containing peak parameters.
-According to user's specication, it will contain one parameter, i.e., peak position, or all parameters.
+OutputParameterFitErrorsWorkspace
+=================================
 
+It is an optional output :py:obj:`MatrixWorkspace <mantid.api.ITableWorkspace>` containing function parameters' fitting error, 
+i.e., uncertainties, for all peaks that are specified to fit.
 The order of the peaks will be exactly the sequence of peaks as the order of the given positions of peaks.
 
+If user specifies a subset of spectra to fit, this TableWorksapce will only contain function 
+parameters' uncertainties that corresponds to the spectra that are fitted.
+
+It has one less column than OutputPeakParametersWorkspace, which is chi2.
+
+FittedPeaksWorkspace
+====================
+
+It is an optional output :py:obj:`MatrixWorkspace <mantid.api.MatrixWorkspace>` containing the peaks 
+calculated from fitting result.
+
 
 FittingCostWorkspace
 ####################
diff --git a/docs/source/algorithms/LoadSpiceXML2DDet-v1.rst b/docs/source/algorithms/LoadSpiceXML2DDet-v1.rst
index 64f4f0d70bce32af7fcd4dbd86fcc0723c21f6d4..80fa1c53c3968456938c3f551ee95e2cdb716c06 100644
--- a/docs/source/algorithms/LoadSpiceXML2DDet-v1.rst
+++ b/docs/source/algorithms/LoadSpiceXML2DDet-v1.rst
@@ -9,9 +9,9 @@
 Description
 -----------
 
-This algorithm is to import SPICE-generated XML file that
+This algorithm is to import SPICE-generated XML file and binary file that
 records data of one measurement by a (two-dimensional) Anger camera
-and create a MatrixWorkspace to contain the detectors' counts, monitor counts 
+and create a MatrixWorkspace to contain the detectors' counts, monitor counts
 and other sample log data.
 
 
@@ -22,8 +22,8 @@ The SPICE XML data file contains four sections under parent node *SPICErack*.
 Each section contains child nodes for detailed information.
 
  - Header: instrument name, reactor power, experiment title and number, scan number and etc.
- - Motor_Position: positions of motor *m1*, *marc*, *2theta*, *chi*, *phi*, *omega* and etc. 
- - Parameter_Positions: reading of sample environment devices, such as temperature at sample.  
+ - Motor_Position: positions of motor *m1*, *marc*, *2theta*, *chi*, *phi*, *omega* and etc.
+ - Parameter_Positions: reading of sample environment devices, such as temperature at sample.
  - Counters: counting time, monitor counts, and *N x N* detectors' counts,
 
 
@@ -41,49 +41,68 @@ Counts of an :math:`n\times m` 2D detectors  are recorded in XML file as below::
 And the (1,1) position is the bottom left corner of the Anger camera as seen from the sample position.
 
 
+Format of SPICE binary data file
+################################
+
+The SPICE binary data file contains 2 + N unsigned integers, where N is the number of detector pixels.
+
+Here is the specification of the SPICE 2D detector binary file.
+
+  int0: number of rows in 2D detector
+  int1: number of columns in 2D detector
+  int2: counts of pixel on the lower left corner facing to detector from sample
+  int3: counts of pixel just above the lower left corner
+  .
+  .
+  .
+  int(N+2): counts of pixel on the upper right corner
+
+Note: int0 x int1 must be equal to N.
+
+
 HB3A instrument facts
 #####################
 
 HB3A has 1 detector with :math:`256 \times 256` pixels.
 
  - Pixel: width = :math:`2 \times 9.921875e-05` m, height = :math:`2 \times 9.921875e-05` m, depth = 0.0001 m.
- - Detector: 
+ - Detector:
 
 
 Output Worskpaces
 #################
 
-The output from this algorithm is a MatrixWorskpaces. 
+The output from this algorithm is a MatrixWorskpaces.
 
 MatrixWorskpace with instrument loaded
 ++++++++++++++++++++++++++++++++++++++
 
 For a 2D detector with :math:`n\times m` pixels, the output MatrixWorkspace
 will have :math:`n \times m` spectrum.
-Each spectrum has 1 data point corresponding to 1 detector's count.  
+Each spectrum has 1 data point corresponding to 1 detector's count.
 
 All experiment information, sample environment devices' readings and monitor counts,
 which are recorded in XML files,
-are converted to the properties in output MatrixWorkspace's sample log. 
+are converted to the properties in output MatrixWorkspace's sample log.
 
 MatrixWorkspace without instrument loaded
 +++++++++++++++++++++++++++++++++++++++++
 
 For a 2D detector with :math:`n\times m` pixels, the output MatrixWorkspace
-will have :math:`n` spectrum, each of which has a vector of length equal to :math:`m`. 
-It can be mapped to the raw data as :math:`WS.readY(i)[j] = X(i+1,j+1)`. 
+will have :math:`n` spectrum, each of which has a vector of length equal to :math:`m`.
+It can be mapped to the raw data as :math:`WS.readY(i)[j] = X(i+1,j+1)`.
 
 All experiment information, sample environment devices' readings and monitor counts,
 which are recorded in XML files,
-are converted to the properties in output MatrixWorkspace's sample log. 
+are converted to the properties in output MatrixWorkspace's sample log.
 
 
 Workflow
 ########
 
-Algorithm *LoadSpiceXML2DDet* is one of a series of algorithms that are implemented to 
-reduced HFIR HB3A data collected from Anger camera. 
-It will be called next to *LoadSpiceAscii* to load the detector's reading. 
+Algorithm *LoadSpiceXML2DDet* is one of a series of algorithms that are implemented to
+reduced HFIR HB3A data collected from Anger camera.
+It will be called next to *LoadSpiceAscii* to load the detector's reading.
 
 Usage
 -----
@@ -93,9 +112,9 @@ Usage
 .. testcode:: ExLoadHB3AXMLData
 
   # Load data by LoadSpiceXML2DDet()
-  LoadSpiceXML2DDet(Filename='HB3A_exp355_scan0001_0522.xml', 
+  LoadSpiceXML2DDet(Filename='HB3A_exp355_scan0001_0522.xml',
       OutputWorkspace='s0001_0522', DetectorGeometry='256,256',
-      LoadInstrument=False)    
+      LoadInstrument=False)
 
   # Access output workspace and print out some result
   ws = mtd["s0001_0522"]
diff --git a/docs/source/algorithms/MuonGroupingAsymmetry-v1.rst b/docs/source/algorithms/MuonGroupingAsymmetry-v1.rst
new file mode 100644
index 0000000000000000000000000000000000000000..65fa97a7e8abfc026b3de9f57520332b55a959dd
--- /dev/null
+++ b/docs/source/algorithms/MuonGroupingAsymmetry-v1.rst
@@ -0,0 +1,129 @@
+.. algorithm::
+
+.. summary::
+
+.. relatedalgorithms::
+
+.. properties::
+
+Description
+-----------
+
+When interacting with the :ref:`Muon_Analysis-ref` interface, operations such as detector grouping, group and pair asymmetry are performed on data. This algorithm performs a "grouping asymmetry" operation, in other words it provides a numerical estimation (without fitting) of the asymmetry. More details can be found at :ref:`algm-EstimateMuonAsymmetryFromCounts`.
+
+This algorithm is part of a set of four; with :ref:`algm-MuonPreProcess` being run first; and the output being fed into this one. This allows the replication of the workflow used by the muon analysis interface to produce group data. 
+
+Analysis
+########
+
+A workspace has one or more *spectra* contained within it; each spectra has a unique detector ID. Assuming the y-values represent counts; a *detector grouping* operation causes the counts to be summed across the given set of detector IDs which are supplied to the **Grouping** argument (for example `1,2,3,4,5` and `1-5`).
+
+The **InputWorkspace** must be a :ref:`WorkspaceGroup <WorkspaceGroup>`, where each workspace within the group represents a single period. Thus, single period data is just a *WorkspaceGroup* with a single workspace within it.
+
+The group must be given a name via **GroupName** which can consist of letters, numbers and underscores. 
+
+#. Valid names : "fwd", "fwd2", "fwd_2", "1234"
+#. Invalid names : "", "fwd!", "fwd "
+
+The group name does not affect the data; however the name is used in the muon interface when automatically generating workspace names from group data.
+
+After the grouping is performed, the analysis described in :ref:`algm-EstimateMuonAsymmetryFromCounts` will be run; effectively estimating the muon decay curve and subtracting it from the grouped data and then estimating the asymmetry on the grouped data. The range over which the estimate is performed is controlled by the **AsymmetryTimeMin** and **AsymmetryTimeMax** inputs.
+
+**Note** : The workspaces supplied to the algorithm must have a number of good frames set in their sample logs. The sample log is called "goodfrm" and can be set using;
+
+.. code:: 
+
+    AddSampleLog(workspace=workspace, LogName="goodfram", LogText ="10")
+
+Multi period data 
+#################
+
+Both single and multi period data are supported by the algorithm.
+
+The **SummedPeriods** and **SubtractedPeriods** inputs are used to control the way that periods are combined. so for example;
+
+#. SummedPeriods = 1,2
+#. SubtractedPeriods = 3,4 
+
+would combine periods in the combination :math:`(1+2)-(3+4)`.
+
+Usage
+-----
+
+**Example - Using MuonPreProcess followed by MuonGroupingAsymmetry on Single Period Data**
+
+.. testcode:: SinglePeriod
+
+    # Create a workspaces with one spectrum
+    dataX = [0, 1, 2, 3, 4, 5]
+    dataY = [10, 20, 30, 20, 10]
+    input_workspace = CreateWorkspace(dataX, dataY, NSpec=1)
+    input_workspace.getSpectrum(0).setDetectorID(1)
+    # Workspace must have the number of good frames set
+    AddSampleLog(Workspace=input_workspace, LogName='goodfrm', LogText="10")
+
+    pre_processed_workspace = MuonPreProcess(InputWorkspace=input_workspace)
+
+    output_workspace = MuonGroupingAsymmetry(InputWorkspace=pre_processed_workspace,
+                                                       GroupName="fwd",
+                                                       Grouping=[1])
+
+    print("X values are : {}".format([round(float(i), 3) for i in output_workspace.readX(0)]))
+    print("Y values are : {}".format([round(float(i), 3) for i in output_workspace.readY(0)]))
+
+
+Output:
+
+.. testoutput:: SinglePeriod
+
+    X values are : [0.0, 1.0, 2.0, 3.0, 4.0, 5.0]
+    Y values are : [-0.789, -0.334, 0.576, 0.656, 0.306]
+
+**Example - Using MuonPreProcess followed by MuonGroupingAsymmetry on Multi Period Data**
+
+.. testcode:: MultiPeriod
+
+    # Create two workspaces each with two spectra
+    dataX = [0, 1, 2, 3, 4, 5] * 2
+    dataY_period1 = [10, 20, 30, 20, 10] + \
+                    [20, 30, 40, 30, 20]
+    dataY_period2 = [30, 40, 50, 40, 30] + \
+                    [40, 50, 60, 50, 40]
+    ws1 = CreateWorkspace(dataX, dataY_period1, NSpec=2)
+    ws2 = CreateWorkspace(dataX, dataY_period2, NSpec=2)
+    AddSampleLog(Workspace=ws1, LogName='goodfrm', LogText="1")
+    AddSampleLog(Workspace=ws2, LogName='goodfrm', LogText="1")
+    for i in range(2):
+        # set detector IDs to be 1,2,3,4
+        # these do not have to be the same as the spectrum numbers
+        # (the spectrum number are 0,1,2,3 in this case)
+        ws1.getSpectrum(i).setDetectorID(i + 1)
+        ws2.getSpectrum(i).setDetectorID(i + 1)
+
+    # Create multi period data
+    multi_period_data = GroupWorkspaces(ws1)
+    multi_period_data.addWorkspace(ws2)
+
+    # This time we won't run MuonPreProcess, as we don't want to apply any pre-processing
+    # and we already have a WorkspaceGroup
+
+    output_workspace = MuonGroupingAsymmetry(InputWorkspace=multi_period_data,
+                                                       GroupName="fwd",
+                                                       Grouping=[1, 2],
+                                                       SummedPeriods=[1, 2])
+
+    # We have asked for periods 1+2, with each period summing detectors 1,2
+    print("X values are : {}".format([round(float(i), 3) for i in output_workspace.readX(0)]))
+    print("Y values are : {}".format([round(float(i), 3) for i in output_workspace.readY(0)]))
+    
+
+Output:
+
+.. testoutput:: MultiPeriod
+
+    X values are : [0.0, 1.0, 2.0, 3.0, 4.0, 5.0]
+    Y values are : [-0.712, -0.364, 0.289, 0.581, 0.78]
+
+.. categories::
+
+.. sourcelink::
\ No newline at end of file
diff --git a/docs/source/algorithms/MuonPairingAsymmetry-v1.rst b/docs/source/algorithms/MuonPairingAsymmetry-v1.rst
new file mode 100644
index 0000000000000000000000000000000000000000..c0b832b34a0326744f2dfe43ac1942c7a19badcb
--- /dev/null
+++ b/docs/source/algorithms/MuonPairingAsymmetry-v1.rst
@@ -0,0 +1,185 @@
+.. algorithm::
+
+.. summary::
+
+.. relatedalgorithms::
+
+.. properties::
+
+Description
+-----------
+
+When interacting with the :ref:`Muon_Analysis-ref` interface, operations such as detector grouping, group and pair asymmetry are performed on data. This algorithm performs a "pair asymmetry" operation, in other words it takes two groups (from :ref:`algm-MuonGroupingCounts` for example) and calculates the asymmetry between them using the common formula from :ref:`algm-AsymmetryCalc`.
+
+This algorithm is part of a set of four; with :ref:`algm-MuonPreProcess` being run first; and the output being fed into this one. Alternatively, the groups may be calculated by using :ref:`algm-MuonPreProcess` followed by :ref:`algm-MuonGroupingCounts` and fed into this algorithm by switching off **SpecifyGroupsManually**. This allows the replication of the workflow used by the muon analysis interface to produce group data. 
+
+Analysis
+########
+
+A *Pair* in this context refers to a pair of groups. A workspace has one or more *spectra* contained within it; each spectra has a unique detector ID. Assuming the y-values represent counts; a *detector group* is a sum over a given set of detectors.
+
+With a pair, one may define an asymmetry operation as in :ref:`algm-AsymmetryCalc`;
+
+.. math:: A = \frac{F-\alpha B}{F+\alpha B},
+
+where :math:`F` and :math:`B` are the forward and backwards groups and alpha is the balance parameter.
+
+The pair must be given a name via **PairName** which can consist of letters, numbers and underscores. 
+
+#. Valid names : "pair", "pair2", "pair_2", "1234"
+#. Invalid names : "", "pair!", "pair "
+
+The pair name does not affect the data; however the name is used in the muon interface when automatically generating workspace names from group data.
+
+Additionally, a value for **alpha** must be supplied, and which must be non-negative.
+
+There are two options for supplying the group data :
+
+#. If **SpecifyGroupsManually** is checked, then the detector ID's that define the two groups must be given (they must be unique); and an **InputWorkspace** supplied. This workspace must be a :ref:`WorkspaceGroup <WorkspaceGroup>` to match the output of :ref:`algm-MuonPreProcess`, where the group contains one *MatrixWorkspace* for each period.
+
+#. If **SpecifyGroupsManually** is not checked, then two workspaces must be supplied which represent the two groups via **InputWorkspace1** and **InputWorkspace2**. These may be *MatrixWorkspace*s (in the case of single period data); or *WorkspaceGroup*s (in the case of multi period data, the two groups must contain the same number of workspaces). Any *MatrixWorkspace* must only contain one spectra (the group counts).
+
+
+Multi period data 
+#################
+
+Both single and multi period data are supported by the algorithm.
+
+The **SummedPeriods** and **SubtractedPeriods** inputs are used to control the way that periods are combined. so for example;
+
+#. SummedPeriods = 1,2
+#. SubtractedPeriods = 3,4 
+
+would combine periods in the combination :math:`(1+2)-(3+4)`.
+
+Usage
+-----
+
+**Example - Using MuonPreProcess and Specifying Groups Manually for Single Period Data**
+
+.. testcode:: SpecifyGroupsManuallySinglePeriod
+
+    # Create a workspaces with four spectra
+    dataX = [0, 1, 2, 3, 4, 5] * 4
+    dataY = [10, 20, 30, 20, 10] + \
+            [20, 30, 40, 30, 20] + \
+            [30, 40, 50, 40, 30] + \
+            [40, 50, 60, 50, 40]
+    input_workspace = CreateWorkspace(dataX, dataY, NSpec=4)
+    for i in range(4):
+        # set detector IDs to be 1,2,3,4
+        # these do not have to be the same as the spectrum numbers
+        # (the spectrum number are 0,1,2,3 in this case)
+        input_workspace.getSpectrum(i).setDetectorID(i + 1)
+
+    pre_processed_workspace = MuonPreProcess(InputWorkspace=input_workspace)
+
+    output_workspace = MuonPairingAsymmetry(InputWorkspace=pre_processed_workspace,
+                                                      PairName="myPair",
+                                                      Alpha=1.0,
+                                                      SpecifyGroupsManually=True,
+                                                      Group1=[1, 2],
+                                                      Group2=[3, 4])
+
+    print("X values are : {}".format([round(float(i), 3) for i in output_workspace.readX(0)]))
+    print("Y values are : {}".format([round(float(i), 3) for i in output_workspace.readY(0)]))
+
+
+Output:
+
+.. testoutput:: SpecifyGroupsManuallySinglePeriod
+
+    X values are : [0.5, 1.5, 2.5, 3.5, 4.5]
+    Y values are : [-0.4, -0.286, -0.222, -0.286, -0.4]
+
+**Example - Using MuonPreProcess and Specifying Groups Manually for Multi Period Data**
+
+.. testcode:: SpecifyGroupsManuallyMultiPeriod
+
+    # Create a workspaces with four spectra
+    dataX = [0, 1, 2, 3, 4, 5] * 4
+    dataY = [10, 20, 30, 20, 10] + \
+            [20, 30, 40, 30, 20] + \
+            [30, 40, 50, 40, 30] + \
+            [40, 50, 60, 50, 40]
+
+    input_workspace = CreateWorkspace(dataX, dataY, NSpec=4)
+    input_workspace_1 = CreateWorkspace(dataX, dataY, NSpec=4)
+    for i in range(4):
+        # set detector IDs to be 1,2,3,4
+        # these do not have to be the same as the spectrum numbers
+        # (the spectrum number are 0,1,2,3 in this case)
+        input_workspace.getSpectrum(i).setDetectorID(i + 1)
+        input_workspace_1.getSpectrum(i).setDetectorID(i + 1)
+
+    # Create multi period data
+    multi_period_data = GroupWorkspaces(input_workspace)
+    multi_period_data.addWorkspace(input_workspace_1)
+
+    pre_processed_workspace = MuonPreProcess(InputWorkspace=multi_period_data)
+
+    output_workspace = MuonPairingAsymmetry(InputWorkspace=pre_processed_workspace,
+                                                      PairName="myPair",
+                                                      Alpha=1.0,
+                                                      SpecifyGroupsManually=True,
+                                                      Group1=[1, 2],
+                                                      Group2=[3, 4],
+                                                      SummedPeriods=[1, 2])
+
+    print("X values are : {}".format([round(float(i), 3) for i in output_workspace.readX(0)]))
+    print("Y values are : {}".format([round(float(i), 3) for i in output_workspace.readY(0)]))
+
+
+Output:
+
+.. testoutput:: SpecifyGroupsManuallyMultiPeriod
+
+    X values are : [0.5, 1.5, 2.5, 3.5, 4.5]
+    Y values are : [-0.4, -0.286, -0.222, -0.286, -0.4]
+
+**Example - Using MuonPreProcess, MuonGroupingCounts for Single Period Data**
+
+.. testcode:: SinglePeriod
+
+    # Create a workspaces with four spectra
+    dataX = [0, 1, 2, 3, 4, 5] * 4
+    dataY = [10, 20, 30, 20, 10] + \
+            [20, 30, 40, 30, 20] + \
+            [30, 40, 50, 40, 30] + \
+            [40, 50, 60, 50, 40]
+    input_workspace = CreateWorkspace(dataX, dataY, NSpec=4)
+    for i in range(4):
+        # set detector IDs to be 1,2,3,4
+        # these do not have to be the same as the spectrum numbers
+        # (the spectrum number are 0,1,2,3 in this case)
+        input_workspace.getSpectrum(i).setDetectorID(i + 1)
+
+    pre_processed_workspace = MuonPreProcess(InputWorkspace=input_workspace)
+    group_workspace_1 = MuonGroupingCounts(InputWorkspace=pre_processed_workspace,
+                                                     GroupName="fwd",
+                                                     Grouping=[1, 2])
+    group_workspace_2 = MuonGroupingCounts(InputWorkspace=pre_processed_workspace,
+                                                     GroupName="bwd",
+                                                     Grouping=[3, 4])
+
+    output_workspace = MuonPairingAsymmetry(InputWorkspace=pre_processed_workspace,
+                                                      PairName="myPair",
+                                                      Alpha=1.0,
+                                                      SpecifyGroupsManually=False,
+                                                      InputWorkspace1=group_workspace_1,
+                                                      InputWorkspace2=group_workspace_2)
+
+    print("X values are : {}".format([round(float(i), 3) for i in output_workspace.readX(0)]))
+    print("Y values are : {}".format([round(float(i), 3) for i in output_workspace.readY(0)]))
+
+
+Output:
+
+.. testoutput:: SinglePeriod
+
+    X values are : [0.5, 1.5, 2.5, 3.5, 4.5]
+    Y values are : [-0.4, -0.286, -0.222, -0.286, -0.4]
+
+.. categories::
+
+.. sourcelink::
\ No newline at end of file
diff --git a/docs/source/algorithms/ParallaxCorrection-v1.rst b/docs/source/algorithms/ParallaxCorrection-v1.rst
new file mode 100644
index 0000000000000000000000000000000000000000..135b87915bf59fcd2e19feccc812aefe3e3712ad
--- /dev/null
+++ b/docs/source/algorithms/ParallaxCorrection-v1.rst
@@ -0,0 +1,80 @@
+
+.. algorithm::
+
+.. summary::
+
+.. relatedalgorithms::
+
+.. properties::
+
+Description
+-----------
+
+This algorithm performs a geometrical correction for the so-called parallax effect in tube based SANS instruments.
+
+The correction formula must be specified in the :ref:`IPF <InstrumentParameterFile>` as follows:
+
+- A string parameter named **direction** must hold **x** or **y** which is the direction of the tubes in the detector.
+
+- A string parameter named **parallax** must hold the `muparser <http://beltoforion.de/article.php?a=muparser>`_ expression, where **t** is reserved for the parallax angle:
+
+:math:`t = \arctan(\frac{x}{z})` if direction is **y**, and :math:`t = \arctan(\frac{y}{z})` if direction is **x**.
+
+:math:`x, y, z` are the coordinates of the detector pixel in the system where sample is at :math:`0,0,0` and :math:`z` is the beam axis.
+:math:`t \in (0,\frac{\pi}{2})` in radians.
+
+The correction will be calculated for each pixel and the input data will be divided by the correction and stored in the output.
+
+The instrument parameters must be defined for detector components and without loss of generality, different components can have different formulae.
+
+At least one component name must be given as input.
+
+Example of adding parameters in the IPF
+---------------------------------------
+
+.. code-block:: xml
+
+  <component-link name="some_bank">
+
+    <parameter name="parallax" type="string">
+      <!-- Normally, the function would be increasing with t -->
+      <value val="1 + 0.01 * t"/>
+    </parameter>
+
+    <parameter name="direction" type="string">
+      <value val="x"/>
+    </parameter>
+
+  </component-link>
+
+Usage
+-----
+
+**Example - ParallaxCorrection**
+
+.. testcode:: ParallaxCorrectionExample
+
+    CreateSampleWorkspace(NumBanks=1, XMin=1, XMax=2, BinWidth=1, BankPixelWidth=100, Function="One Peak", XUnit="Wavelength", OutputWorkspace="in")
+    SetInstrumentParameter(Workspace="in", ParameterName="direction", ComponentName="bank1", ParameterType="String", Value="y")
+    SetInstrumentParameter(Workspace="in", ParameterName="parallax", ComponentName="bank1", ParameterType="String", Value="1+0.1*t")
+    ParallaxCorrection(InputWorkspace="in", ComponentNames="bank1", OutputWorkspace="out")
+    Divide(LHSWorkspace="in", RHSWorkspace="out", OutputWorkspace="corr")
+    print("The correction is {0:.4f} for the spectrum {1}".format(mtd["corr"].readY(1000)[0], 1000))
+
+Output:
+
+.. testoutput:: ParallaxCorrectionExample
+
+  The correction is 1.0016 for the spectrum 1000
+
+Example of correction
+---------------------
+
+Below is an example of the corrections for the instrument D22. The tubes are vertical, the magnitude depends only on the horizontal coordinate.
+The correction increases with increasing angle from the beam, reaching about 10% for the outmost tube for detector distance of 1.5 meters.
+
+.. figure:: /images/parallax.png
+
+.. categories::
+
+.. sourcelink::
diff --git a/docs/source/algorithms/SetupILLD33Reduction-v1.rst b/docs/source/algorithms/SetupILLD33Reduction-v1.rst
deleted file mode 100644
index e5dbb8669318c769e95786bb4bed2d6d45095e6c..0000000000000000000000000000000000000000
--- a/docs/source/algorithms/SetupILLD33Reduction-v1.rst
+++ /dev/null
@@ -1,93 +0,0 @@
-.. algorithm::
-
-.. summary::
-
-.. relatedalgorithms::
-
-.. properties::
-
-Description
------------
-
-Create a PropertyManager object setting the reduction options for ILL
-D33 SANS TOF instrument. The property manager object is then added to
-the PropertyManagerDataService.
-This was based on the :ref:`SetupEQSANSReduction <algm-SetupEQSANSReduction>`.
-
-See :ref:`SANSReduction <algm-SANSReduction>` for details.
-
-Usage
------
-
-**Example - Reduces D33 data from a set of NeXus files:**
-
-.. code-block:: python
-
-	# List of detector IDs masked
-	maskedDetectors = [14709,14710,14711,14712,14713,14714,14715,14716,14717,14718,
-		14719,14720,14721,14722,14723,14724,14725,14726,14727,14728,14729,14730,14731,
-		14732,14733,14734,14735,14965,14966,14967,14968,14969,14970,14971,14972,14973,
-		14974,14975,14976,14977,14978,14979,14980,14981,14982,14983,14984,14985,14986,
-		14987,14988,14989,14990,14991,15221,15222,15223,15224,15225,15226,15227,15228,
-		15229,15230,15231,15232,15233,15234,15235,15236,15237,15238,15239,15240,15241,
-		15242,15243,15244,15245,15246,15247,15477,15478,15479,15480,15481,15482,15483,
-		15484,15485,15486,15487,15488,15489,15490,15491,15492,15493,15494,15495,15496,
-		15497,15498,15499,15500,15501,15502,15503,15733,15734,15735,15736,15737,15738,
-		15739,15740,15741,15742,15743,15744,15745,15746,15747,15748,15749,15750,15751,
-		15752,15753,15754,15755,15756,15757,15758,15759,15989,15990,15991,15992,15993,
-		15994,15995,15996,15997,15998,15999,16000,16001,16002,16003,16004,16005,16006,
-		16007,16008,16009,16010,16011,16012,16013,16014,16015,16245,16246,16247,16248,
-		16249,16250,16251,16252,16253,16254,16255,16256,16257,16258,16259,16260,16261,
-		16262,16263,16264,16265,16266,16267,16268,16269,16270,16271,16501,16502,16503,
-		16504,16505,16506,16507,16508,16509,16510,16511,16512,16513,16514,16515,16516,
-		16517,16518,16519,16520,16521,16522,16523,16524,16525,16526,16527,16757,16758,
-		16759,16760,16761,16762,16763,16764,16765,16766,16767,16768,16769,16770,16771,
-		16772,16773,16774,16775,16776,16777,16778,16779,16780,16781,16782,16783,17013,
-		17014,17015,17016,17017,17018,17019,17020,17021,17022,17023,17024,17025,17026,
-		17027,17028,17029,17030,17031,17032,17033,17034,17035,17036,17037,17038,17039,
-		17269,17270,17271,17272,17273,17274,17275,17276,17277,17278,17279,17280,17281,
-		17282,17283,17284,17285,17286,17287,17288,17289,17290,17291,17292,17293,17294,
-		17295,17525,17526,17527,17528,17529,17530,17531,17532,17533,17534,17535,17536,
-		17537,17538,17539,17540,17541,17542,17543,17544,17545,17546,17547,17548,17549,
-		17550,17551]
-
-	# Set all the necessary parameters to reduce the data
-	SetupILLD33Reduction(
-		MaskedDetectorList=maskedDetectors,
-		BeamCenterMethod="DirectBeam",
-		BeamCenterFile="ILLD33_direct_beam_transm_001427.nxs",
-		Normalisation="Timer",
-		DarkCurrentFile= "ILLD33_b4c_001420.nxs",
-		TransmissionMethod="DirectBeam",
-		TransmissionSampleDataFile= "ILLD33_sample_transm_001431.nxs",
-		TransmissionEmptyDataFile= "ILLD33_direct_beam_transm_001427.nxs",
-		BckTransmissionEmptyDataFile= "ILLD33_direct_beam_transm_001427.nxs",
-		TransmissionBeamRadius = 3,
-		TransmissionUseSampleDC=False,
-		BackgroundFiles= "ILLD33_empty_cell_001422.nxs",
-		BckTransmissionSampleDataFile="ILLD33_empty_cell_transm_001428.nxs",
-		DoAzimuthalAverage=False,	
-		Do2DReduction=False,
-		ComputeResolution=True,
-		ReductionProperties="d33")
-
-	# Reduce the sample data
-	output=SANSReduction(Filename="ILLD33_sample_001425.nxs", ReductionProperties="d33",OutputWorkspace="d33out")
-
-	# Rebin
-	Rebin(InputWorkspace='d33out',OutputWorkspace='d33out_rebin',Params='4,0.1,15')
-	
-	# Do a radial integration
-	SANSAzimuthalAverage1D(InputWorkspace='d33out_rebin',Binning='0.001,0.0002,0.03',OutputWorkspace='IQ_curve')
-
-	# Get the OutputWorkspace as a python workspace
-	iq = mtd['IQ_curve']
-	print("This workspace has {} dimensions and has {} histograms.".format(iq.getNumDims(), iq.getNumberHistograms()))
-
-Output:
-
-	This workspace has 2 dimensions and has 1 histograms.
-
-.. categories::
-
-.. sourcelink::
diff --git a/docs/source/algorithms/Stitch1D-v3.rst b/docs/source/algorithms/Stitch1D-v3.rst
index f7a2ef375ba3d0ca3a5c8b23c3dd411d6b0beefc..e0b3fd6db0e5299ef6650e0ded55f205746f75ed 100644
--- a/docs/source/algorithms/Stitch1D-v3.rst
+++ b/docs/source/algorithms/Stitch1D-v3.rst
@@ -75,7 +75,8 @@ The algorithm workflow for point data is as follows:
 #. Alternatively, if :literal:`UseManualScaleFactor` was set to true, the scale factor is applied
    to the right-hand-side workspace (left-hand-side workspace if :literal:`ScaleRHSWorkspace` was
    set to false).
-#. The output workspace will be created by joining and sorted to guarantee ascending x values.
+#. The output workspace will be created by joining using :ref:`algm-ConjoinXRuns` and sorting using
+   :ref:`algm-SortXAxis`.
    Dx values will be present in the output workspace.
 
 Error propagation
diff --git a/docs/source/images/parallax.png b/docs/source/images/parallax.png
new file mode 100644
index 0000000000000000000000000000000000000000..11f03ec540e11dc3a503521bbd5832819118a884
Binary files /dev/null and b/docs/source/images/parallax.png differ
diff --git a/docs/source/release/v3.14.0/diffraction.rst b/docs/source/release/v3.14.0/diffraction.rst
index 415099dd7ea46957214315a8a33ab9199fc68c7e..b26d90a4528cc052d4ea2e2e62b4b8773fbe8897 100644
--- a/docs/source/release/v3.14.0/diffraction.rst
+++ b/docs/source/release/v3.14.0/diffraction.rst
@@ -73,6 +73,7 @@ Bugfixes
 - multiple_scattering flag is now optional for Polaris focus when absorb_correction is true.
 - Normalisation is fixed in :ref:`SumOverlappingTubes <algm-SumOverlappingTubes>`, which was causing very low peak to background ratio for reduced D2B data.
 - sudden drops at either end of spectra in Pearl caused by partial bins are now cropped.
+- The Powder Diffraction GUI now remembers whether linear or logorithmic binning was selected between uses
 
 New
 ###
diff --git a/docs/source/release/v3.14.0/framework.rst b/docs/source/release/v3.14.0/framework.rst
index 2ddcd7ddd015026c428be337854d386301cf4a89..38fbdb8544706caceb52a9ef8a15be4d37d56be0 100644
--- a/docs/source/release/v3.14.0/framework.rst
+++ b/docs/source/release/v3.14.0/framework.rst
@@ -45,15 +45,18 @@ Algorithms
 New Algorithms
 ##############
 
+- :ref:`DeadTimeCorrection <algm-DeadTimeCorrection>` will correct for the detector dead time.
 - :ref:`CalculateDynamicRange <algm-CalculateDynamicRange>` will calculate the Q range of a SANS workspace.
 - :ref:`MatchSpectra <algm-MatchSpectra>` is an algorithm that calculates factors to match all spectra to a reference spectrum.
 - :ref:`MaskBinsIf <algm-MaskBinsIf>` is an algorithm to mask bins according to criteria specified as a muparser expression.
 - :ref:`MaskNonOverlappingBins <algm-MaskNonOverlappingBins>` masks the bins that do not overlap with another workspace.
+- :ref:`ParallaxCorrection <algm-ParallaxCorrection>` will perform a geometric correction for the so-called parallax effect in tube based SANS detectors.
 
 Improvements
 ############
 
 - :ref:`AppendSpectra <algm-AppendSpectra>` can append now multiple times the same event workspace.
+- :ref:`ConjoinXRuns <algm-ConjoinXRuns>` can merge sample logs according to the parameter file independently from :ref:`MergeRuns <algm-MergeRuns>`. All parameter names must have the prefix ``conjoin_`` appended by the corresponding default parameter names (which are used by :ref:`MergeRuns <algm-MergeRuns>`).
 - :ref:`CropToComponent <algm-CropToComponent>` now supports also scanning workspaces.
 - :ref:`SumOverlappingTubes <algm-SumOverlappingTubes>` will produce histogram data, and will not split the counts between bins by default.
 - :ref:`SumSpectra <algm-SumSpectra>` has an additional option, ``MultiplyBySpectra``, which controls whether or not the output spectra are multiplied by the number of bins. This property should be set to ``False`` for summing spectra as PDFgetN does.
@@ -67,6 +70,7 @@ Improvements
 - :ref:`LoadNexusLogs <algm-LoadNexusLogs-v1>` now will load files that have 1D arrays for each time value in the logs, but will not load this data.
 - :ref:`GroupDetectors <algm-GroupDetectors>` now takes masked bins correctly into account when processing histogram workspaces.
 - :ref:`SaveNexusProcessed <algm-SaveNexusProcessed>` and :ref:`LoadNexusProcessed <algm-LoadNexusProcessed>` can now save and load a ``MaskWorkspace``.
+- :ref:`FitPeaks <algm-FitPeaks>` can output parameters' uncertainty (fitting error) in an optional workspace.
 
 Bugfixes
 ########
@@ -78,11 +82,12 @@ Bugfixes
 - Fixed an issue where if a workspace's history wouldn't update for some algorithms
 - Fixed a ``std::bad_cast`` error in :ref:`algm-LoadLiveData` when the data size changes.
 - :ref:`Fit <algm-Fit>` now applies the ties in correct order independently on the order they are set. If any circular dependencies are found Fit will give an error.
-- Fixed a rare bug in :ref:`MaskDetectors <algm-MaskDetectors>` where a workspace could become invalidaded in Python if it was a ``MaskWorkspace``.
+- Fixed a rare bug in :ref:`MaskDetectors <algm-MaskDetectors>` where a workspace could become invalidated in Python if it was a ``MaskWorkspace``.
 - Fixed a crash in :ref:`MaskDetectors <algm-MaskDetectors>` when a non-existent component was given in ``ComponentList``.
 - History for algorithms that took groups sometimes would get incorrect history causing history to be incomplete, so now full group history is saved for all items belonging to the group.
 - Fixed a bug in `SetGoniometer <algm-SetGoniometer>` where it would use the mean log value rather than the time series average value for goniometer angles.
-
+- `ConvertToMD <algm-ConvertToMD>` now uses the time-average value for logs when using them as ``OtherDimensions``
+- The input validator is fixed in :ref:`MostLikelyMean <algm-MostLikelyMean>` avoiding a segmentation fault.
 
 Python
 ------
@@ -100,12 +105,17 @@ New
  * :class:`mantid.api.SpectrumInfo`
 
 - :class:`mantid.geometry.ComponentInfo` is exposed to allow the user to access geometric information about the components which are part of a beamline.
-
 - :class:`mantid.geometry.DetectorInfo` offers the user the ability to access geometric information about the detector(s) which are part of a beamline. ``DetectorInfo`` has also been given an iterator to allow users to write more Pythonic loops rather than normal index based loops.
-
 - :class:`mantid.api.SpectrumInfo` allows the user to access information about the spectra being used in a beamline. ``SpectrumInfo`` has also been given an iterator to allow users to write more Pythonic loops rather than normal index based loops. In addition to this ``SpectrumDefinition`` objects can also be accessed via a :class:`mantid.api.SpectrumInfo` object. The ``SpectrumDefinition`` object can be used to obtain information about the spectrum to detector mapping and provides a definition of what a spectrum comprises, i.e. indices of all detectors that contribute to the data stored in the spectrum.
-
 - Added new :ref:`unit <Unit Factory>` called ``Temperature`` which has units of Kelvin.
+- Importing ``mantid`` no longer initializes the ``FrameworkManager``. This allows separate classes to be imported without requiring a long delay in waiting for the framework to start. Amongst other things this allows the application name to be set correctly:
+
+.. code-block:: python
+
+   from mantid import FrameworkManager, UsageService
+   UsageService.setApplicationName('myapp')
+   FrameworkManager.Instance()
+
 
 Improvements
 ############
diff --git a/docs/source/release/v3.14.0/indirect_inelastic.rst b/docs/source/release/v3.14.0/indirect_inelastic.rst
index 71dee051f0abb0582dbbcf7a0f111934535fdc49..3c10a9cb3e91a0e538f8f888656e1b33179cbd44 100644
--- a/docs/source/release/v3.14.0/indirect_inelastic.rst
+++ b/docs/source/release/v3.14.0/indirect_inelastic.rst
@@ -102,6 +102,13 @@ Improvements
 
 - The Run button is now above the output options.
 - The Run, Plot and Save buttons are now disabled while running and plotting is taking place.
+- The sample logs are now copied over properly for the result workspace in the ResNorm tab.
+- Sqw files can now be loaded as Vanadium in the ResNorm interface.
+
+Bugfixes
+########
+
+- An unwanted 'Fit' plot is no longer plotted in ResNorm when you click `Plot` in the output options.
 
 
 Diffraction Interface
@@ -120,4 +127,4 @@ Tools Interface
 Improvements
 ############
 
-- The Run button has been moved in the Transmission tab, and is disabled while running.
+- The Run button has been moved in each of the Tools tabs, and is disabled while running.
diff --git a/docs/source/release/v3.14.0/muon.rst b/docs/source/release/v3.14.0/muon.rst
index a17940a3bb47db8303ea4baea05b81f00d18253f..c7d6102feba1984966d9291435405486ee9e666f 100644
--- a/docs/source/release/v3.14.0/muon.rst
+++ b/docs/source/release/v3.14.0/muon.rst
@@ -18,6 +18,7 @@ Bugfixes
 ########
 - Results table now includes all logs that are common to all of the loaded files.
 - When turning TF Asymmetry mode off it no longer resets the global options.
+- The x limits on the settings tab will now correct themselves if bad values are entered. 
 
 Algorithms
 ----------
diff --git a/docs/source/release/v3.14.0/reflectometry.rst b/docs/source/release/v3.14.0/reflectometry.rst
index b7473f39939dec96b5ce2b3de4c124eb42a0dc79..48bad3099ba191c4479d0a59a858dd67ccb70684 100644
--- a/docs/source/release/v3.14.0/reflectometry.rst
+++ b/docs/source/release/v3.14.0/reflectometry.rst
@@ -40,6 +40,7 @@ Bug fixes
 - A bug has been fixed on the Settings tab where the IncludePartialBins check box had been hidden by a misplaced text entry box.
 - :ref:`algm-ReflectometryReductionOneAuto` No longer sums all of a transmission run's workspaces and instead will use the first run only
 - In :ref:`algm-ReflectometryReductionOneAuto` an issue where if you gave only one of either MomentumTransferMax or MomentumTransferMin were specified it would be ignored, this has been fixed.
+- Reverted property names for polarization correction coefficients in :ref:`ReflectometryReductionOneAuto <algm-ReflectometryReductionOneAuto-v2>` for backwards compatibility.
 
 Liquids Reflectometer
 ---------------------
@@ -50,6 +51,7 @@ Magnetism Reflectometer
 -----------------------
 
 - Added option to overwrite :literal:`DIRPIX` and :literal:`DANGLE0`.
+- Added option to skip the final rebinning.
 
 ISIS Reflectometry Interface
 ----------------------------
diff --git a/docs/source/release/v3.14.0/sans.rst b/docs/source/release/v3.14.0/sans.rst
index f227880c6c414a77346bb3936cfda4558c632fee..48d8757a9755c6bd1ea61ef38435e5ef402fa78f 100644
--- a/docs/source/release/v3.14.0/sans.rst
+++ b/docs/source/release/v3.14.0/sans.rst
@@ -24,8 +24,11 @@ Improved
 * Updated file adding to prefix the instrument name
 * Updated file finding to be able to find added runs without instrument name prefix
 * Updated GUI code so calculated merge scale and shift are shown after reduction.
+* Removed instrument selection box. Instrument is now determined by user file.
 * Automatically remembers last loaded user file
 * Added display of current save directory
+* Added a load button to load selected workspaces without processing.
+* Added save_can option to output unsubtracted can and sample workspaces.
 
 Bug fixes
 #########
@@ -41,4 +44,9 @@ Improvements
 
 - :ref:`Q1DWeighted <algm-Q1DWeighted>` now supports the option of asymmetric wedges for unisotropic scatterer.
 
+Removed
+#######
+
+- Obsolete *SetupILLD33Reduction* algorithm was removed.
+
 :ref:`Release 3.14.0 <v3.14.0>`
diff --git a/docs/source/release/v3.14.0/ui.rst b/docs/source/release/v3.14.0/ui.rst
index 6147274e536dca67a65dbb21910ec0d31295b685..be6756153227b293a4f45fc89d3bf4f32c06f9e3 100644
--- a/docs/source/release/v3.14.0/ui.rst
+++ b/docs/source/release/v3.14.0/ui.rst
@@ -24,14 +24,15 @@ Project Recovery
 ----------------
 New
 ###
-- Project recovery can now make a recovery checkpoint on command using mantidplot.app.saveRecoveryCheckpoint() in either the interpreter or script windows in python
+- Project Recovery can now make a recovery checkpoint on command using mantidplot.app.saveRecoveryCheckpoint() in either the interpreter or script windows in python
+- Project Recovery now adds a lock file at the start of saving so if MantidPlot crashes when saving it will no longer use that checkpoint as it is incomplete.
 - If project recovery fails when attempting to recover a checkpoint it will open a new GUI offering multiple checkpoints to the user and the ability to open them in a script window. (See image below)
 
 .. figure:: ../../images/ProjectRecoveryFailureDialog.png
     :class: screenshot
     :align: right
     :figwidth: 70%
-    
+
 - Project Recovery can now make a recovery checkpoint on command using mantidplot.app.saveRecoveryCheckpoint() in either the interpreter or script windows in python
 - Project Recovery now adds a lock file at the start of saving so if MantidPlot crashes when saving it will no longer use that checkpoint as it is incomplete.
 - Project Recovery now has the ability to be changed from inside of MantidPlot without using the config files directly, this can be found in view->preferences->mantid->projectrecovery
@@ -70,6 +71,7 @@ Changes
 #######
 
 - All File Browser dialog boxes will now (by default) display all valid file extensions as the first file filter.
+- Plot -> Advanced now allows for plotting against any property in the ``Run`` object that can be represented as a single number. It uses the time-average value for time series properties and the average for others.
 
 BugFixes
 ########
diff --git a/instrument/D22_Parameters.xml b/instrument/D22_Parameters.xml
index df9e87462874262de08174941160f41a3230980f..8e2dfcc419bc3ae0288223f14b7ddae66ec5d288 100644
--- a/instrument/D22_Parameters.xml
+++ b/instrument/D22_Parameters.xml
@@ -1,16 +1,32 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <parameter-file instrument = "D22" valid-from="2017-10-01 23:59:59">
 
+
+    <!-- This should be removed when the new way of correction is in the workflow-->
     <component-link name="D22">
 
-    <!-- This is the formula to model the theta dependency of the
-    detector sensitivities because of varying path lengths in the
-    detector volume. This is evaluated with python eval.
-    Note that the np stands for the numpy, p must be the parallax:
-    p = arctan(x/l2) [in radians], where x is the coordinate of the
-    detector pixel, l2 is the sample to detector distance. -->
-		<parameter name="parallax" type="string">
-		  <value val="1+0.14*np.exp(-4*np.log(2.)*((np.abs(p)-0.588)/0.414)**2)"/>
+      <!-- This is the formula to model the theta dependency of the
+      detector sensitivities because of varying path lengths in the
+      detector volume. This is evaluated with python eval.
+      Note that the np stands for the numpy, p must be the parallax:
+      p = arctan(x/l2) [in radians], where x is the coordinate of the
+      detector pixel, l2 is the sample to detector distance. -->
+      <parameter name="parallax_old" type="string">
+        <value val="1+0.14*np.exp(-4*np.log(2.)*((np.abs(p)-0.588)/0.414)**2)"/>
+      </parameter>
+
+    </component-link>
+
+    <!-- These parameters are used in ParallaxCorrection algorithm -->
+
+    <component-link name="detector">
+
+    <parameter name="parallax" type="string">
+		  <value val="1+0.14*exp(-4*log(2.)*((t-0.588)/0.414)^2)"/>
+		</parameter>
+
+    <parameter name="direction" type="string">
+		  <value val="y"/>
 		</parameter>
 
     </component-link>
diff --git a/instrument/D22lr_Parameters.xml b/instrument/D22lr_Parameters.xml
index b6fc8c44aaf517b6321ee87f9e3754be1dc06f4f..f8dd9a0f56269a32759875dff22184159868ce96 100644
--- a/instrument/D22lr_Parameters.xml
+++ b/instrument/D22lr_Parameters.xml
@@ -1,16 +1,31 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <parameter-file instrument = "D22lr" valid-from="2017-10-01 23:59:59">
 
+    <!-- This should be removed when the new way of correction is in the workflow-->
     <component-link name="D22lr">
 
-    <!-- This is the formula to model the theta dependency of the
-    detector sensitivities because of varying path lengths in the
-    detector volume. This is evaluated with python eval.
-    Note that the np stands for the numpy, p must be the parallax:
-    p = arctan(x/l2) [in radians], where x is the coordinate of the
-    detector pixel, l2 is the sample to detector distance. -->
-		<parameter name="parallax" type="string">
-		  <value val="1+0.14*np.exp(-4*np.log(2.)*((np.abs(p)-0.588)/0.414)**2)"/>
+      <!-- This is the formula to model the theta dependency of the
+      detector sensitivities because of varying path lengths in the
+      detector volume. This is evaluated with python eval.
+      Note that the np stands for the numpy, p must be the parallax:
+      p = arctan(x/l2) [in radians], where x is the coordinate of the
+      detector pixel, l2 is the sample to detector distance. -->
+      <parameter name="parallax_old" type="string">
+        <value val="1+0.14*np.exp(-4*np.log(2.)*((np.abs(p)-0.588)/0.414)**2)"/>
+      </parameter>
+
+    </component-link>
+
+    <!-- These parameters are used in ParallaxCorrection algorithm -->
+
+    <component-link name="detector">
+
+    <parameter name="parallax" type="string">
+		  <value val="1+0.14*exp(-4*log(2.)*((abs(t)-0.588)/0.414)^2)"/>
+		</parameter>
+
+    <parameter name="direction" type="string">
+		  <value val="y"/>
 		</parameter>
 
     </component-link>
diff --git a/instrument/D33_Parameters.xml b/instrument/D33_Parameters.xml
index 4cafda254fd06d71b8fdb668c7b806871f21477e..e4e753af62fefadfb675eef25600f5a9a165234c 100644
--- a/instrument/D33_Parameters.xml
+++ b/instrument/D33_Parameters.xml
@@ -1,23 +1,66 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <parameter-file instrument = "D33" valid-from = "20th May 2010">
-    
-    <component-link name="D33">
-		
-		<parameter name="number-of-x-pixels">
-		  <value val="256"/>
-		</parameter>
-
-		<parameter name="number-of-y-pixels">
-		  <value val="128"/>
-		</parameter>
-
-		<parameter name="x-pixel-size">
-		  <value val="5.5"/>
-		</parameter>
-		
-		<parameter name="y-pixel-size">
-		  <value val="4.297"/>
-		</parameter>
-
-    </component-link>
-</parameter-file>    
+
+  <!-- These parameters are used in ParallaxCorrection algorithm -->
+
+  <component-link name="back_detector">
+
+    <parameter name="parallax" type="string">
+      <value val="(t * 180 / 3.14 &lt; 10) ? (1 + t * 180 / 3.14 * 0.0081615 / 1.0273) : (1 + 10 * 0.0081615 / 1.0273)"/>
+    </parameter>
+
+    <parameter name="direction" type="string">
+      <value val="x"/>
+    </parameter>
+
+  </component-link>
+
+  <component-link name="front_detector_right">
+
+    <parameter name="parallax" type="string">
+      <value val="(t * 180 / 3.14 &lt; 10) ? (1 + t * 180 / 3.14 * 0.005026 / 0.90814) : (1 + 10 * 0.005026 / 0.90814)"/>
+    </parameter>
+
+    <parameter name="direction" type="string">
+      <value val="y"/>
+    </parameter>
+
+  </component-link>
+
+  <component-link name="front_detector_left">
+
+    <parameter name="parallax" type="string">
+      <value val="(t * 180 / 3.14 &lt; 10) ? (1 + t * 180 / 3.14 * 0.005026 / 0.90814) : (1 + 10 * 0.005026 / 0.90814)"/>
+    </parameter>
+
+    <parameter name="direction" type="string">
+      <value val="y"/>
+    </parameter>
+
+  </component-link>
+
+  <component-link name="front_detector_top">
+
+    <parameter name="parallax" type="string">
+      <value val="(t * 180 / 3.14 &lt; 10) ? (1 + t * 180 / 3.14 * 0.0058296 / 0.98876) : (1 + 10 * 0.0058296 / 0.98876)"/>
+    </parameter>
+
+    <parameter name="direction" type="string">
+      <value val="x"/>
+    </parameter>
+
+  </component-link>
+
+  <component-link name="front_detector_bottom">
+
+    <parameter name="parallax" type="string">
+      <value val="(t * 180 / 3.14 &lt; 10) ? (1 + t * 180 / 3.14 * 0.0058296 / 0.98876) : (1 + 10 * 0.0058296 / 0.98876)"/>
+    </parameter>
+
+    <parameter name="direction" type="string">
+      <value val="x"/>
+    </parameter>
+
+  </component-link>
+
+</parameter-file>
diff --git a/instrument/FIGARO_Parameters.xml b/instrument/FIGARO_Parameters.xml
index 4d42fca9126db23df8715fb1728da09aaace8fac..ddd6abc72d63a475475c26ca5654eb9d0cc8f502 100644
--- a/instrument/FIGARO_Parameters.xml
+++ b/instrument/FIGARO_Parameters.xml
@@ -24,5 +24,24 @@
 		<parameter name="sample_logs_fail_tolerances" type="string">
 			<value val="0, 0, 0, 0" />
 		</parameter>
+                <!-- ConjoinXRuns behavior when merging sample logs when using Stitch1D or Stitch1DMany. -->
+		<parameter name="conjoin_sample_logs_sum" type="string">
+			<value val="" />
+		</parameter>
+		<parameter name="conjoin_sample_logs_time_series" type="string">
+			<value val="" />
+		</parameter>
+		<parameter name="conjoin_sample_logs_warn" type="string">
+			<value val="" />
+		</parameter>
+		<parameter name="conjoin_sample_logs_warn_tolerances" type="string">
+			<value val="" />
+		</parameter>
+		<parameter name="conjoin_sample_logs_fail" type="string">
+			<value val="" />
+		</parameter>
+		<parameter name="conjoin_sample_logs_fail_tolerances" type="string">
+			<value val="" />
+		</parameter>        
 	</component-link>
 </parameter-file>
diff --git a/qt/applications/workbench/workbench/app/mainwindow.py b/qt/applications/workbench/workbench/app/mainwindow.py
index 5ba5d0f575293a8438dc0cab54bb5ca1c28a4a43..4022774d702953148e9a21622b3091707ec4d710 100644
--- a/qt/applications/workbench/workbench/app/mainwindow.py
+++ b/qt/applications/workbench/workbench/app/mainwindow.py
@@ -12,13 +12,19 @@ Defines the QMainWindow of the application and the main() entry point.
 """
 from __future__ import (absolute_import, division,
                         print_function, unicode_literals)
+
+# std imports
 import argparse  # for command line options
 import atexit
-import imp
 import importlib
 import os
 import sys
 
+# third party imports
+from mantid.kernel import (ConfigService, logger, UsageService,
+                           version_str as mantid_version_str)
+from mantid.api import FrameworkManagerImpl
+
 # -----------------------------------------------------------------------------
 # Constants
 # -----------------------------------------------------------------------------
@@ -41,14 +47,16 @@ from qtpy.QtCore import (QEventLoop, Qt, QCoreApplication, QPoint, QSize, QSetti
 from qtpy.QtGui import (QColor, QGuiApplication, QIcon, QPixmap)  # noqa
 from qtpy.QtWidgets import (QApplication, QDesktopWidget, QFileDialog,
                             QMainWindow, QSplashScreen)  # noqa
-from mantidqt.utils.qt import plugins, widget_updates_disabled  # noqa
 from mantidqt.algorithminputhistory import AlgorithmInputHistory  # noqa
+from mantidqt.widgets.manageuserdirectories import ManageUserDirectories  # noqa
 from mantidqt.widgets.codeeditor.execution import PythonCodeExecution  # noqa
+from mantidqt.utils.qt import (add_actions, create_action, plugins,
+                               widget_updates_disabled)  # noqa
 
 # Pre-application setup
 plugins.setup_library_paths()
 
-from workbench.config import APPNAME, CONF, ORG_DOMAIN, ORGANIZATION, set_config_format  # noqa
+from workbench.config import APPNAME, CONF, ORG_DOMAIN, ORGANIZATION  # noqa
 
 
 # -----------------------------------------------------------------------------
@@ -66,15 +74,22 @@ def qapplication():
         QCoreApplication.setAttribute(Qt.AA_ShareOpenGLContexts)
         argv = sys.argv[:]
         argv[0] = APPNAME  # replace application name
+        # Workaround a segfault with the IPython console when using Python 3.5 + PyQt 5
+        # Without this using this fix the above combination causes a segfault when the IPython
+        # console is started
+        # The workaround mentioned in https://groups.google.com/forum/#!topic/leo-editor/ghiIN7irzY0
+        # is to ensure readline is imported before the QApplication object is created
+        if sys.version_info[0] == 3 and sys.version_info[1] == 5:
+            importlib.import_module("readline")
         app = QApplication(argv)
         app.setOrganizationName(ORGANIZATION)
         app.setOrganizationDomain(ORG_DOMAIN)
         app.setApplicationName(APPNAME)
-        # not calling app.setApplicationVersion(mantid.kernel.version_str())
-        # because it needs to happen after logging is monkey-patched in
+        app.setApplicationVersion(mantid_version_str())
+        # Spin up the usage service and set the name for the usage reporting
+        # The report is sent when the FrameworkManager kicks up
+        UsageService.setApplicationName(APPNAME)
 
-        # Set the config format to IniFormat globally
-        set_config_format(QSettings.IniFormat)
     return app
 
 
@@ -108,12 +123,6 @@ SPLASH.showMessage("Starting...", Qt.AlignBottom | Qt.AlignLeft
 # The event loop has not started - force event processing
 QApplication.processEvents(QEventLoop.AllEvents)
 
-# -----------------------------------------------------------------------------
-# Utilities/Widgets
-# -----------------------------------------------------------------------------
-from mantidqt.utils.qt import add_actions, create_action  # noqa
-from mantidqt.widgets.manageuserdirectories import ManageUserDirectories  # noqa
-
 
 # -----------------------------------------------------------------------------
 # MainWindow
@@ -205,6 +214,13 @@ class MainWindow(QMainWindow):
         self.create_actions()
         self.populate_menus()
 
+    def post_mantid_init(self):
+        """Run any setup that requires mantid
+        to have been initialized
+        """
+        self.populate_interfaces_menu()
+        self.algorithm_selector.refresh()
+
     def set_splash(self, msg=None):
         if not self.splash:
             return
@@ -266,14 +282,12 @@ class MainWindow(QMainWindow):
         add_actions(self.file_menu, self.file_menu_actions)
         add_actions(self.view_menu, self.view_menu_actions)
 
-    def launchCustomGUI(self, filename):
-        from mantid.kernel import logger  # noqa
+    def launch_custom_gui(self, filename):
         executioner = PythonCodeExecution()
         executioner.sig_exec_error.connect(lambda errobj: logger.warning(str(errobj)))
         executioner.execute(open(filename).read(), filename)
 
-    def populateAfterMantidImport(self):
-        from mantid.kernel import ConfigService, logger  # noqa
+    def populate_interfaces_menu(self):
         interface_dir = ConfigService['mantidqt.python_interfaces_directory']
         items = ConfigService['mantidqt.python_interfaces'].split()
 
@@ -307,7 +321,7 @@ class MainWindow(QMainWindow):
             for name in names:
                 action = submenu.addAction(name.replace('.py', '').replace('_', ' '))
                 script = os.path.join(interface_dir, name)
-                action.triggered.connect(lambda checked, script=script: self.launchCustomGUI(script))
+                action.triggered.connect(lambda checked, script=script: self.launch_custom_gui(script))
 
     def add_dockwidget(self, plugin):
         """Create a dockwidget around a plugin and add the dock to window"""
@@ -512,13 +526,13 @@ def start_workbench(app, command_line_options):
     from workbench.plotting.config import initialize_matplotlib  # noqa
     initialize_matplotlib()
 
-    # Setup widget layouts etc. mantid cannot be imported before this
-    # or the log messages don't get through
+    # Setup widget layouts etc. mantid.simple cannot be used before this
+    # or the log messages don't get through to the widget
     main_window.setup()
     # start mantid
-    main_window.set_splash('Preloading mantid')
-    importlib.import_module('mantid')
-    main_window.populateAfterMantidImport()
+    main_window.set_splash('Initializing mantid framework')
+    FrameworkManagerImpl.Instance()
+    main_window.post_mantid_init()
     main_window.show()
     main_window.setWindowIcon(QIcon(':/images/MantidIcon.ico'))
 
@@ -540,16 +554,6 @@ def start_workbench(app, command_line_options):
 
 def main():
     """Main entry point for the application"""
-    # Mantid needs to be able to find its .properties file. It looks
-    # in the application directory by default but this is
-    # the directory of python[.exe] and not guaranteed to be where
-    # the properties files is located. MANTIDPATH overrides this.
-    # If we allow a user to override MANTIDPATH then we could end up
-    # loading the wrong properties file and plugins built against
-    # a different version of Mantid and this would likely result in
-    # segfault.
-    _, pkgpath, _ = imp.find_module('mantid')
-    os.environ['MANTIDPATH'] = os.path.dirname(pkgpath)
 
     # setup command line arguments
     parser = argparse.ArgumentParser(description='Mantid Workbench')
diff --git a/qt/applications/workbench/workbench/config/__init__.py b/qt/applications/workbench/workbench/config/__init__.py
index e19faa2d2fb3e558172471695e0e05375ffa229f..897b7fcb040bbf12d4ed590ca07ef8442c350c5b 100644
--- a/qt/applications/workbench/workbench/config/__init__.py
+++ b/qt/applications/workbench/workbench/config/__init__.py
@@ -41,8 +41,5 @@ DEFAULTS = {
 # -----------------------------------------------------------------------------
 # 'Singleton' instance
 # -----------------------------------------------------------------------------
+QSettings.setDefaultFormat(QSettings.IniFormat)
 CONF = UserConfig(ORGANIZATION, APPNAME, defaults=DEFAULTS)
-
-
-def set_config_format(format):
-    QSettings.setDefaultFormat(format)
diff --git a/qt/applications/workbench/workbench/config/user.py b/qt/applications/workbench/workbench/config/user.py
index a832e3ac316618c18dc9c49d2cf6bd28725d68e7..066338d1067ec9d55f7c240e94344a4f370d43fe 100644
--- a/qt/applications/workbench/workbench/config/user.py
+++ b/qt/applications/workbench/workbench/config/user.py
@@ -49,7 +49,10 @@ class UserConfig(object):
         # fixup the values of booleans - they do not evaluate correctly when read from the config file
         # TODO come up with a unit test for this
         for key in self.all_keys():
-            value = self.get(key)
+            try:
+                value = self.get(key)
+            except KeyError:
+                continue
             if value == 'true':
                 self.set(key, True)
             elif value == 'false':
diff --git a/qt/applications/workbench/workbench/plotting/test/test_functions.py b/qt/applications/workbench/workbench/plotting/test/test_functions.py
index 9d89d2c4f78f6533a2fe1f1a0361ad823e902b6a..fe5142ca40871b98b16f659e815d3b3eb4854055 100644
--- a/qt/applications/workbench/workbench/plotting/test/test_functions.py
+++ b/qt/applications/workbench/workbench/plotting/test/test_functions.py
@@ -18,6 +18,8 @@ except ImportError:
 
 # third party imports
 from mantid.api import AnalysisDataService, WorkspaceFactory
+# register mantid projection
+import mantid.plots  # noqa
 import matplotlib
 matplotlib.use('AGG')  # noqa
 import matplotlib.pyplot as plt
diff --git a/qt/applications/workbench/workbench/plugins/algorithmselectorwidget.py b/qt/applications/workbench/workbench/plugins/algorithmselectorwidget.py
index 366ed3958f58412e95e92b1fb941f3ea802b0187..efa69bf258e4283e10065de0c9be971d332b5f73 100644
--- a/qt/applications/workbench/workbench/plugins/algorithmselectorwidget.py
+++ b/qt/applications/workbench/workbench/plugins/algorithmselectorwidget.py
@@ -32,6 +32,10 @@ class AlgorithmSelector(PluginWidget):
         layout.addWidget(self.algorithm_selector)
         self.setLayout(layout)
 
+    def refresh(self):
+        """Refreshes the algorithm list"""
+        self.algorithm_selector.refresh()
+
 # ----------------- Plugin API --------------------
 
     def register_plugin(self):
diff --git a/qt/python/mantidqt/widgets/algorithmselector/model.py b/qt/python/mantidqt/widgets/algorithmselector/model.py
index 6d98be3fac0d86366a7c0d26d76ff327c0e74591..821fe117edeefd9bb5b82d69b60addaf36a7eabe 100644
--- a/qt/python/mantidqt/widgets/algorithmselector/model.py
+++ b/qt/python/mantidqt/widgets/algorithmselector/model.py
@@ -6,7 +6,7 @@
 # SPDX - License - Identifier: GPL - 3.0 +
 from __future__ import absolute_import, print_function
 
-from mantid import AlgorithmFactory
+from mantid.api import AlgorithmFactoryImpl
 
 
 class AlgorithmSelectorModel(object):
@@ -54,7 +54,7 @@ class AlgorithmSelectorModel(object):
             Here self.algorithm_key == '_'
 
         """
-        descriptors = AlgorithmFactory.getDescriptors(self.include_hidden)
+        descriptors = AlgorithmFactoryImpl.Instance().getDescriptors(self.include_hidden)
         algorithm_names = []
         data = {}
         for descriptor in descriptors:
diff --git a/qt/python/mantidqt/widgets/algorithmselector/presenter.py b/qt/python/mantidqt/widgets/algorithmselector/presenter.py
index c05ebeb171e4465717f9b9d21ff756a2af14fc65..e62bc37f326a2e80fa1437501c0df38733f1012b 100644
--- a/qt/python/mantidqt/widgets/algorithmselector/presenter.py
+++ b/qt/python/mantidqt/widgets/algorithmselector/presenter.py
@@ -33,6 +33,9 @@ class IAlgorithmSelectorView(object):
     def populate_ui(self, data):
         raise NotImplementedError('Method has to be implemented in a subclass')
 
+    def refresh(self):
+        raise NotImplementedError('Method has to be implemented in a subclass')
+
     def get_selected_algorithm(self):
         raise NotImplementedError('Method has to be implemented in a subclass')
 
@@ -50,5 +53,8 @@ class AlgorithmSelectorPresenter(object):
     def __init__(self, view, include_hidden):
         self.view = view
         self.model = AlgorithmSelectorModel(self, include_hidden)
+        self.refresh()
+
+    def refresh(self):
         algorithm_data = self.model.get_algorithm_data()
         self.view.populate_ui(algorithm_data)
diff --git a/qt/python/mantidqt/widgets/algorithmselector/test/test_algorithmselector.py b/qt/python/mantidqt/widgets/algorithmselector/test/test_algorithmselector.py
index fb2d7cc8f0cfc9956ed2a58e8793868ff1b581b8..47015e452e176484a967117c71df0a8593b1a695 100644
--- a/qt/python/mantidqt/widgets/algorithmselector/test/test_algorithmselector.py
+++ b/qt/python/mantidqt/widgets/algorithmselector/test/test_algorithmselector.py
@@ -18,40 +18,31 @@ import unittest
 from qtpy.QtCore import Qt
 from qtpy.QtTest import QTest
 
+from mantid.api import AlgorithmFactoryImpl
 from mantidqt.utils.qt.test import select_item_in_combo_box, select_item_in_tree, GuiTest
 from mantidqt.widgets.algorithmselector.model import AlgorithmSelectorModel
 from mantidqt.widgets.algorithmselector.widget import AlgorithmSelectorWidget
 
 AlgorithmDescriptorMock = namedtuple('AlgorithmDescriptorMock', ['name', 'alias', 'category', 'version'])
 mock_get_algorithm_descriptors = Mock()
-mock_get_algorithm_descriptors.return_value = [AlgorithmDescriptorMock(name='Rebin', version=1,
-                                                                       category='Transform', alias=''),
-                                               AlgorithmDescriptorMock(name='Rebin', version=1,
-                                                                       category='Transform\\Rebin', alias=''),
-                                               AlgorithmDescriptorMock(name='Load', version=1,
-                                                                       category='Data', alias=''),
-                                               AlgorithmDescriptorMock(name='DoStuff', version=1,
-                                                                       category='Stuff', alias=''),
-                                               AlgorithmDescriptorMock(name='DoStuff', version=2,
-                                                                       category='Stuff', alias=''),
-                                               ]
-
-
-class AlgorithmFactoryTest(unittest.TestCase):
-
-    def test_getDescriptors(self):
-        from mantid import AlgorithmFactory
-        descriptors = AlgorithmFactory.getDescriptors(True)
-        self.assertGreater(len(descriptors), 0)
-        d = descriptors[0]
-        self.assertFalse(isinstance(d, AlgorithmDescriptorMock))
-        self.assertTrue(hasattr(d, 'name'))
-        self.assertTrue(hasattr(d, 'alias'))
-        self.assertTrue(hasattr(d, 'category'))
-        self.assertTrue(hasattr(d, 'version'))
-
-
-@patch('mantid.AlgorithmFactory.getDescriptors', mock_get_algorithm_descriptors)
+mock_get_algorithm_descriptors.return_value = [
+    AlgorithmDescriptorMock(name='Rebin', version=1,
+                            category='Transform', alias=''),
+    AlgorithmDescriptorMock(name='Rebin', version=1,
+                            category='Transform\\Rebin', alias=''),
+    AlgorithmDescriptorMock(name='Load', version=1,
+                            category='Data', alias=''),
+    AlgorithmDescriptorMock(name='DoStuff', version=1,
+                            category='Stuff', alias=''),
+    AlgorithmDescriptorMock(name='DoStuff', version=2,
+                            category='Stuff', alias=''),
+]
+
+empty_mock_get_algorithm_descriptors = Mock()
+empty_mock_get_algorithm_descriptors.return_value = []
+
+
+@patch.object(AlgorithmFactoryImpl, 'getDescriptors', mock_get_algorithm_descriptors)
 class ModelTest(unittest.TestCase):
 
     def test_get_algorithm_data(self):
@@ -74,9 +65,16 @@ class ModelTest(unittest.TestCase):
         self.assertEqual(mock_get_algorithm_descriptors.mock_calls[-1], call(True))
 
 
-@patch('mantid.AlgorithmFactory.getDescriptors', mock_get_algorithm_descriptors)
+@patch.object(AlgorithmFactoryImpl, 'getDescriptors', mock_get_algorithm_descriptors)
 class WidgetTest(GuiTest):
 
+    # def setUp(self):
+    #     self.getDescriptors_orig = AlgorithmFactoryImpl.getDescriptors
+    #     AlgorithmFactoryImpl.getDescriptors = mock_get_algorithm_descriptors
+    #
+    # def tearDown(self):
+    #     AlgorithmFactoryImpl.getDescriptors = self.getDescriptors_orig
+
     def _select_in_tree(self, widget, item_label):
         select_item_in_tree(widget.tree, item_label)
 
@@ -120,7 +118,7 @@ class WidgetTest(GuiTest):
                           "and the default Qt behaviour is used")
         else:
             widget = AlgorithmSelectorWidget()
-            self.assertEquals(widget.search_box.completer().filterMode(), Qt.MatchContains)
+            self.assertEqual(widget.search_box.completer().filterMode(), Qt.MatchContains)
 
     def test_search_box_selection_ignores_tree_selection(self):
         widget = AlgorithmSelectorWidget()
@@ -159,6 +157,18 @@ class WidgetTest(GuiTest):
             QTest.keyClick(widget.search_box, Qt.Key_Return)
             createDialog.assert_called_once_with('DoStuff', 2)
 
+    def test_refresh(self):
+        # Set a mock to return an empty descriptor list
+        getDescriptors_orig = AlgorithmFactoryImpl.getDescriptors
+        AlgorithmFactoryImpl.getDescriptors = empty_mock_get_algorithm_descriptors
+
+        widget = AlgorithmSelectorWidget()
+        self.assertEqual(0, widget.tree.topLevelItemCount())
+        # put back the original
+        AlgorithmFactoryImpl.getDescriptors = getDescriptors_orig
+        widget.refresh()
+        self.assertEqual(3, widget.tree.topLevelItemCount())
+
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/qt/python/mantidqt/widgets/algorithmselector/widget.py b/qt/python/mantidqt/widgets/algorithmselector/widget.py
index 45276cce822da00a5d9a7b1751670f1988f13009..05106b177edb8fac47f0bd4f0f9d155799d9c08f 100644
--- a/qt/python/mantidqt/widgets/algorithmselector/widget.py
+++ b/qt/python/mantidqt/widgets/algorithmselector/widget.py
@@ -54,6 +54,10 @@ class AlgorithmSelectorWidget(IAlgorithmSelectorView, QWidget):
         QWidget.__init__(self, parent)
         IAlgorithmSelectorView.__init__(self, include_hidden)
 
+    def refresh(self):
+        """Update the algorithms list"""
+        self.presenter.refresh()
+
     def _make_execute_button(self):
         """
         Make the button that starts the algorithm.
diff --git a/qt/scientific_interfaces/ISISReflectometry/QtReflSettingsView.cpp b/qt/scientific_interfaces/ISISReflectometry/QtReflSettingsView.cpp
index d8377bdc3564a761775445b3c195f072e27319a5..dd7e68e2f9bf7a09f8104b53f0ec7348c5dcc526 100644
--- a/qt/scientific_interfaces/ISISReflectometry/QtReflSettingsView.cpp
+++ b/qt/scientific_interfaces/ISISReflectometry/QtReflSettingsView.cpp
@@ -159,10 +159,10 @@ void QtReflSettingsView::registerExperimentSettingsWidgets(
   registerSettingWidget(*m_ui.startOverlapEdit, "StartOverlap", alg);
   registerSettingWidget(*m_ui.endOverlapEdit, "EndOverlap", alg);
   registerSettingWidget(*m_ui.polCorrComboBox, "PolarizationAnalysis", alg);
-  registerSettingWidget(*m_ui.CRhoEdit, "Rho", alg);
-  registerSettingWidget(*m_ui.CAlphaEdit, "Alpha", alg);
-  registerSettingWidget(*m_ui.CApEdit, "Ap", alg);
-  registerSettingWidget(*m_ui.CPpEdit, "Pp", alg);
+  registerSettingWidget(*m_ui.CRhoEdit, "CRho", alg);
+  registerSettingWidget(*m_ui.CAlphaEdit, "CAlpha", alg);
+  registerSettingWidget(*m_ui.CApEdit, "CAp", alg);
+  registerSettingWidget(*m_ui.CPpEdit, "CPp", alg);
   registerSettingWidget(stitchOptionsLineEdit(), "Params", alg);
 }
 
diff --git a/qt/scientific_interfaces/ISISReflectometry/ReflSettingsPresenter.cpp b/qt/scientific_interfaces/ISISReflectometry/ReflSettingsPresenter.cpp
index 06425ad01c06082b5d02f34db295111192a5cce0..61c7b89f5428dac6eb261ac63c6415c17964651f 100644
--- a/qt/scientific_interfaces/ISISReflectometry/ReflSettingsPresenter.cpp
+++ b/qt/scientific_interfaces/ISISReflectometry/ReflSettingsPresenter.cpp
@@ -231,13 +231,13 @@ OptionsQMap ReflSettingsPresenter::getReductionOptions() const {
     auto const pa = m_view->getPolarisationCorrections();
     addIfNotEmpty(options, "PolarizationAnalysis", pa);
     if (pa == "PA") {
-      addIfNotEmpty(options, "Rho", m_view->getCRho());
-      addIfNotEmpty(options, "Alpha", m_view->getCAlpha());
-      addIfNotEmpty(options, "Ap", m_view->getCAp());
-      addIfNotEmpty(options, "Pp", m_view->getCPp());
+      addIfNotEmpty(options, "CRho", m_view->getCRho());
+      addIfNotEmpty(options, "CAlpha", m_view->getCAlpha());
+      addIfNotEmpty(options, "CAp", m_view->getCAp());
+      addIfNotEmpty(options, "CPp", m_view->getCPp());
     } else if (pa == "PNR") {
-      addIfNotEmpty(options, "Ap", m_view->getCAp());
-      addIfNotEmpty(options, "Pp", m_view->getCPp());
+      addIfNotEmpty(options, "CAp", m_view->getCAp());
+      addIfNotEmpty(options, "CPp", m_view->getCPp());
     }
     addIfNotEmpty(options, "StartOverlap", m_view->getStartOverlap());
     addIfNotEmpty(options, "EndOverlap", m_view->getEndOverlap());
diff --git a/qt/scientific_interfaces/Indirect/CMakeLists.txt b/qt/scientific_interfaces/Indirect/CMakeLists.txt
index a1ca49ee8acdb5b85cad5d3533376f183d8f4d55..f3319726973fef3bf1a82681c244275ec6742406 100644
--- a/qt/scientific_interfaces/Indirect/CMakeLists.txt
+++ b/qt/scientific_interfaces/Indirect/CMakeLists.txt
@@ -67,7 +67,7 @@ set ( SRC_FILES
 # Include files aren't required, but this makes them appear in Visual Studio
 # IMPORTANT: Include files are required in the MOC_FILES set. Scroll down to find it.
 set ( INC_FILES
-    DllConfig.h
+	DllConfig.h
 	AbsorptionCorrections.h
 	ApplyAbsorptionCorrections.h
 	CalculatePaalmanPings.h
@@ -81,6 +81,7 @@ set ( INC_FILES
 	DensityOfStates.h
 	Elwin.h
 	IAddWorkspaceDialog.h
+	IIndirectFitPlotView.h
 	ILLEnergyTransfer.h
 	ISISCalibration.h
 	ISISDiagnostics.h
@@ -147,6 +148,7 @@ set ( MOC_FILES
     CorrectionsTab.h
     DensityOfStates.h
     Elwin.h
+    IIndirectFitPlotView.h
     ILLEnergyTransfer.h
     IndirectAddWorkspaceDialog.h
     IndirectBayes.h
diff --git a/qt/scientific_interfaces/Indirect/ConvFitModel.cpp b/qt/scientific_interfaces/Indirect/ConvFitModel.cpp
index 3cbdc9996f5d07df3e26617d2496580d3af9ce4c..0b73a54635633b0efb74c652aadde16dd3415281 100644
--- a/qt/scientific_interfaces/Indirect/ConvFitModel.cpp
+++ b/qt/scientific_interfaces/Indirect/ConvFitModel.cpp
@@ -19,6 +19,11 @@ using namespace Mantid::API;
 
 namespace {
 
+MatrixWorkspace_sptr getADSMatrixWorkspace(std::string const &workspaceName) {
+  return AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(
+      workspaceName);
+}
+
 boost::optional<std::size_t>
 getFirstInCategory(CompositeFunction_const_sptr composite,
                    const std::string &category) {
@@ -110,13 +115,11 @@ CompositeFunction_sptr addTemperatureCorrection(IFunction_sptr model,
   return applyTemperatureCorrection(model, correction, value);
 }
 
-IAlgorithm_sptr loadParameterFileAlgorithm(MatrixWorkspace_sptr workspace,
-                                           const std::string &filename) {
-  IAlgorithm_sptr loadParamFile =
-      AlgorithmManager::Instance().create("LoadParameterFile");
-  loadParamFile->setChild(true);
+IAlgorithm_sptr loadParameterFileAlgorithm(std::string const &workspaceName,
+                                           std::string const &filename) {
+  auto loadParamFile = AlgorithmManager::Instance().create("LoadParameterFile");
   loadParamFile->initialize();
-  loadParamFile->setProperty("Workspace", workspace);
+  loadParamFile->setProperty("Workspace", workspaceName);
   loadParamFile->setProperty("Filename", filename);
   return loadParamFile;
 }
@@ -130,8 +133,8 @@ void readAnalyserFromFile(const std::string &analyser,
   auto const parameterFile = idfDirectory + instrument->getName() + "_" +
                              analyser + "_" + reflection + "_Parameters.xml";
 
-  IAlgorithm_sptr loadParamFile =
-      loadParameterFileAlgorithm(workspace, parameterFile);
+  auto loadParamFile =
+      loadParameterFileAlgorithm(workspace->getName(), parameterFile);
   loadParamFile->execute();
 
   if (!loadParamFile->isExecuted())
@@ -170,37 +173,50 @@ boost::optional<double> instrumentResolution(MatrixWorkspace_sptr workspace) {
   }
 }
 
-MatrixWorkspace_sptr cloneWorkspace(MatrixWorkspace_sptr inputWS) {
-  IAlgorithm_sptr cloneAlg =
-      AlgorithmManager::Instance().create("CloneWorkspace");
+MatrixWorkspace_sptr cloneWorkspace(MatrixWorkspace_sptr inputWS,
+                                    std::string const &outputName) {
+  auto cloneAlg = AlgorithmManager::Instance().create("CloneWorkspace");
   cloneAlg->setLogging(false);
-  cloneAlg->setChild(true);
   cloneAlg->initialize();
   cloneAlg->setProperty("InputWorkspace", inputWS);
-  cloneAlg->setProperty("OutputWorkspace", "__cloned");
+  cloneAlg->setProperty("OutputWorkspace", outputName);
   cloneAlg->execute();
-  Workspace_sptr workspace = cloneAlg->getProperty("OutputWorkspace");
-  return boost::dynamic_pointer_cast<MatrixWorkspace>(workspace);
+  return getADSMatrixWorkspace(outputName);
 }
 
 MatrixWorkspace_sptr appendWorkspace(MatrixWorkspace_sptr leftWS,
                                      MatrixWorkspace_sptr rightWS,
-                                     int numHistograms) {
-  IAlgorithm_sptr appendAlg =
-      AlgorithmManager::Instance().create("AppendSpectra");
+                                     int numHistograms,
+                                     std::string const &outputName) {
+  auto appendAlg = AlgorithmManager::Instance().create("AppendSpectra");
   appendAlg->setLogging(false);
-  appendAlg->setChild(true);
   appendAlg->initialize();
   appendAlg->setProperty("InputWorkspace1", leftWS);
   appendAlg->setProperty("InputWorkspace2", rightWS);
   appendAlg->setProperty("Number", numHistograms);
-  appendAlg->setProperty("OutputWorkspace", "__appended");
+  appendAlg->setProperty("OutputWorkspace", outputName);
   appendAlg->execute();
-  return appendAlg->getProperty("OutputWorkspace");
+  return getADSMatrixWorkspace(outputName);
+}
+
+void renameWorkspace(std::string const &name, std::string const &newName) {
+  auto renamer = AlgorithmManager::Instance().create("RenameWorkspace");
+  renamer->setLogging(false);
+  renamer->setProperty("InputWorkspace", name);
+  renamer->setProperty("OutputWorkspace", newName);
+  renamer->execute();
 }
 
-MatrixWorkspace_sptr extendResolutionWorkspace(MatrixWorkspace_sptr resolution,
-                                               std::size_t numberOfHistograms) {
+void deleteWorkspace(std::string const &workspaceName) {
+  auto deleter = AlgorithmManager::Instance().create("DeleteWorkspace");
+  deleter->setLogging(false);
+  deleter->setProperty("Workspace", workspaceName);
+  deleter->execute();
+}
+
+void extendResolutionWorkspace(MatrixWorkspace_sptr resolution,
+                               std::size_t numberOfHistograms,
+                               std::string const &outputName) {
   const auto resolutionNumHist = resolution->getNumberHistograms();
   if (resolutionNumHist != 1 && resolutionNumHist != numberOfHistograms) {
     std::string msg(
@@ -208,13 +224,15 @@ MatrixWorkspace_sptr extendResolutionWorkspace(MatrixWorkspace_sptr resolution,
     throw std::runtime_error(msg);
   }
 
-  auto resolutionWS = cloneWorkspace(resolution);
+  auto resolutionWS = cloneWorkspace(resolution, "__cloned");
 
   // Append to cloned workspace if necessary
-  if (resolutionNumHist == 1 && numberOfHistograms > 1)
-    return appendWorkspace(resolutionWS, resolution,
-                           static_cast<int>(numberOfHistograms - 1));
-  return resolutionWS;
+  if (resolutionNumHist == 1 && numberOfHistograms > 1) {
+    appendWorkspace(resolutionWS, resolution,
+                    static_cast<int>(numberOfHistograms - 1), outputName);
+    deleteWorkspace("__cloned");
+  } else
+    renameWorkspace("__cloned", outputName);
 }
 
 void getParameterNameChanges(
@@ -535,8 +553,7 @@ void ConvFitModel::removeWorkspace(std::size_t index) {
 }
 
 void ConvFitModel::setResolution(const std::string &name, std::size_t index) {
-  setResolution(
-      AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(name), index);
+  setResolution(getADSMatrixWorkspace(name), index);
 }
 
 void ConvFitModel::setResolution(MatrixWorkspace_sptr resolution,
@@ -555,9 +572,9 @@ void ConvFitModel::setResolution(MatrixWorkspace_sptr resolution,
 
 void ConvFitModel::addExtendedResolution(std::size_t index) {
   const std::string name = "__ConvFitResolution" + std::to_string(index);
-  AnalysisDataService::Instance().addOrReplace(
-      name, extendResolutionWorkspace(m_resolution[index].lock(),
-                                      getNumberHistograms(index)));
+
+  extendResolutionWorkspace(m_resolution[index].lock(),
+                            getNumberHistograms(index), name);
 
   if (m_extendedResolution.size() > index)
     m_extendedResolution[index] = name;
diff --git a/qt/scientific_interfaces/Indirect/IIndirectFitPlotView.h b/qt/scientific_interfaces/Indirect/IIndirectFitPlotView.h
new file mode 100644
index 0000000000000000000000000000000000000000..06c0ed9e70b17100cab7830098f8a8d56251b8c1
--- /dev/null
+++ b/qt/scientific_interfaces/Indirect/IIndirectFitPlotView.h
@@ -0,0 +1,103 @@
+// Mantid Repository : https://github.com/mantidproject/mantid
+//
+// Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
+//     NScD Oak Ridge National Laboratory, European Spallation Source
+//     & Institut Laue - Langevin
+// SPDX - License - Identifier: GPL - 3.0 +
+#ifndef MANTIDQTCUSTOMINTERFACESIDA_IINDIRECTFITPLOTVIEW_H_
+#define MANTIDQTCUSTOMINTERFACESIDA_IINDIRECTFITPLOTVIEW_H_
+
+#include "DllConfig.h"
+#include "MantidAPI/MatrixWorkspace.h"
+#include "MantidQtWidgets/Common/MantidWidget.h"
+
+#include <QObject>
+
+namespace MantidQt {
+namespace CustomInterfaces {
+namespace IDA {
+
+class MANTIDQT_INDIRECT_DLL IIndirectFitPlotView : public API::MantidWidget {
+  Q_OBJECT
+
+public:
+  IIndirectFitPlotView(QWidget *parent = nullptr) : API::MantidWidget(parent){};
+  virtual ~IIndirectFitPlotView(){};
+
+  virtual std::size_t getSelectedSpectrum() const = 0;
+  virtual int getSelectedSpectrumIndex() const = 0;
+  virtual int getSelectedDataIndex() const = 0;
+  virtual std::size_t dataSelectionSize() const = 0;
+  virtual bool isPlotGuessChecked() const = 0;
+
+  virtual void hideMultipleDataSelection() = 0;
+  virtual void showMultipleDataSelection() = 0;
+
+  virtual void setAvailableSpectra(std::size_t minimum,
+                                   std::size_t maximum) = 0;
+  virtual void
+  setAvailableSpectra(const std::vector<std::size_t>::const_iterator &from,
+                      const std::vector<std::size_t>::const_iterator &to) = 0;
+
+  virtual void setMinimumSpectrum(int minimum) = 0;
+  virtual void setMaximumSpectrum(int maximum) = 0;
+  virtual void setPlotSpectrum(int spectrum) = 0;
+  virtual void appendToDataSelection(const std::string &dataName) = 0;
+  virtual void setNameInDataSelection(const std::string &dataName,
+                                      std::size_t index) = 0;
+  virtual void clearDataSelection() = 0;
+
+  virtual void plotInTopPreview(const QString &name,
+                                Mantid::API::MatrixWorkspace_sptr workspace,
+                                std::size_t spectrum,
+                                Qt::GlobalColor colour) = 0;
+  virtual void plotInBottomPreview(const QString &name,
+                                   Mantid::API::MatrixWorkspace_sptr workspace,
+                                   std::size_t spectrum,
+                                   Qt::GlobalColor colour) = 0;
+
+  virtual void removeFromTopPreview(const QString &name) = 0;
+  virtual void removeFromBottomPreview(const QString &name) = 0;
+
+  virtual void enableFitSingleSpectrum(bool enable) = 0;
+  virtual void enablePlotGuess(bool enable) = 0;
+  virtual void enableSpectrumSelection(bool enable) = 0;
+  virtual void enableFitRangeSelection(bool enable) = 0;
+
+  virtual void setBackgroundLevel(double value) = 0;
+
+  virtual void setFitRange(double minimum, double maximum) = 0;
+  virtual void setFitRangeMinimum(double minimum) = 0;
+  virtual void setFitRangeMaximum(double maximum) = 0;
+
+  virtual void setBackgroundRangeVisible(bool visible) = 0;
+  virtual void setHWHMRangeVisible(bool visible) = 0;
+
+  virtual void displayMessage(const std::string &message) const = 0;
+
+public slots:
+  virtual void clearTopPreview() = 0;
+  virtual void clearBottomPreview() = 0;
+  virtual void clear() = 0;
+  virtual void setHWHMRange(double minimum, double maximum) = 0;
+  virtual void setHWHMMaximum(double minimum) = 0;
+  virtual void setHWHMMinimum(double maximum) = 0;
+
+signals:
+  void selectedFitDataChanged(std::size_t);
+  void plotCurrentPreview();
+  void plotSpectrumChanged(std::size_t);
+  void plotGuessChanged(bool);
+  void fitSelectedSpectrum();
+  void startXChanged(double);
+  void endXChanged(double);
+  void hwhmMinimumChanged(double);
+  void hwhmMaximumChanged(double);
+  void hwhmChanged(double, double);
+  void backgroundChanged(double);
+};
+} // namespace IDA
+} // namespace CustomInterfaces
+} // namespace MantidQt
+
+#endif
\ No newline at end of file
diff --git a/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.cpp b/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.cpp
index acd7e0644c72ac6d48d3620587910e660dcb9041..5ee5ceed39d37b3099e9b939d9a3e52a51d8dc92 100644
--- a/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.cpp
+++ b/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.cpp
@@ -264,7 +264,7 @@ void IndirectFitAnalysisTab::setFitDataPresenter(
   m_dataPresenter = std::move(presenter);
 }
 
-void IndirectFitAnalysisTab::setPlotView(IndirectFitPlotView *view) {
+void IndirectFitAnalysisTab::setPlotView(IIndirectFitPlotView *view) {
   m_plotPresenter = Mantid::Kernel::make_unique<IndirectFitPlotPresenter>(
       m_fittingModel.get(), view);
 }
diff --git a/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.h b/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.h
index 0c3d71b7d6259e2d6fc56091dfb0f80a34efb631..11ea2ce31dfbea9268892eced68d85647622bbf9 100644
--- a/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.h
+++ b/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.h
@@ -36,7 +36,7 @@ public:
                          QWidget *parent = nullptr);
 
   void setFitDataPresenter(std::unique_ptr<IndirectFitDataPresenter> presenter);
-  void setPlotView(IndirectFitPlotView *view);
+  void setPlotView(IIndirectFitPlotView *view);
   void setSpectrumSelectionView(IndirectSpectrumSelectionView *view);
   void
   setFitPropertyBrowser(MantidWidgets::IndirectFitPropertyBrowser *browser);
diff --git a/qt/scientific_interfaces/Indirect/IndirectFitOutput.cpp b/qt/scientific_interfaces/Indirect/IndirectFitOutput.cpp
index e5e1909e15c11d95d7718017c7783399d9ef9ce0..b951e21d383883748b16ec59805aa6fda0301adf 100644
--- a/qt/scientific_interfaces/Indirect/IndirectFitOutput.cpp
+++ b/qt/scientific_interfaces/Indirect/IndirectFitOutput.cpp
@@ -6,6 +6,7 @@
 // SPDX - License - Identifier: GPL - 3.0 +
 #include "IndirectFitOutput.h"
 
+#include "MantidAPI/AlgorithmManager.h"
 #include "MantidAPI/AnalysisDataService.h"
 #include "MantidAPI/TableRow.h"
 #include "MantidAPI/TextAxis.h"
@@ -135,17 +136,23 @@ std::vector<std::string> getAxisLabels(MatrixWorkspace_sptr workspace,
   return std::vector<std::string>();
 }
 
+void renameWorkspace(std::string const &name, std::string const &newName) {
+  auto renamer = AlgorithmManager::Instance().create("RenameWorkspace");
+  renamer->setProperty("InputWorkspace", name);
+  renamer->setProperty("OutputWorkspace", newName);
+  renamer->execute();
+}
+
 void renameResult(Workspace_sptr resultWorkspace,
                   const std::string &workspaceName) {
-  AnalysisDataService::Instance().rename(resultWorkspace->getName(),
-                                         workspaceName + "_Result");
+  renameWorkspace(resultWorkspace->getName(), workspaceName + "_Result");
 }
 
 void renameResult(Workspace_sptr resultWorkspace,
                   IndirectFitData const *fitData) {
   const auto name = resultWorkspace->getName();
   const auto newName = fitData->displayName("%1%_s%2%_Result", "_to_");
-  AnalysisDataService::Instance().rename(name, newName);
+  renameWorkspace(name, newName);
 }
 
 void renameResultWithoutSpectra(WorkspaceGroup_sptr resultWorkspace,
diff --git a/qt/scientific_interfaces/Indirect/IndirectFitPlotModel.cpp b/qt/scientific_interfaces/Indirect/IndirectFitPlotModel.cpp
index 634c668f12269b0bd42eec48997c7fffe22e2e0a..e2773c4d051b7a9f6eab09f83c9e59b66eed4aef 100644
--- a/qt/scientific_interfaces/Indirect/IndirectFitPlotModel.cpp
+++ b/qt/scientific_interfaces/Indirect/IndirectFitPlotModel.cpp
@@ -194,8 +194,9 @@ std::string IndirectFitPlotModel::getFitDataName() const {
 }
 
 std::string IndirectFitPlotModel::getLastFitDataName() const {
-  if (m_fittingModel->numberOfWorkspaces())
-    return getFitDataName(m_fittingModel->numberOfWorkspaces() - 1);
+  auto const numberOfWorkspaces = m_fittingModel->numberOfWorkspaces();
+  if (numberOfWorkspaces > 0)
+    return getFitDataName(numberOfWorkspaces - 1);
   return "";
 }
 
diff --git a/qt/scientific_interfaces/Indirect/IndirectFitPlotPresenter.cpp b/qt/scientific_interfaces/Indirect/IndirectFitPlotPresenter.cpp
index 2593a40942d8d5a14e10cdd57661546dfb113a55..4831660c1c3cc193b1180e6b285744bd5f372203 100644
--- a/qt/scientific_interfaces/Indirect/IndirectFitPlotPresenter.cpp
+++ b/qt/scientific_interfaces/Indirect/IndirectFitPlotPresenter.cpp
@@ -10,7 +10,7 @@
 
 namespace {
 using MantidQt::CustomInterfaces::IDA::DiscontinuousSpectra;
-using MantidQt::CustomInterfaces::IDA::IndirectFitPlotView;
+using MantidQt::CustomInterfaces::IDA::IIndirectFitPlotView;
 
 std::string createPlotString(const std::string &workspaceName,
                              const std::string &spectra) {
@@ -26,7 +26,7 @@ std::string createPlotString(const std::string &workspaceName,
 
 struct UpdateAvailableSpectra : public boost::static_visitor<> {
 public:
-  explicit UpdateAvailableSpectra(IndirectFitPlotView *view) : m_view(view) {}
+  explicit UpdateAvailableSpectra(IIndirectFitPlotView *view) : m_view(view) {}
 
   void operator()(const std::pair<std::size_t, std::size_t> &spectra) {
     m_view->setAvailableSpectra(spectra.first, spectra.second);
@@ -37,7 +37,7 @@ public:
   }
 
 private:
-  IndirectFitPlotView *m_view;
+  IIndirectFitPlotView *m_view;
 };
 } // namespace
 
@@ -48,7 +48,7 @@ namespace IDA {
 using namespace Mantid::API;
 
 IndirectFitPlotPresenter::IndirectFitPlotPresenter(IndirectFittingModel *model,
-                                                   IndirectFitPlotView *view)
+                                                   IIndirectFitPlotView *view)
     : m_model(new IndirectFitPlotModel(model)), m_view(view),
       m_plotGuessInSeparateWindow(false) {
   connect(m_view, SIGNAL(selectedFitDataChanged(std::size_t)), this,
diff --git a/qt/scientific_interfaces/Indirect/IndirectFitPlotPresenter.h b/qt/scientific_interfaces/Indirect/IndirectFitPlotPresenter.h
index 931060398b72a392c22165d22415da1d1754edde..85a6440fc2f5031d05f3646db3f61d4af8ae008c 100644
--- a/qt/scientific_interfaces/Indirect/IndirectFitPlotPresenter.h
+++ b/qt/scientific_interfaces/Indirect/IndirectFitPlotPresenter.h
@@ -7,20 +7,22 @@
 #ifndef MANTIDQTCUSTOMINTERFACESIDA_INDIRECTFITPLOTPRESENTER_H_
 #define MANTIDQTCUSTOMINTERFACESIDA_INDIRECTFITPLOTPRESENTER_H_
 
+#include "DllConfig.h"
+
 #include "IndirectFitPlotModel.h"
 
-#include "IndirectFitPlotView.h"
+#include "IIndirectFitPlotView.h"
 #include "LazyAsyncRunner.h"
 
 namespace MantidQt {
 namespace CustomInterfaces {
 namespace IDA {
 
-class DLLExport IndirectFitPlotPresenter : public QObject {
+class MANTIDQT_INDIRECT_DLL IndirectFitPlotPresenter : public QObject {
   Q_OBJECT
 public:
   IndirectFitPlotPresenter(IndirectFittingModel *model,
-                           IndirectFitPlotView *view);
+                           IIndirectFitPlotView *view);
 
   std::size_t getSelectedDataIndex() const;
   std::size_t getSelectedSpectrum() const;
@@ -96,7 +98,7 @@ private:
   std::string getPlotString(std::size_t spectrum) const;
 
   std::unique_ptr<IndirectFitPlotModel> m_model;
-  IndirectFitPlotView *m_view;
+  IIndirectFitPlotView *m_view;
 
   bool m_plotGuessInSeparateWindow;
   MantidQt::API::PythonRunner m_pythonRunner;
diff --git a/qt/scientific_interfaces/Indirect/IndirectFitPlotView.cpp b/qt/scientific_interfaces/Indirect/IndirectFitPlotView.cpp
index d7ca039c2e60b4d4abf8ca505d6adbe6753a0a76..c8fc513b388d6f25d24066a7a94704b13dd714e2 100644
--- a/qt/scientific_interfaces/Indirect/IndirectFitPlotView.cpp
+++ b/qt/scientific_interfaces/Indirect/IndirectFitPlotView.cpp
@@ -17,7 +17,7 @@ namespace CustomInterfaces {
 namespace IDA {
 
 IndirectFitPlotView::IndirectFitPlotView(QWidget *parent)
-    : API::MantidWidget(parent), m_plotForm(new Ui::IndirectFitPreviewPlot) {
+    : IIndirectFitPlotView(parent), m_plotForm(new Ui::IndirectFitPreviewPlot) {
   m_plotForm->setupUi(this);
 
   connect(m_plotForm->cbDataSelection, SIGNAL(currentIndexChanged(int)), this,
diff --git a/qt/scientific_interfaces/Indirect/IndirectFitPlotView.h b/qt/scientific_interfaces/Indirect/IndirectFitPlotView.h
index 3e4311c4a07c9b35b4829671cf39e1591cf21047..ec73575f7927837563f71c941c11c98fe54634fa 100644
--- a/qt/scientific_interfaces/Indirect/IndirectFitPlotView.h
+++ b/qt/scientific_interfaces/Indirect/IndirectFitPlotView.h
@@ -9,86 +9,79 @@
 
 #include "ui_IndirectFitPreviewPlot.h"
 
+#include "DllConfig.h"
 #include "MantidAPI/MatrixWorkspace.h"
 
+#include "IIndirectFitPlotView.h"
+
 #include "MantidQtWidgets/Common/MantidWidget.h"
 
 namespace MantidQt {
 namespace CustomInterfaces {
 namespace IDA {
 
-class DLLExport IndirectFitPlotView : public API::MantidWidget {
+class MANTIDQT_INDIRECT_DLL IndirectFitPlotView : public IIndirectFitPlotView {
   Q_OBJECT
 public:
-  IndirectFitPlotView(QWidget *parent);
-  ~IndirectFitPlotView() override;
-
-  std::size_t getSelectedSpectrum() const;
-  int getSelectedSpectrumIndex() const;
-  int getSelectedDataIndex() const;
-  std::size_t dataSelectionSize() const;
-  bool isPlotGuessChecked() const;
-
-  void hideMultipleDataSelection();
-  void showMultipleDataSelection();
-
-  void setAvailableSpectra(std::size_t minimum, std::size_t maximum);
-  void setAvailableSpectra(const std::vector<std::size_t>::const_iterator &from,
-                           const std::vector<std::size_t>::const_iterator &to);
-
-  void setMinimumSpectrum(int minimum);
-  void setMaximumSpectrum(int maximum);
-  void setPlotSpectrum(int spectrum);
-  void appendToDataSelection(const std::string &dataName);
-  void setNameInDataSelection(const std::string &dataName, std::size_t index);
-  void clearDataSelection();
+  IndirectFitPlotView(QWidget *parent = nullptr);
+  virtual ~IndirectFitPlotView() override;
+
+  std::size_t getSelectedSpectrum() const override;
+  int getSelectedSpectrumIndex() const override;
+  int getSelectedDataIndex() const override;
+  std::size_t dataSelectionSize() const override;
+  bool isPlotGuessChecked() const override;
+
+  void hideMultipleDataSelection() override;
+  void showMultipleDataSelection() override;
+
+  void setAvailableSpectra(std::size_t minimum, std::size_t maximum) override;
+  void setAvailableSpectra(
+      const std::vector<std::size_t>::const_iterator &from,
+      const std::vector<std::size_t>::const_iterator &to) override;
+
+  void setMinimumSpectrum(int minimum) override;
+  void setMaximumSpectrum(int maximum) override;
+  void setPlotSpectrum(int spectrum) override;
+  void appendToDataSelection(const std::string &dataName) override;
+  void setNameInDataSelection(const std::string &dataName,
+                              std::size_t index) override;
+  void clearDataSelection() override;
 
   void plotInTopPreview(const QString &name,
                         Mantid::API::MatrixWorkspace_sptr workspace,
-                        std::size_t spectrum, Qt::GlobalColor colour);
+                        std::size_t spectrum, Qt::GlobalColor colour) override;
   void plotInBottomPreview(const QString &name,
                            Mantid::API::MatrixWorkspace_sptr workspace,
-                           std::size_t spectrum, Qt::GlobalColor colour);
+                           std::size_t spectrum,
+                           Qt::GlobalColor colour) override;
 
-  void removeFromTopPreview(const QString &name);
-  void removeFromBottomPreview(const QString &name);
+  void removeFromTopPreview(const QString &name) override;
+  void removeFromBottomPreview(const QString &name) override;
 
-  void enableFitSingleSpectrum(bool enable);
-  void enablePlotGuess(bool enable);
-  void enableSpectrumSelection(bool enable);
-  void enableFitRangeSelection(bool enable);
+  void enableFitSingleSpectrum(bool enable) override;
+  void enablePlotGuess(bool enable) override;
+  void enableSpectrumSelection(bool enable) override;
+  void enableFitRangeSelection(bool enable) override;
 
-  void setBackgroundLevel(double value);
+  void setBackgroundLevel(double value) override;
 
-  void setFitRange(double minimum, double maximum);
-  void setFitRangeMinimum(double minimum);
-  void setFitRangeMaximum(double maximum);
+  void setFitRange(double minimum, double maximum) override;
+  void setFitRangeMinimum(double minimum) override;
+  void setFitRangeMaximum(double maximum) override;
 
-  void setBackgroundRangeVisible(bool visible);
-  void setHWHMRangeVisible(bool visible);
+  void setBackgroundRangeVisible(bool visible) override;
+  void setHWHMRangeVisible(bool visible) override;
 
-  void displayMessage(const std::string &message) const;
+  void displayMessage(const std::string &message) const override;
 
 public slots:
-  void clearTopPreview();
-  void clearBottomPreview();
-  void clear();
-  void setHWHMRange(double minimum, double maximum);
-  void setHWHMMaximum(double minimum);
-  void setHWHMMinimum(double maximum);
-
-signals:
-  void selectedFitDataChanged(std::size_t);
-  void plotCurrentPreview();
-  void plotSpectrumChanged(std::size_t);
-  void plotGuessChanged(bool);
-  void fitSelectedSpectrum();
-  void startXChanged(double);
-  void endXChanged(double);
-  void hwhmMinimumChanged(double);
-  void hwhmMaximumChanged(double);
-  void hwhmChanged(double, double);
-  void backgroundChanged(double);
+  void clearTopPreview() override;
+  void clearBottomPreview() override;
+  void clear() override;
+  void setHWHMRange(double minimum, double maximum) override;
+  void setHWHMMaximum(double minimum) override;
+  void setHWHMMinimum(double maximum) override;
 
 private slots:
   void emitPlotSpectrumChanged(int);
diff --git a/qt/scientific_interfaces/Indirect/IndirectFittingModel.h b/qt/scientific_interfaces/Indirect/IndirectFittingModel.h
index c9879f49bf2f11bf1e79d12cf0546fc1902a9470..f0c9f168409215fc61e7b8e684834e7a6ac495c5 100644
--- a/qt/scientific_interfaces/Indirect/IndirectFittingModel.h
+++ b/qt/scientific_interfaces/Indirect/IndirectFittingModel.h
@@ -47,15 +47,16 @@ public:
   IndirectFittingModel();
   virtual ~IndirectFittingModel() = default;
 
-  Mantid::API::MatrixWorkspace_sptr getWorkspace(std::size_t index) const;
+  virtual Mantid::API::MatrixWorkspace_sptr
+  getWorkspace(std::size_t index) const;
   Spectra getSpectra(std::size_t index) const;
-  std::pair<double, double> getFittingRange(std::size_t dataIndex,
-                                            std::size_t spectrum) const;
+  virtual std::pair<double, double> getFittingRange(std::size_t dataIndex,
+                                                    std::size_t spectrum) const;
   virtual std::string getExcludeRegion(std::size_t dataIndex,
                                        std::size_t index) const;
-  std::string createDisplayName(const std::string &formatString,
-                                const std::string &rangeDelimiter,
-                                std::size_t dataIndex) const;
+  virtual std::string createDisplayName(const std::string &formatString,
+                                        const std::string &rangeDelimiter,
+                                        std::size_t dataIndex) const;
   std::string createOutputName(const std::string &formatString,
                                const std::string &rangeDelimiter,
                                std::size_t dataIndex) const;
@@ -63,7 +64,7 @@ public:
   bool isPreviouslyFit(std::size_t dataIndex, std::size_t spectrum) const;
   bool hasZeroSpectra(std::size_t dataIndex) const;
   virtual boost::optional<std::string> isInvalidFunction() const;
-  std::size_t numberOfWorkspaces() const;
+  virtual std::size_t numberOfWorkspaces() const;
   std::size_t getNumberOfSpectra(std::size_t index) const;
   std::vector<std::string> getFitParameterNames() const;
   virtual Mantid::API::IFunction_sptr getFittingFunction() const;
@@ -74,8 +75,10 @@ public:
   void setSpectra(const std::string &spectra, std::size_t dataIndex);
   void setSpectra(Spectra &&spectra, std::size_t dataIndex);
   void setSpectra(const Spectra &spectra, std::size_t dataIndex);
-  void setStartX(double startX, std::size_t dataIndex, std::size_t spectrum);
-  void setEndX(double endX, std::size_t dataIndex, std::size_t spectrum);
+  virtual void setStartX(double startX, std::size_t dataIndex,
+                         std::size_t spectrum);
+  virtual void setEndX(double endX, std::size_t dataIndex,
+                       std::size_t spectrum);
   void setExcludeRegion(const std::string &exclude, std::size_t dataIndex,
                         std::size_t spectrum);
 
@@ -89,8 +92,8 @@ public:
   PrivateFittingData clearWorkspaces();
   void setFittingMode(FittingMode mode);
   virtual void setFitFunction(Mantid::API::IFunction_sptr function);
-  void setDefaultParameterValue(const std::string &name, double value,
-                                std::size_t dataIndex);
+  virtual void setDefaultParameterValue(const std::string &name, double value,
+                                        std::size_t dataIndex);
   void addSingleFitOutput(Mantid::API::IAlgorithm_sptr fitAlgorithm,
                           std::size_t index);
   virtual void addOutput(Mantid::API::IAlgorithm_sptr fitAlgorithm);
diff --git a/qt/scientific_interfaces/Indirect/IndirectSqw.cpp b/qt/scientific_interfaces/Indirect/IndirectSqw.cpp
index e91e3d798ab349244184833d6ccb210092cbb2b9..1c4c0fdd54181bc4f3dd6793dbc25de7d7c94c31 100644
--- a/qt/scientific_interfaces/Indirect/IndirectSqw.cpp
+++ b/qt/scientific_interfaces/Indirect/IndirectSqw.cpp
@@ -218,8 +218,12 @@ void IndirectSqw::plotSpectrumClicked() {
 void IndirectSqw::plotContourClicked() {
   setPlotContourIsPlotting(true);
 
-  if (checkADSForPlotSaveWorkspace(m_pythonExportWsName, true))
-    plot2D(QString::fromStdString(m_pythonExportWsName));
+  if (checkADSForPlotSaveWorkspace(m_pythonExportWsName, true)) {
+    QString pyInput = "from mantidplot import plot2D\nimportMatrixWorkspace('" +
+                      QString::fromStdString(m_pythonExportWsName) +
+                      "').plotGraph2D()\n";
+    m_pythonRunner.runPythonCode(pyInput);
+  }
 
   setPlotContourIsPlotting(false);
 }
diff --git a/qt/scientific_interfaces/Indirect/Iqt.cpp b/qt/scientific_interfaces/Indirect/Iqt.cpp
index d57e191d1de65b027ed22de559f6490d78d3f258..e181b723d6bf133f3abd68798e24ba9ac3ceb113 100644
--- a/qt/scientific_interfaces/Indirect/Iqt.cpp
+++ b/qt/scientific_interfaces/Indirect/Iqt.cpp
@@ -8,6 +8,7 @@
 #include "../General/UserInputValidator.h"
 
 #include "MantidAPI/ITableWorkspace.h"
+#include "MantidAPI/MatrixWorkspace.h"
 #include "MantidGeometry/Instrument.h"
 #include "MantidQtWidgets/Common/SignalBlocker.h"
 #include "MantidQtWidgets/LegacyQwt/RangeSelector.h"
@@ -16,9 +17,43 @@
 
 #include <tuple>
 
+using namespace Mantid::API;
+
 namespace {
 Mantid::Kernel::Logger g_log("Iqt");
 
+MatrixWorkspace_sptr getADSWorkspace(std::string const &workspaceName) {
+  return AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(
+      workspaceName);
+}
+
+std::size_t getWsNumberOfSpectra(std::string const &workspaceName) {
+  return getADSWorkspace(workspaceName)->getNumberHistograms();
+}
+
+bool checkADSForWorkspace(std::string const &workspaceName) {
+  return AnalysisDataService::Instance().doesExist(workspaceName);
+}
+
+void cloneWorkspace(std::string const &workspaceName,
+                    std::string const &cloneName) {
+  auto cloner = AlgorithmManager::Instance().create("CloneWorkspace");
+  cloner->initialize();
+  cloner->setProperty("InputWorkspace", workspaceName);
+  cloner->setProperty("OutputWorkspace", cloneName);
+  cloner->execute();
+}
+
+void cropWorkspace(std::string const &name, std::string const &newName,
+                   double const &cropValue) {
+  auto croper = AlgorithmManager::Instance().create("CropWorkspace");
+  croper->initialize();
+  croper->setProperty("InputWorkspace", name);
+  croper->setProperty("OutputWorkspace", newName);
+  croper->setProperty("XMin", cropValue);
+  croper->execute();
+}
+
 /**
  * Calculate the number of bins in the sample & resolution workspaces
  * @param wsName The sample workspace name
@@ -68,8 +103,6 @@ calculateBinParameters(QString wsName, QString resName, double energyMin,
 }
 } // namespace
 
-using namespace Mantid::API;
-
 namespace MantidQt {
 namespace CustomInterfaces {
 namespace IDA {
@@ -145,28 +178,25 @@ void Iqt::setup() {
 }
 
 void Iqt::run() {
-  using namespace Mantid::API;
-
   setRunIsRunning(true);
 
   updateDisplayedBinParameters();
 
   // Construct the result workspace for Python script export
-  QString sampleName = m_uiForm.dsInput->getCurrentDataName();
+  QString const sampleName = m_uiForm.dsInput->getCurrentDataName();
   m_pythonExportWsName =
       sampleName.left(sampleName.lastIndexOf("_")).toStdString() + "_iqt";
 
-  QString wsName = m_uiForm.dsInput->getCurrentDataName();
-  QString resName = m_uiForm.dsResolution->getCurrentDataName();
-  QString nIterations = m_uiForm.spIterations->cleanText();
-  bool calculateErrors = m_uiForm.cbCalculateErrors->isChecked();
+  QString const wsName = m_uiForm.dsInput->getCurrentDataName();
+  QString const resName = m_uiForm.dsResolution->getCurrentDataName();
+  QString const nIterations = m_uiForm.spIterations->cleanText();
+  bool const calculateErrors = m_uiForm.cbCalculateErrors->isChecked();
 
-  double energyMin = m_dblManager->value(m_properties["ELow"]);
-  double energyMax = m_dblManager->value(m_properties["EHigh"]);
-  double numBins = m_dblManager->value(m_properties["SampleBinning"]);
+  double const energyMin = m_dblManager->value(m_properties["ELow"]);
+  double const energyMax = m_dblManager->value(m_properties["EHigh"]);
+  double const numBins = m_dblManager->value(m_properties["SampleBinning"]);
 
-  IAlgorithm_sptr IqtAlg =
-      AlgorithmManager::Instance().create("TransformToIqt");
+  auto IqtAlg = AlgorithmManager::Instance().create("TransformToIqt");
   IqtAlg->initialize();
 
   IqtAlg->setProperty("SampleWorkspace", wsName.toStdString());
@@ -185,14 +215,6 @@ void Iqt::run() {
   m_batchAlgoRunner->executeBatchAsync();
 }
 
-MatrixWorkspace_const_sptr Iqt::getADSWorkspace(std::string const &name) const {
-  return AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(name);
-}
-
-std::size_t Iqt::getOutWsNumberOfSpectra() const {
-  return getADSWorkspace(m_pythonExportWsName)->getNumberHistograms();
-}
-
 /**
  * Handle algorithm completion.
  *
@@ -205,10 +227,16 @@ void Iqt::algorithmComplete(bool error) {
     setTiledPlotEnabled(false);
     setSaveResultEnabled(false);
   } else {
-    setPlotSpectrumIndexMax(static_cast<int>(getOutWsNumberOfSpectra()) - 1);
-    setPlotSpectrumIndex(selectedSpectrum());
-    setTiledPlotFirstIndex(selectedSpectrum());
-    setTiledPlotLastIndex(static_cast<int>(getOutWsNumberOfSpectra()) - 1);
+    auto const lastSpectrumIndex =
+        static_cast<int>(getWsNumberOfSpectra(m_pythonExportWsName)) - 1;
+    auto const selectedSpec = selectedSpectrum();
+
+    setPlotSpectrumIndexMax(lastSpectrumIndex);
+    setPlotSpectrumIndex(selectedSpec);
+    setMinMaxOfTiledPlotFirstIndex(0, lastSpectrumIndex);
+    setMinMaxOfTiledPlotLastIndex(0, lastSpectrumIndex);
+    setTiledPlotFirstIndex(selectedSpec);
+    setTiledPlotLastIndex(lastSpectrumIndex);
   }
 } // namespace IDA
 /**
@@ -270,26 +298,14 @@ void Iqt::plotTiled() {
   auto const firstTiledPlot = m_uiForm.spTiledPlotFirst->text().toInt();
   auto const lastTiledPlot = m_uiForm.spTiledPlotLast->text().toInt();
 
+  // Clone workspace before cropping to keep in ADS
+  if (!checkADSForWorkspace(tiledPlotWsName))
+    cloneWorkspace(outWs->getName(), tiledPlotWsName);
+
   // Get first x value which corresponds to a y value below 1
   auto const cropValue =
       getXMinValue(outWs, static_cast<std::size_t>(firstTiledPlot));
-
-  // Clone workspace before cropping to keep in ADS
-  IAlgorithm_sptr clone = AlgorithmManager::Instance().create("CloneWorkspace");
-  clone->initialize();
-  clone->setProperty("InputWorkspace", outWs->getName());
-  clone->setProperty("OutputWorkspace", tiledPlotWsName);
-  clone->execute();
-
-  // Crop based on selected first and last spectra
-  IAlgorithm_sptr crop = AlgorithmManager::Instance().create("CropWorkspace");
-  crop->initialize();
-  crop->setProperty("InputWorkspace", tiledPlotWsName);
-  crop->setProperty("OutputWorkspace", tiledPlotWsName);
-  crop->setProperty("StartWorkspaceIndex", firstTiledPlot);
-  crop->setProperty("EndWorkspaceIndex", lastTiledPlot);
-  crop->setProperty("XMin", cropValue);
-  crop->execute();
+  cropWorkspace(tiledPlotWsName, tiledPlotWsName, cropValue);
 
   auto const tiledPlotWs = getADSWorkspace(tiledPlotWsName);
 
@@ -502,24 +518,31 @@ void Iqt::updateRS(QtProperty *prop, double val) {
 
 void Iqt::setTiledPlotFirstPlot(int value) {
   MantidQt::API::SignalBlocker<QObject> blocker(m_uiForm.spTiledPlotFirst);
-  auto lastPlotIndex = m_uiForm.spTiledPlotLast->text().toInt();
-  auto firstPlotMinimum = lastPlotIndex - m_maxTiledPlots >= 0
-                              ? lastPlotIndex - m_maxTiledPlots
-                              : 0;
-  setMinMaxOfTiledPlotFirstIndex(firstPlotMinimum, lastPlotIndex);
-  if (lastPlotIndex - value <= m_maxTiledPlots)
-    setMinMaxOfTiledPlotLastIndex(value, value + m_maxTiledPlots);
-  setTiledPlotFirstIndex(value);
+  auto const lastPlotIndex = m_uiForm.spTiledPlotLast->text().toInt();
+  auto const rangeSize = lastPlotIndex - value;
+  if (value > lastPlotIndex)
+    setTiledPlotFirstIndex(lastPlotIndex);
+  else if (rangeSize > m_maxTiledPlots) {
+    auto const lastSpectrumIndex =
+        static_cast<int>(getWsNumberOfSpectra(m_pythonExportWsName)) - 1;
+    auto const lastIndex = value + m_maxTiledPlots <= lastSpectrumIndex
+                               ? value + m_maxTiledPlots
+                               : lastSpectrumIndex;
+    setTiledPlotLastIndex(lastIndex);
+  }
 }
 
 void Iqt::setTiledPlotLastPlot(int value) {
   MantidQt::API::SignalBlocker<QObject> blocker(m_uiForm.spTiledPlotLast);
-  auto firstPlotIndex = m_uiForm.spTiledPlotFirst->text().toInt();
-  setMinMaxOfTiledPlotLastIndex(firstPlotIndex,
-                                firstPlotIndex + m_maxTiledPlots);
-  if (value - firstPlotIndex <= m_maxTiledPlots && value - m_maxTiledPlots >= 0)
-    setMinMaxOfTiledPlotFirstIndex(value - m_maxTiledPlots, value);
-  setTiledPlotLastIndex(value);
+  auto const firstPlotIndex = m_uiForm.spTiledPlotFirst->text().toInt();
+  auto const rangeSize = value - firstPlotIndex;
+  if (value < firstPlotIndex)
+    setTiledPlotLastIndex(firstPlotIndex);
+  else if (rangeSize > m_maxTiledPlots) {
+    auto const firstIndex =
+        value - m_maxTiledPlots >= 0 ? value - m_maxTiledPlots : 0;
+    setTiledPlotFirstIndex(firstIndex);
+  }
 }
 
 void Iqt::setMinMaxOfTiledPlotFirstIndex(int minimum, int maximum) {
@@ -539,10 +562,10 @@ void Iqt::setTiledPlotFirstIndex(int value) {
 
 void Iqt::setTiledPlotLastIndex(int value) {
   MantidQt::API::SignalBlocker<QObject> blocker(m_uiForm.spTiledPlotLast);
-  auto firstPlotIndex = m_uiForm.spTiledPlotFirst->text().toInt();
-  auto lastPlotIndex = value - m_maxTiledPlots > firstPlotIndex
-                           ? firstPlotIndex + m_maxTiledPlots
-                           : value;
+  auto const firstPlotIndex = m_uiForm.spTiledPlotFirst->text().toInt();
+  auto const lastPlotIndex = value - m_maxTiledPlots > firstPlotIndex
+                                 ? firstPlotIndex + m_maxTiledPlots
+                                 : value;
   m_uiForm.spTiledPlotLast->setValue(lastPlotIndex);
 }
 
diff --git a/qt/scientific_interfaces/Indirect/Iqt.h b/qt/scientific_interfaces/Indirect/Iqt.h
index 778683061cb9afec780303e7706644f5974b3816..3db642987319d4fa690e8d068e21ae9c40d5877b 100644
--- a/qt/scientific_interfaces/Indirect/Iqt.h
+++ b/qt/scientific_interfaces/Indirect/Iqt.h
@@ -27,9 +27,6 @@ private:
 
   bool isErrorsEnabled();
 
-  Mantid::API::MatrixWorkspace_const_sptr
-  getADSWorkspace(std::string const &name) const;
-  std::size_t getOutWsNumberOfSpectra() const;
   std::size_t getXMinIndex(Mantid::MantidVec const &firstSpectraYData,
                            std::vector<double>::const_iterator iter);
   double getXMinValue(Mantid::API::MatrixWorkspace_const_sptr workspace,
diff --git a/qt/scientific_interfaces/Indirect/Iqt.ui b/qt/scientific_interfaces/Indirect/Iqt.ui
index 648954c8aea168db0adcf201c09ab0f877c24460..4c245b8f9908d6f7a2c1bae0644edea805302f68 100644
--- a/qt/scientific_interfaces/Indirect/Iqt.ui
+++ b/qt/scientific_interfaces/Indirect/Iqt.ui
@@ -400,7 +400,7 @@
          <item>
           <widget class="QLabel" name="label_3">
            <property name="text">
-            <string>Spectra (max 18):</string>
+            <string>Range (max 18):</string>
            </property>
           </widget>
          </item>
diff --git a/qt/scientific_interfaces/Indirect/JumpFitModel.cpp b/qt/scientific_interfaces/Indirect/JumpFitModel.cpp
index 22e3cba12161f4b333db7d437af880cfc0c41c7a..d55b93d8cd6d00b7db3044f9efd3483490f58a31 100644
--- a/qt/scientific_interfaces/Indirect/JumpFitModel.cpp
+++ b/qt/scientific_interfaces/Indirect/JumpFitModel.cpp
@@ -80,89 +80,101 @@ JumpFitParameters createJumpFitParameters(MatrixWorkspace *workspace) {
   return parameters;
 }
 
-MatrixWorkspace_sptr scaleWorkspace(MatrixWorkspace_sptr workspace,
-                                    double factor) {
+void deleteTemporaryWorkspaces(std::vector<std::string> const &workspaceNames) {
+  auto deleter = AlgorithmManager::Instance().create("DeleteWorkspace");
+  deleter->setLogging(false);
+  for (auto const &name : workspaceNames) {
+    deleter->setProperty("Workspace", name);
+    deleter->execute();
+  }
+}
+
+std::string scaleWorkspace(std::string const &inputName,
+                           std::string const &outputName, double factor) {
   auto scaleAlg = AlgorithmManager::Instance().create("Scale");
   scaleAlg->initialize();
   scaleAlg->setLogging(false);
-  scaleAlg->setChild(true);
-  scaleAlg->setProperty("InputWorkspace", workspace);
-  scaleAlg->setProperty("OutputWorkspace", "__scaled");
+  scaleAlg->setProperty("InputWorkspace", inputName);
+  scaleAlg->setProperty("OutputWorkspace", outputName);
   scaleAlg->setProperty("Factor", factor);
   scaleAlg->execute();
-  return scaleAlg->getProperty("OutputWorkspace");
+  return outputName;
 }
 
-MatrixWorkspace_sptr extractSpectra(MatrixWorkspace_sptr workspace,
-                                    int startIndex, int endIndex) {
+std::string extractSpectra(std::string const &inputName, int startIndex,
+                           int endIndex, std::string const &outputName) {
   auto extractAlg = AlgorithmManager::Instance().create("ExtractSpectra");
   extractAlg->initialize();
-  extractAlg->setChild(true);
   extractAlg->setLogging(false);
-  extractAlg->setProperty("InputWorkspace", workspace);
+  extractAlg->setProperty("InputWorkspace", inputName);
   extractAlg->setProperty("StartWorkspaceIndex", startIndex);
   extractAlg->setProperty("EndWorkspaceIndex", endIndex);
-  extractAlg->setProperty("OutputWorkspace", "__extracted");
+  extractAlg->setProperty("OutputWorkspace", outputName);
   extractAlg->execute();
-  return extractAlg->getProperty("OutputWorkspace");
+  return outputName;
 }
 
-MatrixWorkspace_sptr extractSpectrum(MatrixWorkspace_sptr workspace,
-                                     int index) {
-  return extractSpectra(workspace, index, index);
+std::string extractSpectrum(MatrixWorkspace_sptr workspace, int index,
+                            std::string const &outputName) {
+  return extractSpectra(workspace->getName(), index, index, outputName);
 }
 
-MatrixWorkspace_sptr extractHWHMSpectrum(MatrixWorkspace_sptr workspace,
-                                         int index) {
-  return scaleWorkspace(extractSpectrum(workspace, index), 0.5);
+std::string extractHWHMSpectrum(MatrixWorkspace_sptr workspace, int index) {
+  auto const scaledName = "__scaled_" + std::to_string(index);
+  auto const extractedName = "__extracted_" + std::to_string(index);
+  auto const outputName = scaleWorkspace(
+      extractSpectrum(workspace, index, extractedName), scaledName, 0.5);
+  deleteTemporaryWorkspaces({extractedName});
+  return outputName;
 }
 
-MatrixWorkspace_sptr appendWorkspace(MatrixWorkspace_sptr lhs,
-                                     MatrixWorkspace_sptr rhs) {
+std::string appendWorkspace(std::string const &lhsName,
+                            std::string const &rhsName,
+                            std::string const &outputName) {
   auto appendAlg = AlgorithmManager::Instance().create("AppendSpectra");
   appendAlg->initialize();
-  appendAlg->setChild(true);
   appendAlg->setLogging(false);
-  appendAlg->setProperty("InputWorkspace1", lhs);
-  appendAlg->setProperty("InputWorkspace2", rhs);
-  appendAlg->setProperty("OutputWorkspace", "__appended");
+  appendAlg->setProperty("InputWorkspace1", lhsName);
+  appendAlg->setProperty("InputWorkspace2", rhsName);
+  appendAlg->setProperty("OutputWorkspace", outputName);
   appendAlg->execute();
-  return appendAlg->getProperty("OutputWorkspace");
+  return outputName;
 }
 
-MatrixWorkspace_sptr
-appendAll(const std::vector<MatrixWorkspace_sptr> &workspaces) {
-  auto appended = workspaces.front();
+MatrixWorkspace_sptr appendAll(std::vector<std::string> const &workspaces,
+                               std::string const &outputName) {
+  auto appended = workspaces[0];
   for (auto i = 1u; i < workspaces.size(); ++i)
-    appended = appendWorkspace(appended, workspaces[i]);
-  return appended;
-}
-
-MatrixWorkspace_sptr addToADS(MatrixWorkspace_sptr workspace,
-                              const std::string &name) {
-  AnalysisDataService::Instance().addOrReplace(name, workspace);
-  return workspace;
+    appended = appendWorkspace(appended, workspaces[i], outputName);
+  return AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(appended);
 }
 
-std::vector<MatrixWorkspace_sptr>
+std::vector<std::string>
 subdivideWidthWorkspace(MatrixWorkspace_sptr workspace,
                         const std::vector<std::size_t> &widthSpectra) {
-  std::vector<MatrixWorkspace_sptr> subworkspaces;
+  std::vector<std::string> subworkspaces;
   subworkspaces.reserve(1 + 2 * widthSpectra.size());
 
   int start = 0;
   for (auto i = 0u; i < widthSpectra.size(); ++i) {
     const auto spectrum = static_cast<int>(widthSpectra[i]);
-    if (spectrum > start)
-      subworkspaces.emplace_back(
-          extractSpectra(workspace, start, spectrum - 1));
+    if (spectrum > start) {
+      auto const outputName = "__extracted_" + std::to_string(start) + "_to_" +
+                              std::to_string(spectrum);
+      subworkspaces.emplace_back(extractSpectra(workspace->getName(), start,
+                                                spectrum - 1, outputName));
+    }
     subworkspaces.emplace_back(extractHWHMSpectrum(workspace, spectrum));
     start = spectrum + 1;
   }
 
   const int end = static_cast<int>(workspace->getNumberHistograms());
-  if (start < end)
-    subworkspaces.emplace_back(extractSpectra(workspace, start, end - 1));
+  if (start < end) {
+    auto const outputName =
+        "__extracted_" + std::to_string(start) + "_to_" + std::to_string(end);
+    subworkspaces.emplace_back(
+        extractSpectra(workspace->getName(), start, end - 1, outputName));
+  }
   return subworkspaces;
 }
 
@@ -176,10 +188,13 @@ createHWHMWorkspace(MatrixWorkspace_sptr workspace, const std::string &hwhmName,
         hwhmName);
 
   const auto subworkspaces = subdivideWidthWorkspace(workspace, widthSpectra);
-  const auto hwhmWorkspace = appendAll(subworkspaces);
+  const auto hwhmWorkspace = appendAll(subworkspaces, hwhmName);
   const auto axis = workspace->getAxis(1)->clone(hwhmWorkspace.get());
   hwhmWorkspace->replaceAxis(1, dynamic_cast<TextAxis *>(axis));
-  return addToADS(hwhmWorkspace, hwhmName);
+
+  deleteTemporaryWorkspaces(subworkspaces);
+
+  return hwhmWorkspace;
 }
 
 boost::optional<std::size_t>
diff --git a/qt/scientific_interfaces/Indirect/LazyAsyncRunner.h b/qt/scientific_interfaces/Indirect/LazyAsyncRunner.h
index ecfada107f88293ec69a4ddc0b9cd6df90705c58..567bbb06fc11265a5c01697286ce4e7e9aa26216 100644
--- a/qt/scientific_interfaces/Indirect/LazyAsyncRunner.h
+++ b/qt/scientific_interfaces/Indirect/LazyAsyncRunner.h
@@ -20,7 +20,7 @@ namespace MantidQt {
 namespace CustomInterfaces {
 namespace IDA {
 
-class DLLExport QtLazyAsyncRunnerBase : public QObject {
+class MANTIDQT_INDIRECT_DLL QtLazyAsyncRunnerBase : public QObject {
   Q_OBJECT
 
 signals:
@@ -35,7 +35,7 @@ protected:
 };
 
 template <typename Callback>
-class DLLExport QtLazyAsyncRunner : public QtLazyAsyncRunnerBase {
+class MANTIDQT_INDIRECT_DLL QtLazyAsyncRunner : public QtLazyAsyncRunnerBase {
 public:
   using ReturnType = typename std::result_of<Callback()>::type;
 
diff --git a/qt/scientific_interfaces/Indirect/ResNorm.cpp b/qt/scientific_interfaces/Indirect/ResNorm.cpp
index 3b26d2f5a2be3424ef6d569377e2ed30f3063aca..8e3830a9e5ad48b381de6b139a10d3b7157dfc63 100644
--- a/qt/scientific_interfaces/Indirect/ResNorm.cpp
+++ b/qt/scientific_interfaces/Indirect/ResNorm.cpp
@@ -9,10 +9,31 @@
 #include "../General/UserInputValidator.h"
 #include "MantidAPI/ITableWorkspace.h"
 #include "MantidAPI/WorkspaceFactory.h"
-#include "MantidAPI/WorkspaceGroup.h"
+
+#include <map>
+#include <string>
 
 using namespace Mantid::API;
 
+namespace {
+
+MatrixWorkspace_sptr getADSMatrixWorkspace(std::string const &workspaceName) {
+  return AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(
+      workspaceName);
+}
+
+WorkspaceGroup_sptr getADSGroupWorkspace(std::string const &workspaceName) {
+  return AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>(
+      workspaceName);
+}
+
+ITableWorkspace_sptr getADSTableWorkspace(std::string const &workspaceName) {
+  return AnalysisDataService::Instance().retrieveWS<ITableWorkspace>(
+      workspaceName);
+}
+
+} // namespace
+
 namespace MantidQt {
 namespace CustomInterfaces {
 ResNorm::ResNorm(QWidget *parent) : IndirectBayesTab(parent), m_previewSpec(0) {
@@ -69,45 +90,38 @@ bool ResNorm::validate() {
   UserInputValidator uiv;
   QString errors("");
 
-  const bool vanValid =
+  bool const vanValid =
       uiv.checkDataSelectorIsValid("Vanadium", m_uiForm.dsVanadium);
-  const bool resValid =
+  bool const resValid =
       uiv.checkDataSelectorIsValid("Resolution", m_uiForm.dsResolution);
 
   if (vanValid) {
-    // Check vanadium input is _red ws
-    QString vanadiumName = m_uiForm.dsVanadium->getCurrentDataName();
-    int cutIndex = vanadiumName.lastIndexOf("_");
-    QString vanadiumSuffix =
-        vanadiumName.right(vanadiumName.size() - (cutIndex + 1));
-    if (vanadiumSuffix.compare("red") != 0) {
-      uiv.addErrorMessage(
-          "The Vanadium run is not a reduction (_red) workspace");
-    }
+    // Check vanadium input is _red or _sqw workspace
+    QString const vanName = m_uiForm.dsVanadium->getCurrentDataName();
+    int const cutIndex = vanName.lastIndexOf("_");
+    QString const vanSuffix = vanName.right(vanName.size() - (cutIndex + 1));
+    if (vanSuffix.compare("red") != 0 && vanSuffix.compare("sqw") != 0)
+      uiv.addErrorMessage("The Vanadium run is not _red or _sqw workspace");
 
     // Check Res and Vanadium are the same Run
     if (resValid) {
       // Check that Res file is still in ADS if not, load it
-      auto resolutionWs =
-          AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(
-              m_uiForm.dsResolution->getCurrentDataName().toStdString());
-      auto vanadiumWs =
-          AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(
-              vanadiumName.toStdString());
+      auto const resolutionWs = getADSMatrixWorkspace(
+          m_uiForm.dsResolution->getCurrentDataName().toStdString());
+      auto const vanadiumWs = getADSMatrixWorkspace(vanName.toStdString());
 
-      const int resRun = resolutionWs->getRunNumber();
-      const int vanRun = vanadiumWs->getRunNumber();
+      int const resRun = resolutionWs->getRunNumber();
+      int const vanRun = vanadiumWs->getRunNumber();
 
-      if (resRun != vanRun) {
+      if (resRun != vanRun)
         uiv.addErrorMessage("The provided Vanadium and Resolution do not have "
                             "matching run numbers");
-      }
     }
   }
 
   // check eMin and eMax values
-  const auto eMin = m_dblManager->value(m_properties["EMin"]);
-  const auto eMax = m_dblManager->value(m_properties["EMax"]);
+  auto const eMin = getDoubleManagerProperty("EMin");
+  auto const eMax = getDoubleManagerProperty("EMax");
   if (eMin >= eMax)
     errors.append("EMin must be strictly less than EMax.\n");
 
@@ -125,15 +139,15 @@ bool ResNorm::validate() {
  * Run the ResNorm v2 algorithm.
  */
 void ResNorm::run() {
-  const auto vanWsName(m_uiForm.dsVanadium->getCurrentDataName());
-  const auto resWsName(m_uiForm.dsResolution->getCurrentDataName());
+  auto const vanWsName(m_uiForm.dsVanadium->getCurrentDataName());
+  auto const resWsName(m_uiForm.dsResolution->getCurrentDataName());
 
-  const auto eMin(m_dblManager->value(m_properties["EMin"]));
-  const auto eMax(m_dblManager->value(m_properties["EMax"]));
+  auto const eMin(getDoubleManagerProperty("EMin"));
+  auto const eMax(getDoubleManagerProperty("EMax"));
 
-  const auto outputWsName = getWorkspaceBasename(resWsName) + "_ResNorm";
+  auto const outputWsName = getWorkspaceBasename(resWsName) + "_ResNorm";
 
-  IAlgorithm_sptr resNorm = AlgorithmManager::Instance().create("ResNorm", 2);
+  auto resNorm = AlgorithmManager::Instance().create("ResNorm", 2);
   resNorm->initialize();
   resNorm->setProperty("VanadiumWorkspace", vanWsName.toStdString());
   resNorm->setProperty("ResolutionWorkspace", resWsName.toStdString());
@@ -155,15 +169,92 @@ void ResNorm::run() {
  */
 void ResNorm::handleAlgorithmComplete(bool error) {
   setRunIsRunning(false);
-  if (!error)
+  if (!error) {
     // Update preview plot
     previewSpecChanged(m_previewSpec);
-  else {
+    // Copy and add sample logs to result workspaces
+    processLogs();
+  } else {
     setPlotResultEnabled(false);
     setSaveResultEnabled(false);
   }
 }
 
+void ResNorm::processLogs() {
+  auto const resWsName(m_uiForm.dsResolution->getCurrentDataName());
+  auto const outputWsName = getWorkspaceBasename(resWsName) + "_ResNorm";
+  auto const resolutionWorkspace =
+      getADSMatrixWorkspace(resWsName.toStdString());
+  auto const resultWorkspace = getADSGroupWorkspace(outputWsName.toStdString());
+
+  copyLogs(resolutionWorkspace, resultWorkspace);
+  addAdditionalLogs(resultWorkspace);
+}
+
+void ResNorm::addAdditionalLogs(WorkspaceGroup_sptr resultGroup) const {
+  for (auto const &workspace : *resultGroup)
+    addAdditionalLogs(workspace);
+}
+
+void ResNorm::addAdditionalLogs(Workspace_sptr resultWorkspace) const {
+  auto logAdder = AlgorithmManager::Instance().create("AddSampleLog");
+  auto const name = resultWorkspace->getName();
+
+  for (auto const &log : getAdditionalLogStrings()) {
+    logAdder->setProperty("Workspace", name);
+    logAdder->setProperty("LogType", "String");
+    logAdder->setProperty("LogName", log.first);
+    logAdder->setProperty("LogText", log.second);
+    logAdder->execute();
+  }
+
+  for (auto const &log : getAdditionalLogNumbers()) {
+    logAdder->setProperty("Workspace", name);
+    logAdder->setProperty("LogType", "Number");
+    logAdder->setProperty("LogName", log.first);
+    logAdder->setProperty("LogText", log.second);
+    logAdder->execute();
+  }
+}
+
+std::map<std::string, std::string> ResNorm::getAdditionalLogStrings() const {
+  auto logs = std::map<std::string, std::string>();
+  logs["sample_filename"] =
+      m_uiForm.dsVanadium->getCurrentDataName().toStdString();
+  logs["resolution_filename"] =
+      m_uiForm.dsResolution->getCurrentDataName().toStdString();
+  logs["fit_program"] = "ResNorm";
+  logs["create_output"] = "true";
+  return logs;
+}
+
+std::map<std::string, std::string> ResNorm::getAdditionalLogNumbers() const {
+  auto logs = std::map<std::string, std::string>();
+  logs["e_min"] =
+      boost::lexical_cast<std::string>(getDoubleManagerProperty("EMin"));
+  logs["e_max"] =
+      boost::lexical_cast<std::string>(getDoubleManagerProperty("EMax"));
+  return logs;
+}
+
+double ResNorm::getDoubleManagerProperty(QString const &propName) const {
+  return m_dblManager->value(m_properties[propName]);
+}
+
+void ResNorm::copyLogs(MatrixWorkspace_sptr resultWorkspace,
+                       WorkspaceGroup_sptr resultGroup) const {
+  for (auto const &workspace : *resultGroup)
+    copyLogs(resultWorkspace, workspace);
+}
+
+void ResNorm::copyLogs(MatrixWorkspace_sptr resultWorkspace,
+                       Workspace_sptr workspace) const {
+  auto logCopier = AlgorithmManager::Instance().create("CopyLogs");
+  logCopier->setProperty("InputWorkspace", resultWorkspace->getName());
+  logCopier->setProperty("OutputWorkspace", workspace->getName());
+  logCopier->execute();
+}
+
 /**
  * Set the data selectors to use the default save directory
  * when browsing for input files.
@@ -186,11 +277,10 @@ void ResNorm::handleVanadiumInputReady(const QString &filename) {
   m_uiForm.ppPlot->addSpectrum("Vanadium", filename, m_previewSpec);
 
   QPair<double, double> res;
-  QPair<double, double> range = m_uiForm.ppPlot->getCurveRange("Vanadium");
+  QPair<double, double> const range =
+      m_uiForm.ppPlot->getCurveRange("Vanadium");
 
-  MatrixWorkspace_sptr vanWs =
-      AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(
-          filename.toStdString());
+  auto const vanWs = getADSMatrixWorkspace(filename.toStdString());
   m_uiForm.spPreviewSpectrum->setMaximum(
       static_cast<int>(vanWs->getNumberHistograms()) - 1);
 
@@ -258,8 +348,8 @@ void ResNorm::updateProperties(QtProperty *prop, double val) {
   auto eRangeSelector = m_uiForm.ppPlot->getRangeSelector("ResNormERange");
 
   if (prop == m_properties["EMin"] || prop == m_properties["EMax"]) {
-    auto bounds = qMakePair(m_dblManager->value(m_properties["EMin"]),
-                            m_dblManager->value(m_properties["EMax"]));
+    auto bounds = qMakePair(getDoubleManagerProperty("EMin"),
+                            getDoubleManagerProperty("EMax"));
     setRangeSelector(eRangeSelector, m_properties["EMin"], m_properties["EMax"],
                      bounds);
   }
@@ -282,27 +372,21 @@ void ResNorm::previewSpecChanged(int value) {
   std::string fitWsGroupName(m_pythonExportWsName + "_Fit_Workspaces");
   std::string fitParamsName(m_pythonExportWsName + "_Fit");
   if (AnalysisDataService::Instance().doesExist(fitWsGroupName)) {
-    WorkspaceGroup_sptr fitWorkspaces =
-        AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>(
-            fitWsGroupName);
-    ITableWorkspace_sptr fitParams =
-        AnalysisDataService::Instance().retrieveWS<ITableWorkspace>(
-            fitParamsName);
+    auto const fitWorkspaces = getADSGroupWorkspace(fitWsGroupName);
+    auto const fitParams = getADSTableWorkspace(fitParamsName);
     if (fitWorkspaces && fitParams) {
       Column_const_sptr scaleFactors = fitParams->getColumn("Scaling");
       std::string fitWsName(fitWorkspaces->getItem(m_previewSpec)->getName());
-      MatrixWorkspace_const_sptr fitWs =
-          AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(
-              fitWsName);
+      auto const fitWs = getADSMatrixWorkspace(fitWsName);
 
-      MatrixWorkspace_sptr fit = WorkspaceFactory::Instance().create(fitWs, 1);
+      auto fit = WorkspaceFactory::Instance().create(fitWs, 1);
       fit->setSharedX(0, fitWs->sharedX(1));
       fit->setSharedY(0, fitWs->sharedY(1));
       fit->setSharedE(0, fitWs->sharedE(1));
 
       fit->mutableY(0) /= scaleFactors->cell<double>(m_previewSpec);
 
-      m_uiForm.ppPlot->addSpectrum("Fit", fit, 0, Qt::red);
+      m_uiForm.ppPlot->addSpectrum("Fit", fit, 0, Qt::green);
 
       AnalysisDataService::Instance().addOrReplace(
           "__" + fitWsGroupName + "_scaled", fit);
@@ -365,23 +449,12 @@ void ResNorm::saveClicked() {
  */
 void ResNorm::plotClicked() {
   setPlotResultIsPlotting(true);
-  WorkspaceGroup_sptr fitWorkspaces =
-      AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>(
-          m_pythonExportWsName + "_Fit_Workspaces");
-
-  QString fitWsName("");
-
-  if (fitWorkspaces)
-    fitWsName = QString::fromStdString(
-        fitWorkspaces->getItem(m_previewSpec)->getName());
 
-  QString plotOptions(m_uiForm.cbPlot->currentText());
+  QString const plotOptions = m_uiForm.cbPlot->currentText();
   if (plotOptions == "Intensity" || plotOptions == "All")
     plotSpectrum(QString::fromStdString(m_pythonExportWsName) + "_Intensity");
   if (plotOptions == "Stretch" || plotOptions == "All")
     plotSpectrum(QString::fromStdString(m_pythonExportWsName) + "_Stretch");
-  if (plotOptions == "Fit" || plotOptions == "All")
-    plotSpectrum(fitWsName, 0, 1);
 
   setPlotResultIsPlotting(false);
 }
diff --git a/qt/scientific_interfaces/Indirect/ResNorm.h b/qt/scientific_interfaces/Indirect/ResNorm.h
index 2def3ba0697f04e94fdec0108afa02388b6fecbe..e444290e133793c31a9d2df87e59bb40718ef2e1 100644
--- a/qt/scientific_interfaces/Indirect/ResNorm.h
+++ b/qt/scientific_interfaces/Indirect/ResNorm.h
@@ -8,6 +8,8 @@
 #define MANTIDQTCUSTOMINTERFACES_RESNORM_H_
 
 #include "IndirectBayesTab.h"
+#include "MantidAPI/MatrixWorkspace.h"
+#include "MantidAPI/WorkspaceGroup.h"
 #include "ui_ResNorm.h"
 
 namespace MantidQt {
@@ -47,6 +49,17 @@ private slots:
   void plotCurrentPreview();
 
 private:
+  void processLogs();
+  void addAdditionalLogs(Mantid::API::WorkspaceGroup_sptr resultGroup) const;
+  void addAdditionalLogs(Mantid::API::Workspace_sptr resultWorkspace) const;
+  std::map<std::string, std::string> getAdditionalLogStrings() const;
+  std::map<std::string, std::string> getAdditionalLogNumbers() const;
+  double getDoubleManagerProperty(QString const &propName) const;
+  void copyLogs(Mantid::API::MatrixWorkspace_sptr resultWorkspace,
+                Mantid::API::WorkspaceGroup_sptr resultGroup) const;
+  void copyLogs(Mantid::API::MatrixWorkspace_sptr resultWorkspace,
+                Mantid::API::Workspace_sptr workspace) const;
+
   void setRunEnabled(bool enabled);
   void setPlotResultEnabled(bool enabled);
   void setSaveResultEnabled(bool enabled);
diff --git a/qt/scientific_interfaces/Indirect/ResNorm.ui b/qt/scientific_interfaces/Indirect/ResNorm.ui
index b7b1bbdc8af40a388cf8fcf05c1b445f0ff7ed72..c94631b03d4582d2dbaab13362b7eeb541a22a8b 100644
--- a/qt/scientific_interfaces/Indirect/ResNorm.ui
+++ b/qt/scientific_interfaces/Indirect/ResNorm.ui
@@ -76,11 +76,13 @@
        <property name="workspaceSuffixes" stdset="0">
         <stringlist>
          <string>_red</string>
+         <string>_sqw</string>
         </stringlist>
        </property>
        <property name="fileBrowserSuffixes" stdset="0">
         <stringlist>
          <string>_red.nxs</string>
+         <string>_sqw.nxs</string>
         </stringlist>
        </property>
        <property name="showLoad" stdset="0">
diff --git a/qt/scientific_interfaces/Indirect/Stretch.cpp b/qt/scientific_interfaces/Indirect/Stretch.cpp
index 0013a59c2aff1d9333c285dd7b76ac200ea11065..0069a3f534f9b625fde7e3dcac32193d0e8415c2 100644
--- a/qt/scientific_interfaces/Indirect/Stretch.cpp
+++ b/qt/scientific_interfaces/Indirect/Stretch.cpp
@@ -266,12 +266,21 @@ void Stretch::plotWorkspaces() {
   // Check Sigma and Beta workspaces exist
   if (sigma.right(5).compare("Sigma") == 0 &&
       beta.right(4).compare("Beta") == 0) {
+    QString pyInput = "from mantidplot import plot2D\n";
 
     std::string const plotType = m_uiForm.cbPlot->currentText().toStdString();
-    if (plotType == "All" || plotType == "Beta")
-      plotSpectrum(beta);
-    if (plotType == "All" || plotType == "Sigma")
-      plotSpectrum(sigma);
+    if (plotType == "All" || plotType == "Beta") {
+      pyInput += "importMatrixWorkspace('";
+      pyInput += beta;
+      pyInput += "').plotGraph2D()\n";
+    }
+    if (plotType == "All" || plotType == "Sigma") {
+      pyInput += "importMatrixWorkspace('";
+      pyInput += sigma;
+      pyInput += "').plotGraph2D()\n";
+    }
+
+    m_pythonRunner.runPythonCode(pyInput);
   } else {
     g_log.error(
         "Beta and Sigma workspace were not found and could not be plotted.");
@@ -283,9 +292,11 @@ void Stretch::plotContourClicked() {
   setPlotContourIsPlotting(true);
 
   auto const workspaceName = m_uiForm.cbPlotContour->currentText();
-  if (checkADSForPlotSaveWorkspace(workspaceName.toStdString(), true))
-    plot2D(workspaceName);
-
+  if (checkADSForPlotSaveWorkspace(workspaceName.toStdString(), true)) {
+    QString pyInput = "from mantidplot import plot2D\nimportMatrixWorkspace('" +
+                      workspaceName + "').plotGraph2D()\n";
+    m_pythonRunner.runPythonCode(pyInput);
+  }
   setPlotContourIsPlotting(false);
 }
 
@@ -425,7 +436,7 @@ void Stretch::setPlotResultIsPlotting(bool plotting) {
 }
 
 void Stretch::setPlotContourIsPlotting(bool plotting) {
-  m_uiForm.pbPlot->setText(plotting ? "Plotting..." : "Plot Contour");
+  m_uiForm.pbPlotContour->setText(plotting ? "Plotting..." : "Plot Contour");
   setButtonsEnabled(!plotting);
 }
 
diff --git a/qt/scientific_interfaces/Indirect/test/CMakeLists.txt b/qt/scientific_interfaces/Indirect/test/CMakeLists.txt
index 53d64ce7ee128d5518d72c3f7b4f156bd1c18e24..c402c6ca2c551c8e65cf191d18256e192ea6dcf3 100644
--- a/qt/scientific_interfaces/Indirect/test/CMakeLists.txt
+++ b/qt/scientific_interfaces/Indirect/test/CMakeLists.txt
@@ -5,6 +5,7 @@ set ( TEST_FILES
   IndirectFitDataTest.h
   IndirectFitOutputTest.h
   IndirectFitPlotModelTest.h
+  IndirectFitPlotPresenterTest.h
   IndirectFittingModelTest.h
   IndirectSpectrumSelectionPresenterTest.h
 )
@@ -37,6 +38,7 @@ mtd_add_qt_tests (TARGET_NAME MantidQtInterfacesIndirectTest
   MTD_QT_LINK_LIBS
     MantidScientificInterfacesIndirect
     MantidQtWidgetsCommon
+    MantidQtWidgetsLegacyQwt
   PARENT_DEPENDENCIES
     GUITests
 )
diff --git a/qt/scientific_interfaces/Indirect/test/IndirectFitPlotPresenterTest.h b/qt/scientific_interfaces/Indirect/test/IndirectFitPlotPresenterTest.h
new file mode 100644
index 0000000000000000000000000000000000000000..74b425440fa32d82f4f00c2df5cd9164f9b095c4
--- /dev/null
+++ b/qt/scientific_interfaces/Indirect/test/IndirectFitPlotPresenterTest.h
@@ -0,0 +1,636 @@
+// Mantid Repository : https://github.com/mantidproject/mantid
+//
+// Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
+//     NScD Oak Ridge National Laboratory, European Spallation Source
+//     & Institut Laue - Langevin
+// SPDX - License - Identifier: GPL - 3.0 +
+#ifndef MANTIDQT_INDIRECTFITPLOTPRESENTERTEST_H_
+#define MANTIDQT_INDIRECTFITPLOTPRESENTERTEST_H_
+
+#include <cxxtest/TestSuite.h>
+#include <gmock/gmock.h>
+
+#include "IIndirectFitPlotView.h"
+#include "IndirectFitPlotPresenter.h"
+#include "IndirectFittingModel.h"
+#include "MantidAPI/FrameworkManager.h"
+#include "MantidAPI/FunctionFactory.h"
+#include "MantidAPI/IFunction.h"
+#include "MantidAPI/MatrixWorkspace.h"
+#include "MantidKernel/WarningSuppressions.h"
+#include "MantidTestHelpers/IndirectFitDataCreationHelper.h"
+
+using namespace Mantid::API;
+using namespace Mantid::IndirectFitDataCreationHelper;
+using namespace MantidQt::CustomInterfaces;
+using namespace MantidQt::CustomInterfaces::IDA;
+using namespace testing;
+
+namespace {
+
+IFunction_sptr getFunction(std::string const &functionString) {
+  return FunctionFactory::Instance().createInitialized(functionString);
+}
+
+IFunction_sptr getFunctionWithWorkspaceName(std::string const &workspaceName) {
+  std::string const functionString =
+      "name=LinearBackground,A0=0,A1=0,ties=(A0=0.000000,A1=0.0);"
+      "(composite=Convolution,FixResolution=true,NumDeriv=true;"
+      "name=Resolution,Workspace=" +
+      workspaceName +
+      ",WorkspaceIndex=0;((composite=ProductFunction,NumDeriv="
+      "false;name=Lorentzian,Amplitude=1,PeakCentre=0,FWHM=0."
+      "0175)))";
+  return getFunction(functionString);
+}
+
+} // namespace
+
+GNU_DIAG_OFF_SUGGEST_OVERRIDE
+
+/// Mock object to mock the view
+class MockIndirectFitPlotView : public IIndirectFitPlotView {
+public:
+  /// Signals
+  void emitSelectedFitDataChanged(std::size_t index) {
+    emit selectedFitDataChanged(index);
+  }
+
+  void emitPlotCurrentPreview() { emit plotCurrentPreview(); }
+
+  void emitPlotSpectrumChanged(std::size_t spectrum) {
+    emit plotSpectrumChanged(spectrum);
+  }
+
+  void emitPlotGuessChanged(bool doPlotGuess) {
+    emit plotGuessChanged(doPlotGuess);
+  }
+
+  void emitStartXChanged(double startX) { emit startXChanged(startX); }
+
+  void emitEndXChanged(double endX) { emit endXChanged(endX); }
+
+  void emitHWHMMinimumChanged(double minimum) {
+    emit hwhmMinimumChanged(minimum);
+  }
+
+  void emitHWHMMaximumChanged(double maximum) {
+    emit hwhmMaximumChanged(maximum);
+  }
+
+  void emitBackgroundChanged(double value) { emit backgroundChanged(value); }
+
+  /// Public methods
+  MOCK_CONST_METHOD0(getSelectedSpectrum, std::size_t());
+  MOCK_CONST_METHOD0(getSelectedSpectrumIndex, int());
+  MOCK_CONST_METHOD0(getSelectedDataIndex, int());
+  MOCK_CONST_METHOD0(dataSelectionSize, std::size_t());
+  MOCK_CONST_METHOD0(isPlotGuessChecked, bool());
+
+  MOCK_METHOD0(hideMultipleDataSelection, void());
+  MOCK_METHOD0(showMultipleDataSelection, void());
+
+  MOCK_METHOD2(setAvailableSpectra,
+               void(std::size_t minimum, std::size_t maximum));
+  MOCK_METHOD2(setAvailableSpectra,
+               void(std::vector<std::size_t>::const_iterator const &from,
+                    std::vector<std::size_t>::const_iterator const &to));
+
+  MOCK_METHOD1(setMinimumSpectrum, void(int minimum));
+  MOCK_METHOD1(setMaximumSpectrum, void(int maximum));
+  MOCK_METHOD1(setPlotSpectrum, void(int spectrum));
+  MOCK_METHOD1(appendToDataSelection, void(std::string const &dataName));
+  MOCK_METHOD2(setNameInDataSelection,
+               void(std::string const &dataName, std::size_t index));
+  MOCK_METHOD0(clearDataSelection, void());
+
+  MOCK_METHOD4(plotInTopPreview,
+               void(QString const &name,
+                    Mantid::API::MatrixWorkspace_sptr workspace,
+                    std::size_t spectrum, Qt::GlobalColor colour));
+  MOCK_METHOD4(plotInBottomPreview,
+               void(QString const &name,
+                    Mantid::API::MatrixWorkspace_sptr workspace,
+                    std::size_t spectrum, Qt::GlobalColor colour));
+
+  MOCK_METHOD1(removeFromTopPreview, void(QString const &name));
+  MOCK_METHOD1(removeFromBottomPreview, void(QString const &name));
+
+  MOCK_METHOD1(enableFitSingleSpectrum, void(bool enable));
+  MOCK_METHOD1(enablePlotGuess, void(bool enable));
+  MOCK_METHOD1(enableSpectrumSelection, void(bool enable));
+  MOCK_METHOD1(enableFitRangeSelection, void(bool enable));
+
+  MOCK_METHOD1(setBackgroundLevel, void(double value));
+
+  MOCK_METHOD2(setFitRange, void(double minimum, double maximum));
+  MOCK_METHOD1(setFitRangeMinimum, void(double minimum));
+  MOCK_METHOD1(setFitRangeMaximum, void(double maximum));
+
+  MOCK_METHOD1(setBackgroundRangeVisible, void(bool visible));
+  MOCK_METHOD1(setHWHMRangeVisible, void(bool visible));
+
+  MOCK_CONST_METHOD1(displayMessage, void(std::string const &message));
+
+  /// Public Slots
+  MOCK_METHOD0(clearTopPreview, void());
+  MOCK_METHOD0(clearBottomPreview, void());
+  MOCK_METHOD0(clear, void());
+
+  MOCK_METHOD2(setHWHMRange, void(double minimum, double maximum));
+  MOCK_METHOD1(setHWHMMinimum, void(double minimum));
+  MOCK_METHOD1(setHWHMMaximum, void(double maximum));
+};
+
+class MockIndirectFittingModel : public IndirectFittingModel {
+public:
+  /// Public methods
+  MOCK_CONST_METHOD1(getWorkspace, MatrixWorkspace_sptr(std::size_t index));
+  MOCK_CONST_METHOD2(getFittingRange,
+                     std::pair<double, double>(std::size_t dataIndex,
+                                               std::size_t spectrum));
+  MOCK_CONST_METHOD3(createDisplayName,
+                     std::string(std::string const &formatString,
+                                 std::string const &rangeDelimiter,
+                                 std::size_t dataIndex));
+  MOCK_CONST_METHOD0(isMultiFit, bool());
+  MOCK_CONST_METHOD0(numberOfWorkspaces, std::size_t());
+  MOCK_CONST_METHOD0(getFittingFunction, IFunction_sptr());
+
+  MOCK_METHOD3(setStartX, void(double startX, std::size_t dataIndex,
+                               std::size_t spectrum));
+  MOCK_METHOD3(setEndX,
+               void(double endX, std::size_t dataIndex, std::size_t spectrum));
+
+  MOCK_METHOD3(setDefaultParameterValue,
+               void(std::string const &name, double value,
+                    std::size_t dataIndex));
+
+private:
+  std::string sequentialFitOutputName() const override { return ""; };
+  std::string simultaneousFitOutputName() const override { return ""; };
+  std::string singleFitOutputName(std::size_t index,
+                                  std::size_t spectrum) const override {
+    UNUSED_ARG(index);
+    UNUSED_ARG(spectrum);
+    return "";
+  };
+
+  std::vector<std::string> getSpectrumDependentAttributes() const override {
+    return {};
+  };
+};
+
+GNU_DIAG_ON_SUGGEST_OVERRIDE
+
+class IndirectFitPlotPresenterTest : public CxxTest::TestSuite {
+public:
+  /// Needed to make sure everything is initialized
+  IndirectFitPlotPresenterTest() { FrameworkManager::Instance(); }
+
+  static IndirectFitPlotPresenterTest *createSuite() {
+    return new IndirectFitPlotPresenterTest();
+  }
+
+  static void destroySuite(IndirectFitPlotPresenterTest *suite) {
+    delete suite;
+  }
+
+  void setUp() override {
+    /// Note that the IndirectFitPlotModel could not be mocked as the Presenter
+    /// takes an IndirectFittingModel. This means the IndirectFittingModel is
+    /// mocked instead - which is a good substitute anyway
+    m_view = std::make_unique<NiceMock<MockIndirectFitPlotView>>();
+    m_fittingModel = std::make_unique<NiceMock<MockIndirectFittingModel>>();
+    m_presenter = std::make_unique<IndirectFitPlotPresenter>(
+        std::move(m_fittingModel.get()), std::move(m_view.get()));
+
+    SetUpADSWithWorkspace m_ads("WorkspaceName", createWorkspace(10));
+    m_fittingModel->addWorkspace("WorkspaceName");
+  }
+
+  void tearDown() override {
+    AnalysisDataService::Instance().clear();
+
+    TS_ASSERT(Mock::VerifyAndClearExpectations(m_view.get()));
+    TS_ASSERT(Mock::VerifyAndClearExpectations(m_fittingModel.get()));
+
+    m_presenter.reset();
+    m_fittingModel.reset();
+    m_view.reset();
+  }
+
+  ///----------------------------------------------------------------------
+  /// Unit tests to check for successful presenter instantiation
+  ///----------------------------------------------------------------------
+
+  void test_that_the_model_and_view_have_been_instantiated_correctly() {
+    std::size_t const selectedSpectrum(3);
+
+    ON_CALL(*m_view, getSelectedSpectrum())
+        .WillByDefault(Return(selectedSpectrum));
+    ON_CALL(*m_fittingModel, isMultiFit()).WillByDefault(Return(false));
+
+    EXPECT_CALL(*m_view, getSelectedSpectrum())
+        .Times(1)
+        .WillOnce(Return(selectedSpectrum));
+    EXPECT_CALL(*m_fittingModel, isMultiFit()).Times(1).WillOnce(Return(false));
+
+    m_view->getSelectedSpectrum();
+    m_fittingModel->isMultiFit();
+  }
+
+  void
+  test_that_invoking_a_presenter_method_will_call_the_relevant_methods_in_the_model_and_view() {
+    std::size_t const selectionSize(2);
+
+    ON_CALL(*m_view, dataSelectionSize()).WillByDefault(Return(selectionSize));
+
+    EXPECT_CALL(*m_fittingModel, numberOfWorkspaces())
+        .Times(2)
+        .WillRepeatedly(Return(1));
+    EXPECT_CALL(*m_view, dataSelectionSize())
+        .Times(1)
+        .WillOnce(Return(selectionSize));
+
+    m_presenter->appendLastDataToSelection();
+  }
+
+  ///----------------------------------------------------------------------
+  /// Unit Tests that test the signals (only the view emits signals here)
+  ///----------------------------------------------------------------------
+
+  void test_that_the_selectedFitDataChanged_signal_will_set_the_activeIndex() {
+    m_view->emitSelectedFitDataChanged(1);
+    TS_ASSERT_EQUALS(m_presenter->getSelectedDataIndex(), 1);
+  }
+
+  void
+  test_that_the_selectedFitDataChanged_signal_will_set_the_available_spectra() {
+    std::size_t const index(0);
+    ON_CALL(*m_fittingModel, getWorkspace(index))
+        .WillByDefault(Return(m_ads->retrieveWorkspace("WorkspaceName")));
+
+    EXPECT_CALL(*m_view, setAvailableSpectra(0, 9)).Times(1);
+
+    m_view->emitSelectedFitDataChanged(index);
+  }
+
+  void
+  test_that_the_selectedFitDataChanged_signal_will_disable_selectors_when_there_is_no_workspace() {
+    std::size_t const index(0);
+    ON_CALL(*m_fittingModel, getWorkspace(index))
+        .WillByDefault(Return(nullptr));
+
+    EXPECT_CALL(*m_view, enableSpectrumSelection(false)).Times(1);
+    EXPECT_CALL(*m_view, enableFitRangeSelection(false)).Times(1);
+
+    m_view->emitSelectedFitDataChanged(index);
+  }
+
+  void
+  test_that_the_selectedFitDataChanged_signal_will_plot_the_input_when_there_is_only_an_input_workspace() {
+    std::size_t const index(0);
+    ON_CALL(*m_fittingModel, getWorkspace(index))
+        .WillByDefault(Return(m_ads->retrieveWorkspace("WorkspaceName")));
+
+    EXPECT_CALL(*m_fittingModel, getWorkspace(index)).Times(3);
+    EXPECT_CALL(*m_view, removeFromBottomPreview(QString("Difference")))
+        .Times(1);
+
+    m_view->emitSelectedFitDataChanged(index);
+  }
+
+  void
+  test_that_the_selectedFitDataChanged_signal_will_clear_the_plots_when_there_is_no_input_workspace() {
+    std::size_t const index(0);
+    ON_CALL(*m_fittingModel, getWorkspace(index))
+        .WillByDefault(Return(nullptr));
+
+    EXPECT_CALL(*m_fittingModel, getWorkspace(index)).Times(2);
+    EXPECT_CALL(*m_view, clear()).Times(1);
+
+    m_view->emitSelectedFitDataChanged(index);
+  }
+
+  void
+  test_that_the_selectedFitDataChanged_signal_will_set_the_minimum_and_maximum_of_the_fit_range() {
+    std::size_t const index(0);
+    auto const range = std::make_pair(1.0, 2.0);
+    ON_CALL(*m_fittingModel, getFittingRange(index, 0))
+        .WillByDefault(Return(range));
+
+    EXPECT_CALL(*m_fittingModel, getFittingRange(index, 0))
+        .Times(2)
+        .WillRepeatedly(Return(range));
+    EXPECT_CALL(*m_view, setFitRangeMinimum(1.0)).Times(2);
+    EXPECT_CALL(*m_view, setFitRangeMaximum(2.0)).Times(2);
+
+    m_view->emitSelectedFitDataChanged(index);
+  }
+
+  void
+  test_that_the_selectedFitDataChanged_signal_will_enable_PlotGuess_when_there_is_a_fit_function_and_workspace() {
+    std::size_t const index(0);
+    std::string const workspaceName("WorkspaceName");
+    auto const fitFunction = getFunctionWithWorkspaceName(workspaceName);
+
+    ON_CALL(*m_fittingModel, getFittingFunction())
+        .WillByDefault(Return(fitFunction));
+    ON_CALL(*m_fittingModel, getWorkspace(index))
+        .WillByDefault(Return(m_ads->retrieveWorkspace(workspaceName)));
+
+    EXPECT_CALL(*m_view, enablePlotGuess(true)).Times(1);
+
+    m_view->emitSelectedFitDataChanged(index);
+  }
+
+  void
+  test_that_the_selectedFitDataChanged_signal_will_disable_the_guess_plot_when_there_is_no_fit_function() {
+    std::size_t const index(0);
+    ON_CALL(*m_fittingModel, getWorkspace(index))
+        .WillByDefault(Return(m_ads->retrieveWorkspace("WorkspaceName")));
+
+    EXPECT_CALL(*m_view, enablePlotGuess(false)).Times(1);
+
+    m_view->emitSelectedFitDataChanged(index);
+  }
+
+  void test_that_the_plotSpectrumChanged_signal_will_set_the_active_spectrum() {
+    m_view->emitPlotSpectrumChanged(2);
+    TS_ASSERT_EQUALS(m_presenter->getSelectedSpectrum(), 2);
+  }
+
+  void
+  test_that_the_plotSpectrumChanged_signal_will_plot_the_input_when_there_is_only_an_input_workspace() {
+    std::size_t const index(0);
+    ON_CALL(*m_fittingModel, getWorkspace(index))
+        .WillByDefault(Return(m_ads->retrieveWorkspace("WorkspaceName")));
+
+    EXPECT_CALL(*m_fittingModel, getWorkspace(index)).Times(2);
+    EXPECT_CALL(*m_view, removeFromBottomPreview(QString("Difference")))
+        .Times(1);
+
+    m_view->emitPlotSpectrumChanged(index);
+  }
+
+  void
+  test_that_the_plotSpectrumChanged_signal_will_clear_the_plots_when_there_is_no_input_workspace() {
+    std::size_t const index(0);
+    ON_CALL(*m_fittingModel, getWorkspace(index))
+        .WillByDefault(Return(nullptr));
+
+    EXPECT_CALL(*m_fittingModel, getWorkspace(index)).Times(1);
+    EXPECT_CALL(*m_view, clear()).Times(1);
+
+    m_view->emitPlotSpectrumChanged(index);
+  }
+
+  void
+  test_that_the_plotSpectrumChanged_signal_will_set_the_minimum_and_maximum_of_the_fit_range() {
+    std::size_t const index(0);
+    auto const range = std::make_pair(1.0, 2.0);
+    ON_CALL(*m_fittingModel, getFittingRange(index, 0))
+        .WillByDefault(Return(range));
+
+    EXPECT_CALL(*m_fittingModel, getFittingRange(index, 0))
+        .Times(2)
+        .WillRepeatedly(Return(range));
+    EXPECT_CALL(*m_view, setFitRangeMinimum(1.0)).Times(2);
+    EXPECT_CALL(*m_view, setFitRangeMaximum(2.0)).Times(2);
+
+    m_view->emitPlotSpectrumChanged(index);
+  }
+
+  void
+  test_that_the_plotCurrentPreview_signal_will_display_an_error_message_if_there_is_no_input_workspace() {
+    std::size_t const index(0);
+    std::string const message("Workspace not found - data may not be loaded.");
+
+    ON_CALL(*m_fittingModel, getWorkspace(index))
+        .WillByDefault(Return(nullptr));
+
+    Expectation getWorkspace =
+        EXPECT_CALL(*m_fittingModel, getWorkspace(index)).Times(1);
+    EXPECT_CALL(*m_view, displayMessage(message)).Times(1).After(getWorkspace);
+
+    m_view->emitPlotCurrentPreview();
+  }
+
+  void
+  test_that_the_plotGuessChanged_signal_will_not_clear_the_guess_plot_when_passed_true() {
+    std::size_t const index(0);
+    std::string const workspaceName("WorkspaceName");
+    auto const range = std::make_pair(1.0, 2.0);
+    auto const fitFunction = getFunctionWithWorkspaceName(workspaceName);
+
+    ON_CALL(*m_fittingModel, getFittingRange(index, 0))
+        .WillByDefault(Return(range));
+    ON_CALL(*m_fittingModel, getFittingFunction())
+        .WillByDefault(Return(fitFunction));
+    ON_CALL(*m_fittingModel, getWorkspace(index))
+        .WillByDefault(Return(m_ads->retrieveWorkspace(workspaceName)));
+
+    EXPECT_CALL(*m_view, removeFromTopPreview(QString("Guess"))).Times(0);
+
+    m_view->emitPlotGuessChanged(true);
+  }
+
+  void
+  test_that_the_plotGuessChanged_signal_will_clear_the_guess_plot_when_passed_false() {
+    std::size_t const index(0);
+    ON_CALL(*m_fittingModel, getWorkspace(index))
+        .WillByDefault(Return(m_ads->retrieveWorkspace("WorkspaceName")));
+
+    EXPECT_CALL(*m_view, removeFromTopPreview(QString("Guess"))).Times(1);
+
+    m_view->emitPlotGuessChanged(false);
+  }
+
+  void test_that_the_startXChanged_signal_will_set_the_fitting_models_startX() {
+    auto const range = std::make_pair(0.0, 2.0);
+    ON_CALL(*m_fittingModel, getFittingRange(0, 0))
+        .WillByDefault(Return(range));
+
+    EXPECT_CALL(*m_fittingModel, setStartX(1.0, 0, 0)).Times(1);
+
+    m_view->emitStartXChanged(1.0);
+  }
+
+  void test_that_the_endXChanged_signal_will_set_the_fitting_models_endX() {
+    EXPECT_CALL(*m_fittingModel, setEndX(2.0, 0, 0)).Times(1);
+    m_view->emitEndXChanged(2.0);
+  }
+
+  void test_that_the_hwhmMaximumChanged_signal_will_set_the_hwhm_minimum() {
+    EXPECT_CALL(*m_view, setHWHMMinimum(-2.0)).Times(1);
+    m_view->emitHWHMMaximumChanged(2.0);
+  }
+
+  void test_that_the_hwhmMinimumChanged_signal_will_set_the_hwhm_maximum() {
+    EXPECT_CALL(*m_view, setHWHMMaximum(-2.0)).Times(1);
+    m_view->emitHWHMMinimumChanged(2.0);
+  }
+
+  void
+  test_that_the_backgroundChanged_signal_will_set_the_functions_background() {
+    double const background(1.2);
+    auto const fitFunction = getFunctionWithWorkspaceName("WorkspaceName");
+
+    ON_CALL(*m_fittingModel, getFittingFunction())
+        .WillByDefault(Return(fitFunction));
+
+    Expectation setDefault =
+        EXPECT_CALL(*m_fittingModel,
+                    setDefaultParameterValue("A0", background, 0))
+            .Times(1);
+    EXPECT_CALL(*m_fittingModel, getFittingFunction())
+        .Times(1)
+        .After(setDefault);
+
+    m_view->emitBackgroundChanged(background);
+  }
+
+  ///----------------------------------------------------------------------
+  /// Unit Tests that test the methods and slots
+  ///----------------------------------------------------------------------
+
+  void
+  test_that_getSelectedSpectrumIndex_will_get_the_selected_spectrum_from_the_view() {
+    EXPECT_CALL(*m_view, getSelectedSpectrumIndex())
+        .Times(1)
+        .WillOnce(Return(0));
+    m_presenter->getSelectedSpectrumIndex();
+  }
+
+  void
+  test_that_isCurrentlySelected_returns_true_if_the_index_and_spectrum_given_are_selected() {
+    m_view->emitSelectedFitDataChanged(2);
+    TS_ASSERT(m_presenter->isCurrentlySelected(2, 0));
+  }
+
+  void
+  test_that_isCurrentlySelected_returns_false_if_the_index_and_spectrum_given_are_not_selected() {
+    m_view->emitSelectedFitDataChanged(2);
+    TS_ASSERT(!m_presenter->isCurrentlySelected(0, 0));
+  }
+
+  void test_that_setStartX_will_set_the_fit_range_minimum_in_the_view() {
+    EXPECT_CALL(*m_view, setFitRangeMinimum(2.0)).Times(1);
+    m_presenter->setStartX(2.0);
+  }
+
+  void test_that_setEndX_will_set_the_fit_range_maximum_in_the_view() {
+    EXPECT_CALL(*m_view, setFitRangeMaximum(3.0)).Times(1);
+    m_presenter->setEndX(3.0);
+  }
+
+  void
+  test_that_hideMultipleDataSelection_will_call_hideMultipleDataSelection_in_the_view() {
+    EXPECT_CALL(*m_view, hideMultipleDataSelection()).Times(1);
+    m_presenter->hideMultipleDataSelection();
+  }
+
+  void
+  test_that_showMultipleDataSelection_will_call_showMultipleDataSelection_in_the_view() {
+    EXPECT_CALL(*m_view, showMultipleDataSelection()).Times(1);
+    m_presenter->showMultipleDataSelection();
+  }
+
+  void test_that_updateRangeSelectors_will_update_the_background_selector() {
+    auto const fitFunction = getFunctionWithWorkspaceName("WorkspaceName");
+
+    ON_CALL(*m_fittingModel, getFittingFunction())
+        .WillByDefault(Return(fitFunction));
+
+    Expectation setVisible =
+        EXPECT_CALL(*m_view, setBackgroundRangeVisible(true)).Times(1);
+    EXPECT_CALL(*m_view, setBackgroundLevel(0.0)).Times(1).After(setVisible);
+
+    m_presenter->updateRangeSelectors();
+  }
+
+  void test_that_updateRangeSelectors_will_update_the_hwhm_selector() {
+    auto const fitFunction = getFunctionWithWorkspaceName("WorkspaceName");
+
+    ON_CALL(*m_fittingModel, getFittingFunction())
+        .WillByDefault(Return(fitFunction));
+
+    Expectation setVisible =
+        EXPECT_CALL(*m_view, setHWHMRangeVisible(true)).Times(1);
+    EXPECT_CALL(*m_view, setHWHMMinimum(-0.00875)).Times(1).After(setVisible);
+    EXPECT_CALL(*m_view, setHWHMMaximum(0.00875)).Times(1).After(setVisible);
+
+    m_presenter->updateRangeSelectors();
+  }
+
+  void
+  test_that_appendLastDataToSelection_will_set_the_name_of_the_data_selection_if_the_dataSelectionSize_and_numberOfWorkspaces_are_equal() {
+    std::size_t const index(1);
+
+    ON_CALL(*m_view, dataSelectionSize()).WillByDefault(Return(2));
+    ON_CALL(*m_fittingModel, numberOfWorkspaces()).WillByDefault(Return(2));
+    ON_CALL(*m_fittingModel, createDisplayName("%1% (%2%)", "-", index))
+        .WillByDefault(Return("DisplayName-1"));
+    ON_CALL(*m_fittingModel, getWorkspace(index))
+        .WillByDefault(Return(m_ads->retrieveWorkspace("WorkspaceName")));
+
+    Expectation createName =
+        EXPECT_CALL(*m_fittingModel, createDisplayName("%1% (%2%)", "-", index))
+            .Times(1);
+    EXPECT_CALL(*m_view, setNameInDataSelection("DisplayName-1", index))
+        .Times(1)
+        .After(createName);
+
+    m_presenter->appendLastDataToSelection();
+  }
+
+  void
+  test_that_appendLastDataToSelection_will_add_to_the_data_selection_if_the_dataSelectionSize_and_numberOfWorkspaces_are_not_equal() {
+    std::size_t const index(1);
+
+    ON_CALL(*m_view, dataSelectionSize()).WillByDefault(Return(1));
+    ON_CALL(*m_fittingModel, numberOfWorkspaces()).WillByDefault(Return(2));
+    ON_CALL(*m_fittingModel, createDisplayName("%1% (%2%)", "-", index))
+        .WillByDefault(Return("DisplayName-1"));
+    ON_CALL(*m_fittingModel, getWorkspace(index))
+        .WillByDefault(Return(m_ads->retrieveWorkspace("WorkspaceName")));
+
+    Expectation createName =
+        EXPECT_CALL(*m_fittingModel, createDisplayName("%1% (%2%)", "-", index))
+            .Times(1);
+    EXPECT_CALL(*m_view, appendToDataSelection("DisplayName-1"))
+        .Times(1)
+        .After(createName);
+
+    m_presenter->appendLastDataToSelection();
+  }
+
+  void
+  test_that_updateSelectedDataName_will_update_the_name_in_the_data_selection() {
+    std::size_t const index(0);
+
+    ON_CALL(*m_fittingModel, createDisplayName("%1% (%2%)", "-", index))
+        .WillByDefault(Return("DisplayName-1"));
+    ON_CALL(*m_fittingModel, getWorkspace(index))
+        .WillByDefault(Return(m_ads->retrieveWorkspace("WorkspaceName")));
+
+    Expectation createName =
+        EXPECT_CALL(*m_fittingModel, createDisplayName("%1% (%2%)", "-", index))
+            .Times(1);
+    EXPECT_CALL(*m_view, setNameInDataSelection("DisplayName-1", 0))
+        .Times(1)
+        .After(createName);
+
+    m_presenter->updateSelectedDataName();
+  }
+
+private:
+  std::unique_ptr<MockIndirectFitPlotView> m_view;
+  std::unique_ptr<MockIndirectFittingModel> m_fittingModel;
+  std::unique_ptr<IndirectFitPlotPresenter> m_presenter;
+
+  SetUpADSWithWorkspace *m_ads;
+};
+
+#endif
\ No newline at end of file
diff --git a/qt/scientific_interfaces/Indirect/test/IndirectSpectrumSelectionPresenterTest.h b/qt/scientific_interfaces/Indirect/test/IndirectSpectrumSelectionPresenterTest.h
index 3962e59f60e143d2b4fc6af2be08b7610309485f..cfcf11936cf5e61216434f07a904ed4221151a6a 100644
--- a/qt/scientific_interfaces/Indirect/test/IndirectSpectrumSelectionPresenterTest.h
+++ b/qt/scientific_interfaces/Indirect/test/IndirectSpectrumSelectionPresenterTest.h
@@ -93,7 +93,7 @@ public:
 /// Note that there is limited (if any) interaction going from this model to the
 /// IndirectSpectrumSelectionView, meaning that not many methods are required
 /// for mocking.
-class MockIndirectFittingModel : public IndirectFittingModel {
+class MockIndirectSpectrumSelectionModel : public IndirectFittingModel {
 public:
   /// Public methods
   MOCK_CONST_METHOD2(getExcludeRegion,
@@ -131,9 +131,10 @@ public:
   }
 
   void setUp() override {
-    m_view = new NiceMock<MockIndirectSpectrumSelectionView>();
-    m_model = new NiceMock<MockIndirectFittingModel>();
-    m_presenter = new IndirectSpectrumSelectionPresenter(m_model, m_view);
+    m_view = std::make_unique<NiceMock<MockIndirectSpectrumSelectionView>>();
+    m_model = std::make_unique<NiceMock<MockIndirectSpectrumSelectionModel>>();
+    m_presenter = std::make_unique<IndirectSpectrumSelectionPresenter>(
+        std::move(m_model.get()), std::move(m_view.get()));
 
     SetUpADSWithWorkspace ads("WorkspaceName", createWorkspace(10));
     m_model->addWorkspace("WorkspaceName");
@@ -142,11 +143,12 @@ public:
   void tearDown() override {
     AnalysisDataService::Instance().clear();
 
-    TS_ASSERT(Mock::VerifyAndClearExpectations(m_view));
-    TS_ASSERT(Mock::VerifyAndClearExpectations(m_model));
+    TS_ASSERT(Mock::VerifyAndClearExpectations(m_view.get()));
+    TS_ASSERT(Mock::VerifyAndClearExpectations(m_model.get()));
 
-    delete m_presenter; /// Note that the m_view destructor is called here
-    delete m_model;
+    m_presenter.reset(); /// The views destructor is called at this point
+    m_model.reset();
+    m_view.release();
   }
 
   ///----------------------------------------------------------------------
@@ -358,9 +360,9 @@ public:
   }
 
 private:
-  MockIndirectSpectrumSelectionView *m_view;
-  MockIndirectFittingModel *m_model;
-  IndirectSpectrumSelectionPresenter *m_presenter;
+  std::unique_ptr<MockIndirectSpectrumSelectionView> m_view;
+  std::unique_ptr<MockIndirectSpectrumSelectionModel> m_model;
+  std::unique_ptr<IndirectSpectrumSelectionPresenter> m_presenter;
 };
 
 #endif
diff --git a/qt/scientific_interfaces/Muon/MuonAnalysis.cpp b/qt/scientific_interfaces/Muon/MuonAnalysis.cpp
index b5088718cfb16193e40ac8e247ef0f1d35234cc4..3011ce199ad9fb5fe65df08424525706473b00f0 100644
--- a/qt/scientific_interfaces/Muon/MuonAnalysis.cpp
+++ b/qt/scientific_interfaces/Muon/MuonAnalysis.cpp
@@ -40,8 +40,8 @@
 #include <Poco/File.h>
 #include <Poco/Path.h>
 #include <Poco/StringTokenizer.h>
-
 #include <boost/lexical_cast.hpp>
+#include <math.h>
 
 #include <algorithm>
 
@@ -1935,11 +1935,50 @@ void MuonAnalysis::plotSpectrum(const QString &wsName, bool logScale) {
 QMap<QString, QString> MuonAnalysis::getPlotStyleParams(const QString &wsName) {
   // Get parameter values from the options tab
   QMap<QString, QString> params = m_optionTab->parsePlotStyleParams();
+  auto upper = m_uiForm.timeAxisFinishAtInput->text().toDouble();
+
+  Workspace_const_sptr ws_ptr =
+      AnalysisDataService::Instance().retrieve(wsName.toStdString());
+  MatrixWorkspace_const_sptr matrix_workspace =
+      boost::dynamic_pointer_cast<const MatrixWorkspace>(ws_ptr);
+  const auto &xData = matrix_workspace->x(0);
 
-  params["XAxisMin"] =
-      QString::number(m_uiForm.timeAxisStartAtInput->text().toDouble());
-  params["XAxisMax"] =
-      QString::number(m_uiForm.timeAxisFinishAtInput->text().toDouble());
+  auto lower = m_uiForm.timeAxisStartAtInput->text().toDouble();
+  if (upper > *max_element(xData.begin(), xData.end())) {
+    QMessageBox::warning(this, tr("Muon Analysis"),
+                         tr("Upper bound is beyond data range.\n"
+                            "Setting end time to last time value (minus 1)."),
+                         QMessageBox::Ok, QMessageBox::Ok);
+    // subtract a small amount off to prevent a crash from using the exact end
+    upper = *max_element(xData.begin(), xData.end()) - 1.;
+    m_uiForm.timeAxisFinishAtInput->setText(QString::number(upper));
+  }
+  if (upper < *min_element(xData.begin(), xData.end())) {
+    QMessageBox::warning(this, tr("Muon Analysis"),
+                         tr("No data in selected range.\n"
+                            "Setting end time to last time value (minus 1)."),
+                         QMessageBox::Ok, QMessageBox::Ok);
+    upper = *max_element(xData.begin(), xData.end()) - 1.;
+    m_uiForm.timeAxisFinishAtInput->setText(QString::number(upper));
+  }
+  params["XAxisMax"] = QString::number(upper);
+  if (lower > upper) {
+    QMessageBox::warning(this, tr("Muon Analysis"),
+                         tr("Time max is less than time min.\n"
+                            "Will change time min."),
+                         QMessageBox::Ok, QMessageBox::Ok);
+    lower = *min_element(xData.begin(), xData.end());
+    m_uiForm.timeAxisStartAtInput->setText(QString::number(lower));
+  }
+  if (lower > *max_element(xData.begin(), xData.end())) {
+    QMessageBox::warning(this, tr("Muon Analysis"),
+                         tr("No data in selected range.\n"
+                            "Setting start time to first time value."),
+                         QMessageBox::Ok, QMessageBox::Ok);
+    lower = *min_element(xData.begin(), xData.end());
+    m_uiForm.timeAxisStartAtInput->setText(QString::number(lower));
+  }
+  params["XAxisMin"] = QString::number(lower);
 
   // If autoscale disabled
   if (params["YAxisAuto"] == "False") {
@@ -1949,10 +1988,6 @@ QMap<QString, QString> MuonAnalysis::getPlotStyleParams(const QString &wsName) {
 
     // If any of those is not specified - get min and max by default
     if (min.isEmpty() || max.isEmpty()) {
-      Workspace_sptr ws_ptr =
-          AnalysisDataService::Instance().retrieve(wsName.toStdString());
-      MatrixWorkspace_sptr matrix_workspace =
-          boost::dynamic_pointer_cast<MatrixWorkspace>(ws_ptr);
       const auto &yData = matrix_workspace->y(0);
 
       if (min.isEmpty())
diff --git a/qt/scientific_interfaces/test/ReflSettingsPresenterTest.h b/qt/scientific_interfaces/test/ReflSettingsPresenterTest.h
index 373645d4039ff511815de82aa03774dbdba9ceca..435a6012813952422de09fb230f5c82781e46659 100644
--- a/qt/scientific_interfaces/test/ReflSettingsPresenterTest.h
+++ b/qt/scientific_interfaces/test/ReflSettingsPresenterTest.h
@@ -204,10 +204,10 @@ public:
 
     auto options = presenter.getReductionOptions();
     TS_ASSERT_EQUALS(variantToString(options["PolarizationAnalysis"]), "PNR");
-    TS_ASSERT_EQUALS(variantToString(options["Rho"]), "");
-    TS_ASSERT_EQUALS(variantToString(options["Alpha"]), "");
-    TS_ASSERT_EQUALS(variantToString(options["Ap"]), "100.0,17.0,44.0");
-    TS_ASSERT_EQUALS(variantToString(options["Pp"]), "0.54,0.33,1.81");
+    TS_ASSERT_EQUALS(variantToString(options["CRho"]), "");
+    TS_ASSERT_EQUALS(variantToString(options["CAlpha"]), "");
+    TS_ASSERT_EQUALS(variantToString(options["CAp"]), "100.0,17.0,44.0");
+    TS_ASSERT_EQUALS(variantToString(options["CPp"]), "0.54,0.33,1.81");
 
     TS_ASSERT(Mock::VerifyAndClearExpectations(&mockView));
   }
diff --git a/qt/widgets/common/src/MantidWSIndexDialog.cpp b/qt/widgets/common/src/MantidWSIndexDialog.cpp
index f6a6ad46081235408b1507bdfaa79eeec25a1657..afebf2794f9f57c51d472b44e23fd5f64067ed76 100644
--- a/qt/widgets/common/src/MantidWSIndexDialog.cpp
+++ b/qt/widgets/common/src/MantidWSIndexDialog.cpp
@@ -10,6 +10,7 @@
 #include "MantidAPI/Run.h"
 #include "MantidAPI/SpectraDetectorTypes.h"
 #include "MantidAPI/WorkspaceGroup.h"
+#include "MantidKernel/TimeSeriesProperty.h"
 
 #include <QMessageBox>
 #include <QPalette>
@@ -619,6 +620,19 @@ void MantidWSIndexWidget::onPlotOptionChanged(const QString &plotOption) {
   }
 }
 
+namespace {
+struct LogTestStruct {
+  LogTestStruct()
+      : isconstantvalue(true), value(std::numeric_limits<double>::quiet_NaN()) {
+  }
+  LogTestStruct(bool isconstantvalue, double value)
+      : isconstantvalue(isconstantvalue), value(value) {}
+
+  bool isconstantvalue;
+  double value;
+};
+} // namespace
+
 /**
  * Populate the log combo box with all log names that
  * have single numeric value per workspace (and occur
@@ -628,34 +642,55 @@ void MantidWSIndexWidget::populateLogComboBox() {
   // First item should be "Workspace index"
   m_logSelector->addItem(WORKSPACE_NAME);
 
-  // Create a table of all single-value numeric log names versus
-  // how many workspaces they appear in
-  std::map<std::string, int> logCounts;
+  // Create a map of all logs and their double represenation to compare against.
+  // Only logs that can be converted to a double and are not all equal will make
+  // the final cut
+  // it is map[logname] = (isconstantvalue, value)
+  std::map<std::string, LogTestStruct> usableLogs;
+  // add the logs that are present in the first workspace
+  auto ws = getWorkspace(m_wsNames[0]);
+  if (ws) {
+    const auto runObj = ws->run();
+    const std::vector<Mantid::Kernel::Property *> &logData =
+        runObj.getLogData();
+    for (auto &log : logData) {
+      const std::string &name = log->name();
+      try {
+        const auto value = runObj.getLogAsSingleValue(
+            name, Mantid::Kernel::Math::TimeAveragedMean);
+        usableLogs[name] = LogTestStruct{true, value};
+      } catch (std::invalid_argument &) {
+        // it can't be represented as a double so ignore it
+      }
+    }
+  }
+
+  // loop over all of the workspaces in the group to see that the value has
+  // changed
   for (auto &wsName : m_wsNames) {
     auto ws = getWorkspace(wsName);
     if (ws) {
-      const std::vector<Mantid::Kernel::Property *> &logData =
-          ws->run().getLogData();
-      for (auto &log : logData) {
-        // If this is a single-value numeric log, add it to the list of counts
-        if (dynamic_cast<Mantid::Kernel::PropertyWithValue<int> *>(log) ||
-            dynamic_cast<Mantid::Kernel::PropertyWithValue<double> *>(log)) {
-          const std::string &name = log->name();
-          if (logCounts.find(name) != logCounts.end()) {
-            logCounts[name]++;
-          } else {
-            logCounts[name] = 1;
+      const auto runObj = ws->run();
+      for (auto &logItem : usableLogs) {
+        if (runObj.hasProperty(logItem.first)) {
+          // check the value if it is still considered constant
+          if (logItem.second.isconstantvalue) {
+            const auto value = runObj.getLogAsSingleValue(
+                logItem.first, Mantid::Kernel::Math::TimeAveragedMean);
+            // set the bool to whether the value is the same
+            logItem.second.isconstantvalue = (value == logItem.second.value);
           }
+        } else { // delete it from the list using the name
+          usableLogs.erase(logItem.first);
         }
       }
     }
   }
 
   // Add the log names to the combo box if they appear in all workspaces
-  const int nWorkspaces = m_wsNames.size();
-  for (auto &logCount : logCounts) {
-    if (logCount.second == nWorkspaces) {
-      m_logSelector->addItem(logCount.first.c_str());
+  for (auto &logItem : usableLogs) {
+    if (!(logItem.second.isconstantvalue)) { // values are non-constant
+      m_logSelector->addItem(logItem.first.c_str());
     }
   }
 
diff --git a/qt/widgets/common/src/WorkspacePresenter/WorkspaceTreeWidgetSimple.cpp b/qt/widgets/common/src/WorkspacePresenter/WorkspaceTreeWidgetSimple.cpp
index 0fa4eef0b8671dd803e9874048c2cf3666c38de5..8cd58c9de557b4c6736ff336d21980f03d98f616 100644
--- a/qt/widgets/common/src/WorkspacePresenter/WorkspaceTreeWidgetSimple.cpp
+++ b/qt/widgets/common/src/WorkspacePresenter/WorkspaceTreeWidgetSimple.cpp
@@ -6,14 +6,15 @@
 // SPDX - License - Identifier: GPL - 3.0 +
 #include "MantidQtWidgets/Common/WorkspacePresenter/WorkspaceTreeWidgetSimple.h"
 #include "MantidQtWidgets/Common/MantidTreeModel.h"
-#include <MantidQtWidgets/Common/MantidTreeWidget.h>
-#include <MantidQtWidgets/Common/MantidTreeWidgetItem.h>
+#include "MantidQtWidgets/Common/MantidTreeWidget.h"
+#include "MantidQtWidgets/Common/MantidTreeWidgetItem.h"
 
-#include <MantidAPI/AlgorithmManager.h>
-#include <MantidAPI/FileProperty.h>
-#include <MantidAPI/ITableWorkspace.h>
-#include <MantidAPI/MatrixWorkspace.h>
-#include <MantidAPI/WorkspaceGroup.h>
+#include "MantidAPI/AlgorithmManager.h"
+#include "MantidAPI/FileProperty.h"
+#include "MantidAPI/ITableWorkspace.h"
+#include "MantidAPI/MatrixWorkspace.h"
+#include "MantidAPI/WorkspaceGroup.h"
+#include "MantidGeometry/Instrument.h"
 
 #include <QMenu>
 #include <QSignalMapper>
@@ -85,7 +86,8 @@ void WorkspaceTreeWidgetSimple::popupContextMenu() {
     } catch (Exception::NotFoundError &) {
       return;
     }
-    if (boost::dynamic_pointer_cast<MatrixWorkspace>(workspace)) {
+    if (auto matrixWS =
+            boost::dynamic_pointer_cast<MatrixWorkspace>(workspace)) {
       QMenu *plotSubMenu(new QMenu("Plot", menu));
       plotSubMenu->addAction(m_plotSpectrum);
       plotSubMenu->addAction(m_overplotSpectrum);
@@ -97,6 +99,9 @@ void WorkspaceTreeWidgetSimple::popupContextMenu() {
       menu->addSeparator();
       menu->addAction(m_showData);
       menu->addAction(m_showInstrument);
+      m_showInstrument->setEnabled(
+          matrixWS->getInstrument() &&
+          !matrixWS->getInstrument()->getName().empty());
       menu->addSeparator();
     }
     menu->addAction(m_rename);
diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt
index b3cf15b9d79b71efe3905c1f127feaa2ea5d7226..f7838b680f04ed8535d8daf45960e8d914f7b36f 100644
--- a/scripts/CMakeLists.txt
+++ b/scripts/CMakeLists.txt
@@ -19,13 +19,63 @@ set_property ( TARGET CompileUIErrorReporter PROPERTY FOLDER "CompilePyUI" )
 add_subdirectory ( ExternalInterfaces )
 
 # --------------------------------------------------------------------
-# Testing
+# .pth files
 # --------------------------------------------------------------------
-# All of the following tests (and those in subdirectories) require the
-# test format to import mantid to setup paths to the scripts
-# directory
-set ( PYUNITTEST_TESTRUNNER_IMPORT_MANTID 1 )
+set ( _pth_dirs .
+  Calibration
+  DiamondAttenuationCorrection
+  Diffraction
+  Engineering
+  GSAS-II
+  Inelastic
+  Interface
+  Reflectometry
+  SANS
+  SCD_Reduction
+  TemporaryREF_MScripts
+  Vates
+)
+
+set ( _pth_list_dev )
+set ( _pth_list_install )
+if ( APPLE )
+  set ( _scripts_rel_path "../../scripts" )
+else ()
+  set ( _scripts_rel_path "../scripts" )
+endif()
+
+foreach ( _dir ${_pth_dirs} )
+  list ( APPEND _pth_list_dev "${CMAKE_SOURCE_DIR}/scripts/${_dir}" )
+  list ( APPEND _pth_list_install "${_scripts_rel_path}/${_dir}" )
+endforeach()
+list ( APPEND _pth_list_dev ${MSLICE_DEV} )
+list ( APPEND _pth_list_install "${_scripts_rel_path}/Externalinterfaces" )
+
+# development copy
+set ( _scripts_pth_src "${CMAKE_CURRENT_BINARY_DIR}/mantid-scripts.pth.src" )
+set ( _scripts_pth_dest "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR}/mantid-scripts.pth" )
+string ( REPLACE ";" "\n" _pth_list_dev "${_pth_list_dev}" )
+file ( WRITE ${_scripts_pth_src} "${_pth_list_dev}\n" )
+add_custom_command ( OUTPUT ${_scripts_pth_dest}
+  COMMAND ${CMAKE_COMMAND} -E copy_if_different
+    ${_scripts_pth_src} ${_scripts_pth_dest}
+  DEPENDS ${_scripts_pth_src}
+  COMMENT "Generating scripts .pth file"
+)
+add_custom_target ( ScriptsDotPth ALL DEPENDS ${_scripts_pth_dest} )
+add_dependencies ( PythonInterface ScriptsDotPth )
 
+# install copy
+set ( _scripts_pth_install "${CMAKE_CURRENT_BINARY_DIR}/mantid-scripts.pth.install" )
+string ( REPLACE ";" "\n" _pth_list_install "${_pth_list_install}" )
+file ( WRITE ${_scripts_pth_install} "${_pth_list_install}\n" )
+install ( FILES ${_scripts_pth_install} DESTINATION ${BIN_DIR}
+  RENAME mantid-scripts.pth
+)
+
+# --------------------------------------------------------------------
+# Testing
+# --------------------------------------------------------------------
 set ( TEST_PY_FILES
       test/AbinsAtomsDataTest.py
       test/AbinsCalculateDWSingleCrystalTest.py
@@ -52,6 +102,7 @@ set ( TEST_PY_FILES
       test/IndirectCommonTests.py
       test/ISISDirecInelasticConfigTest.py
       test/PyChopTest.py
+      test/pythonTSVTest.py
       test/ReductionSettingsTest.py
       test/ReductionWrapperTest.py
       test/ReflectometryQuickAuxiliaryTest.py
diff --git a/scripts/ExternalInterfaces/CMakeLists.txt b/scripts/ExternalInterfaces/CMakeLists.txt
index b1156a8446f01c08efd19f1133d79818f07c8feb..dced936ed5047f8b39eae5c8e77209d7f1c6f96b 100644
--- a/scripts/ExternalInterfaces/CMakeLists.txt
+++ b/scripts/ExternalInterfaces/CMakeLists.txt
@@ -14,7 +14,6 @@ ExternalProject_Add ( mslice
   TEST_COMMAND ""
   INSTALL_COMMAND ""
 )
-set ( _mslice_src ${_mslice_external_root}/src/mslice )
 message ( STATUS "Fetching/updating mslice" )
 execute_process ( COMMAND ${CMAKE_COMMAND} ARGS -P ${_mslice_external_root}/tmp/mslice-gitclone.cmake
                   RESULT_VARIABLE _exit_code )
@@ -27,7 +26,10 @@ if ( _exit_code EQUAL 0 )
 else ()
   message ( FATAL_ERROR "Unable to clone mslice" )
 endif()
+# Let the parent lists file know where dev copy of mslice is
+set ( MSLICE_DEV ${_mslice_external_root}/src/mslice PARENT_SCOPE )
 
 # Installation
+# MSLICE_DEV is only set in PARENT_SCOPE!
 install ( DIRECTORY ${_mslice_external_root}/src/mslice/mslice/
           DESTINATION ${INBUNDLE}scripts/ExternalInterfaces/mslice )
diff --git a/scripts/Frequency_Domain_Analysis.py b/scripts/Frequency_Domain_Analysis.py
index f7c6c7311a9eb692feaf2eefca0ef078c024f9fc..3d4261ed50989072f6330c9287cd61db306d3698 100644
--- a/scripts/Frequency_Domain_Analysis.py
+++ b/scripts/Frequency_Domain_Analysis.py
@@ -12,7 +12,7 @@ import sys
 import PyQt4.QtGui as QtGui
 
 from Muon.GUI.FrequencyDomainAnalysis.Transform.transform_widget import TransformWidget
-from Muon.GUI.Common import load_utils
+from Muon.GUI.Common.utilities import load_utils
 from Muon.GUI.Common import message_box
 
 
diff --git a/scripts/Interface/reduction_gui/widgets/diffraction/diffraction_run_setup.py b/scripts/Interface/reduction_gui/widgets/diffraction/diffraction_run_setup.py
index 64fe2123cc12757635d14bed283bef76c34d47f7..27fb1f739eec516fbcecd843e891676ceaa3435c 100644
--- a/scripts/Interface/reduction_gui/widgets/diffraction/diffraction_run_setup.py
+++ b/scripts/Interface/reduction_gui/widgets/diffraction/diffraction_run_setup.py
@@ -154,9 +154,8 @@ class RunSetupWidget(BaseWidget):
 
         self._content.help_button.clicked.connect(self._show_help)
 
-        # Validated widgets
-
-        return
+        # mutex for whether to update the linear/log drop-down
+        self._content._binning_edit_mutex = False
 
     def set_state(self, state):
         """ Populate the UI elements with the data from the given state.
@@ -170,7 +169,26 @@ class RunSetupWidget(BaseWidget):
         self._content.lineEdit_expIniFile.setText(state.exp_ini_file_name)
         self._content.charfile_edit.setText(state.charfilename)
         self._content.sum_checkbox.setChecked(state.dosum)
-        # Set binning type (logarithm or linear)
+
+        # Set binning parameter
+        try:
+            binning_float = float(state.binning)
+            binning_str = '%.6f' % abs(binning_float)
+        except ValueError:
+            binning_str = str(state.binning)
+        self._content._binning_edit_mutex = True
+        self._content.binning_edit.setText(binning_str)
+        # Set ResampleX
+        try:
+            resamplex_i = int(state.resamplex)
+            resamplex_str = '%d' % abs(resamplex_i)
+        except ValueError:
+            resamplex_str = str(state.resamplex)
+        self._content.resamplex_edit.setText(resamplex_str)
+        self._content._binning_edit_mutex = False
+
+        # Set binning type (logarithm=1 or linear=0) - must be done after the
+        # binning/resamplex boxes are set or it will be lost
         bintype_index = 1
         if state.doresamplex is True:
             # resample x
@@ -189,20 +207,6 @@ class RunSetupWidget(BaseWidget):
         # END-IF-ELSE
         self._content.bintype_combo.setCurrentIndex(bintype_index)
 
-        # Set binning parameter
-        try:
-            binning_float = float(state.binning)
-            binning_str = '%.6f' % abs(binning_float)
-        except ValueError:
-            binning_str = str(state.binning)
-        self._content.binning_edit.setText(binning_str)
-        # Set ResampleX
-        try:
-            resamplex_i = int(state.resamplex)
-            resamplex_str = '%d' % abs(resamplex_i)
-        except ValueError:
-            resamplex_str = str(state.resamplex)
-        self._content.resamplex_edit.setText(resamplex_str)
         # Others
         self._content.binind_checkbox.setChecked(state.binindspace)
         self._content.outputdir_edit.setText(state.outputdir)
@@ -307,8 +311,6 @@ class RunSetupWidget(BaseWidget):
         if fname:
             self._content.calfile_edit.setText(fname)
 
-        return
-
     def _charfile_browse(self):
         """ Event handing for browsing calibration file
         """
@@ -316,8 +318,6 @@ class RunSetupWidget(BaseWidget):
         if fname:
             self._content.charfile_edit.setText(','.join(fname))
 
-        return
-
     def _groupfile_browse(self):
         ''' Event handling for browsing for a grouping file
         '''
@@ -325,8 +325,6 @@ class RunSetupWidget(BaseWidget):
         if fname:
             self._content.groupfile_edit.setText(fname)
 
-        return
-
     def do_browse_ini_file(self):
         """ Event handling for browsing Exp Ini file
         :return:
@@ -335,8 +333,6 @@ class RunSetupWidget(BaseWidget):
         if exp_ini_file_name:
             self._content.lineEdit_expIniFile.setText(exp_ini_file_name)
 
-        return
-
     def _outputdir_browse(self):
         """ Event handling for browing output directory
         """
@@ -344,8 +340,6 @@ class RunSetupWidget(BaseWidget):
         if dirname:
             self._content.outputdir_edit.setText(dirname)
 
-        return
-
     def _binvalue_edit(self):
         """ Handling event for binning value changed
         """
@@ -358,11 +352,11 @@ class RunSetupWidget(BaseWidget):
         else:
             self._content.bintype_combo.setCurrentIndex(0)
 
-        return
-
     def _bintype_process(self):
         """ Handling bin type changed
         """
+        if self._content._binning_edit_mutex:
+            return
         currindex = self._content.bintype_combo.currentIndex()
         curbinning = self._content.binning_edit.text()
         if curbinning != "" and curbinning is not None:
@@ -374,8 +368,6 @@ class RunSetupWidget(BaseWidget):
             #ENDIFELSE
         #ENDIF
 
-        return
-
     def validateIntegerList(self, intliststring):
         """ Validate whether the string can be divided into integer strings.
         Allowed: a, b, c-d, e, f, g:h
diff --git a/scripts/Interface/ui/sans_isis/__init__.py b/scripts/Interface/ui/sans_isis/__init__.py
index 17a894e3f9249dd4edeec240f5970db9a9fe5bd8..d2cfb75232dfb59fac674e2b49dba356e18ebbc1 100644
--- a/scripts/Interface/ui/sans_isis/__init__.py
+++ b/scripts/Interface/ui/sans_isis/__init__.py
@@ -4,19 +4,3 @@
 #     NScD Oak Ridge National Laboratory, European Spallation Source
 #     & Institut Laue - Langevin
 # SPDX - License - Identifier: GPL - 3.0 +
-# Note that this a workaround
-# When introduced? During the Python2 to Python3 conversion
-# Why introduced? The autogenerated file ui_XXX.py contains a line
-#                 from XXXX import YYYY
-#                 which should be
-#                 from .XXXX import YYYY
-#                 for Python3, but since it is autogenerated we cannot change
-#                 this easily.
-import os
-import sys
-
-import mantid
-
-path = mantid.config["pythonscripts.directories"].split(";")[0]
-path = os.path.join(path[:path.index("scripts")], "scripts", "Interface", "ui", "sans_isis")
-sys.path.append(path)
diff --git a/scripts/Interface/ui/sans_isis/add_runs_page.py b/scripts/Interface/ui/sans_isis/add_runs_page.py
index 1d492b06ec73a4bdfeb9b3e61d7026140164695c..eebb3c8769626c8871d6d7a722bf49a036750b63 100644
--- a/scripts/Interface/ui/sans_isis/add_runs_page.py
+++ b/scripts/Interface/ui/sans_isis/add_runs_page.py
@@ -4,9 +4,11 @@
 #     NScD Oak Ridge National Laboratory, European Spallation Source
 #     & Institut Laue - Langevin
 # SPDX - License - Identifier: GPL - 3.0 +
+from __future__ import absolute_import
+
 from PyQt4 import QtGui
 from PyQt4.QtCore import pyqtSignal
-import ui_add_runs_page
+from . import ui_add_runs_page
 
 
 class AddRunsPage(QtGui.QWidget, ui_add_runs_page.Ui_AddRunsPage):
diff --git a/scripts/Interface/ui/sans_isis/beam_centre.py b/scripts/Interface/ui/sans_isis/beam_centre.py
index 4e851a4db09aad2b8238ead794c543bb51f843d1..7746bf68d5c688d9b4422703444d3b6510c16158 100644
--- a/scripts/Interface/ui/sans_isis/beam_centre.py
+++ b/scripts/Interface/ui/sans_isis/beam_centre.py
@@ -10,7 +10,7 @@ from abc import ABCMeta, abstractmethod
 
 from PyQt4 import QtGui, QtCore
 from six import with_metaclass
-import ui_beam_centre
+from . import ui_beam_centre
 from mantidqtpython import MantidQt
 from sans.gui_logic.gui_common import get_detector_from_gui_selection, \
     get_detector_strings_for_gui, get_string_for_gui_from_reduction_mode
diff --git a/scripts/Interface/ui/sans_isis/diagnostics_page.py b/scripts/Interface/ui/sans_isis/diagnostics_page.py
index 8887fc8827a6c6eab42b7877c4e760e13acabba1..f59ea65a73ece7c211c2a5bba99f886883c406ba 100644
--- a/scripts/Interface/ui/sans_isis/diagnostics_page.py
+++ b/scripts/Interface/ui/sans_isis/diagnostics_page.py
@@ -10,7 +10,7 @@ from abc import ABCMeta, abstractmethod
 
 from PyQt4 import QtGui, QtCore
 from six import with_metaclass
-import ui_diagnostics_page
+from . import ui_diagnostics_page
 from sans.gui_logic.gui_common import (load_file, GENERIC_SETTINGS)
 
 try:
diff --git a/scripts/Interface/ui/sans_isis/masking_table.py b/scripts/Interface/ui/sans_isis/masking_table.py
index 2a6b305fb18a21108d3a7d1bef5537327f138ca7..14143323350f200fc43dd557e2105302b91b28cf 100644
--- a/scripts/Interface/ui/sans_isis/masking_table.py
+++ b/scripts/Interface/ui/sans_isis/masking_table.py
@@ -16,7 +16,7 @@ from abc import ABCMeta, abstractmethod
 
 from PyQt4 import QtGui
 from six import with_metaclass
-import ui_masking_table
+from . import ui_masking_table
 
 
 class MaskingTable(QtGui.QWidget, ui_masking_table.Ui_MaskingTable):
diff --git a/scripts/Interface/ui/sans_isis/run_selector_widget.py b/scripts/Interface/ui/sans_isis/run_selector_widget.py
index 63110fbefb7474544fd34c1d059e658bf4189368..b32530558fa82ff682f0819def89199a99eaa955 100644
--- a/scripts/Interface/ui/sans_isis/run_selector_widget.py
+++ b/scripts/Interface/ui/sans_isis/run_selector_widget.py
@@ -8,7 +8,7 @@ from __future__ import (absolute_import, division, print_function)
 
 from PyQt4 import QtGui, QtCore
 
-import ui_run_selector_widget
+from . import ui_run_selector_widget
 from PyQt4.QtCore import pyqtSignal
 from mantidqtpython import MantidQt
 
diff --git a/scripts/Interface/ui/sans_isis/sans_data_processor_gui.py b/scripts/Interface/ui/sans_isis/sans_data_processor_gui.py
index a0b6b79777fe7dce37a250d380bd4c89be0f4e4e..02991dd007279110f16a5084361f889a4bf11ff5 100644
--- a/scripts/Interface/ui/sans_isis/sans_data_processor_gui.py
+++ b/scripts/Interface/ui/sans_isis/sans_data_processor_gui.py
@@ -10,16 +10,15 @@
 
 from __future__ import (absolute_import, division, print_function)
 
-import os
 from abc import ABCMeta, abstractmethod
 from inspect import isclass
 
 from six import with_metaclass
-from qtpy.QtWidgets import (QListWidget, QListWidgetItem, QMainWindow, QMessageBox)  # noqa
+from qtpy.QtWidgets import (QListWidgetItem, QMainWindow, QMessageBox)  # noqa
 from qtpy.QtCore import (QRegExp, QSettings)  # noqa
 from qtpy.QtGui import (QDoubleValidator, QIcon, QIntValidator, QRegExpValidator)  # noqa
 
-from mantid.kernel import (Logger, config)
+from mantid.kernel import (Logger)
 from mantidqtpython import MantidQt
 
 try:
@@ -35,10 +34,7 @@ from sans.common.enums import (ReductionDimensionality, OutputMode, SaveType, SA
 from sans.common.file_information import SANSFileInformationFactory
 from sans.gui_logic.gui_common import (get_reduction_mode_from_gui_selection, get_reduction_mode_strings_for_gui,
                                        get_string_for_gui_from_reduction_mode, GENERIC_SETTINGS, load_file,
-                                       load_default_file, set_setting, get_instrument_from_gui_selection,
-                                       get_string_for_gui_from_instrument)
-
-from sans.common.general_functions import get_instrument
+                                       load_default_file, set_setting, get_instrument_from_gui_selection)
 
 from sans.gui_logic.models.run_summation import RunSummation
 from sans.gui_logic.models.run_selection import RunSelection
@@ -107,6 +103,10 @@ class SANSDataProcessorGui(QMainWindow, ui_sans_data_processor_window.Ui_SansDat
         def on_processed_clicked(self):
             pass
 
+        @abstractmethod
+        def on_load_clicked(self):
+            pass
+
         @abstractmethod
         def on_multi_period_selection(self, show_periods):
             pass
@@ -169,7 +169,6 @@ class SANSDataProcessorGui(QMainWindow, ui_sans_data_processor_window.Ui_SansDat
         self.__generic_settings = GENERIC_SETTINGS
         self.__path_key = "sans_path"
         self.__user_file_key = "user_file"
-        self.__instrument_name = "sans_instrument"
         self.__mask_file_input_path_key = "mask_files"
 
         # Logger
@@ -181,14 +180,8 @@ class SANSDataProcessorGui(QMainWindow, ui_sans_data_processor_window.Ui_SansDat
                                                                   SANSInstrument.LOQ,
                                                                   SANSInstrument.LARMOR,
                                                                   SANSInstrument.ZOOM]])
-        settings = QSettings()
-        settings.beginGroup(self.__generic_settings)
-        instrument_name = settings.value(self.__instrument_name,
-                                         SANSInstrument.to_string(SANSInstrument.NoInstrument),
-                                         type=str)
-        settings.endGroup()
 
-        self.instrument = SANSInstrument.from_string(instrument_name)
+        self.instrument = SANSInstrument.NoInstrument
 
         self.paste_button.setIcon(QIcon(":/paste.png"))
         self.copy_button.setIcon(QIcon(":/copy.png"))
@@ -274,8 +267,6 @@ class SANSDataProcessorGui(QMainWindow, ui_sans_data_processor_window.Ui_SansDat
         # Set the 0th row enabled
         self.tab_choice_list.setCurrentRow(0)
 
-        self._setup_add_runs_page()
-
         # --------------------------------------------------------------------------------------------------------------
         # Main Tab
         # --------------------------------------------------------------------------------------------------------------
@@ -288,10 +279,10 @@ class SANSDataProcessorGui(QMainWindow, ui_sans_data_processor_window.Ui_SansDat
 
         self.wavelength_step_type_combo_box.currentIndexChanged.connect(self._on_wavelength_step_type_changed)
 
-        self.instrument_combo_box.currentIndexChanged.connect(self._instrument_changed)
-
         self.process_button.clicked.connect(self._processed_clicked)
 
+        self.load_botton.clicked.connect(self._load_clicked)
+
         self.help_button.clicked.connect(self._on_help_button_clicked)
 
         # --------------------------------------------------------------------------------------------------------------
@@ -388,14 +379,6 @@ class SANSDataProcessorGui(QMainWindow, ui_sans_data_processor_window.Ui_SansDat
         else:
             self.hide_period_columns()
 
-        # Set the list of available instruments in the widget and the default instrument
-        instrument_name = config.getString("default.instrument")
-        instrument_name_enum = get_instrument(instrument_name)
-
-        if instrument_name_enum:
-            self.set_instrument_settings(instrument_name_enum)
-            self._instrument_changed()
-
         self.data_processor_widget_layout.addWidget(self.data_processor_table)
         self.table_signals.cellTextChanged.connect(self._data_changed)
         self.table_signals.rowInserted.connect(self._row_inserted)
@@ -430,6 +413,9 @@ class SANSDataProcessorGui(QMainWindow, ui_sans_data_processor_window.Ui_SansDat
         """
         self._call_settings_listeners(lambda listener: listener.on_processed_clicked())
 
+    def _load_clicked(self):
+        self._call_settings_listeners(lambda listener: listener.on_load_clicked())
+
     def _processing_finished(self):
         """
         Clean up
@@ -508,35 +494,25 @@ class SANSDataProcessorGui(QMainWindow, ui_sans_data_processor_window.Ui_SansDat
                   self.get_batch_file_path)
         self._call_settings_listeners(lambda listener: listener.on_batch_file_load())
 
-    def _set_mantid_instrument(self, instrument_string):
-        # Add the instrument to the settings
-        settings = QSettings()
-        settings.beginGroup(self.__generic_settings)
-        settings.setValue(self.__instrument_name, instrument_string)
-        settings.endGroup()
-
-        # Set the default instrument on Mantid
-        config.setFacility("ISIS")
-        config.setString("default.instrument", instrument_string)
-
-    def _handle_instrument_change(self):
-        instrument_string = str(self.data_processor_table.getCurrentInstrument())
-        instrument = get_instrument_from_gui_selection(instrument_string)
-        self.instrument = instrument
-
     def disable_buttons(self):
         self.process_button.setEnabled(False)
-        self.instrument_combo_box.setEnabled(False)
         self.batch_button.setEnabled(False)
         self.user_file_button.setEnabled(False)
         self.manage_directories_button.setEnabled(False)
+        self.load_botton.setEnabled(False)
 
     def enable_buttons(self):
         self.process_button.setEnabled(True)
-        self.instrument_combo_box.setEnabled(True)
         self.batch_button.setEnabled(True)
         self.user_file_button.setEnabled(True)
         self.manage_directories_button.setEnabled(True)
+        self.load_botton.setEnabled(True)
+
+    def disable_process_buttons(self):
+        self.process_button.setEnabled(False)
+
+    def enable_process_buttons(self):
+        self.process_button.setEnabled(True)
 
     def display_message_box(self, title, message, details):
         msg = QMessageBox()
@@ -752,12 +728,12 @@ class SANSDataProcessorGui(QMainWindow, ui_sans_data_processor_window.Ui_SansDat
     # ------------------------------------------------------------------------------------------------------------------
     def set_instrument_settings(self, instrument):
         if instrument:
-            self.instrument = instrument
-            instrument_string = SANSInstrument.to_string(instrument)
-            self._set_mantid_instrument(instrument_string)
             reduction_mode_list = get_reduction_mode_strings_for_gui(instrument)
             self.set_reduction_modes(reduction_mode_list)
 
+            if instrument != SANSInstrument.NoInstrument:
+                self._setup_add_runs_page()
+
     def update_gui_combo_box(self, value, expected_type, combo_box):
         # There are two types of values that can be passed:
         # Lists: we set the combo box to the values in the list
@@ -861,6 +837,14 @@ class SANSDataProcessorGui(QMainWindow, ui_sans_data_processor_window.Ui_SansDat
     def zero_error_free(self, value):
         self.save_zero_error_free.setChecked(value)
 
+    @property
+    def save_can(self):
+        return self.save_can_checkBox.isChecked()
+
+    @save_can.setter
+    def save_can(self, value):
+        self.save_can_checkBox.setChecked(value)
+
     @property
     def progress_bar_minimum(self):
         return self.batch_progress_bar.minimum()
@@ -943,24 +927,13 @@ class SANSDataProcessorGui(QMainWindow, ui_sans_data_processor_window.Ui_SansDat
 
     @property
     def instrument(self):
-        instrument_as_string = self.instrument_combo_box.currentText()
-        return get_instrument_from_gui_selection(instrument_as_string)
+        return get_instrument_from_gui_selection(self.instrument_type.text())
 
     @instrument.setter
     def instrument(self, value):
-        instrument_as_string = get_string_for_gui_from_instrument(value)
-        if instrument_as_string:
-            index = self.instrument_combo_box.findText(instrument_as_string)
-            if index != -1:
-                self.instrument_combo_box.setCurrentIndex(index)
-
-    def set_instruments(self, instrument_list):
-        current_index = self.instrument_combo_box.currentIndex()
-        self.instrument_combo_box.clear()
-        for element in instrument_list:
-            self.instrument_combo_box.addItem(element)
-        if current_index != -1:
-            self.instrument_combo_box.setCurrentIndex(current_index)
+        instrument_string = SANSInstrument.to_string(value)
+        self.instrument_type.setText("{}".format(instrument_string))
+        self._instrument_changed()
 
     # ==================================================================================================================
     # ==================================================================================================================
diff --git a/scripts/Interface/ui/sans_isis/sans_data_processor_window.ui b/scripts/Interface/ui/sans_isis/sans_data_processor_window.ui
index 5abd7d61b8610acf61b58da095b130728a11be0e..88b83c11430b093df8e1897afd0e8bb876764b6c 100644
--- a/scripts/Interface/ui/sans_isis/sans_data_processor_window.ui
+++ b/scripts/Interface/ui/sans_isis/sans_data_processor_window.ui
@@ -103,6 +103,9 @@ QGroupBox::title {
            <layout class="QVBoxLayout" name="run_layout">
             <item>
              <layout class="QGridLayout" name="header">
+              <property name="verticalSpacing">
+               <number>6</number>
+              </property>
               <item row="1" column="1">
                <widget class="QLabel" name="user_file_label">
                 <property name="toolTip">
@@ -122,18 +125,9 @@ QGroupBox::title {
               <item row="0" column="4">
                <layout class="QHBoxLayout" name="horizontalLayout_2">
                 <item>
-                 <widget class="QComboBox" name="instrument_combo_box">
-                  <property name="sizePolicy">
-                   <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
-                    <horstretch>0</horstretch>
-                    <verstretch>0</verstretch>
-                   </sizepolicy>
-                  </property>
-                  <property name="maximumSize">
-                   <size>
-                    <width>834</width>
-                    <height>16777215</height>
-                   </size>
+                 <widget class="QLabel" name="instrument_type">
+                  <property name="text">
+                   <string>NoInstrument</string>
                   </property>
                  </widget>
                 </item>
@@ -231,6 +225,13 @@ QGroupBox::title {
                 </property>
                </widget>
               </item>
+              <item row="4" column="5">
+               <widget class="QPushButton" name="load_botton">
+                <property name="text">
+                 <string>Load</string>
+                </property>
+               </widget>
+              </item>
              </layout>
             </item>
             <item>
@@ -456,6 +457,13 @@ QGroupBox::title {
                        </property>
                       </widget>
                      </item>
+                     <item>
+                      <widget class="QCheckBox" name="save_can_checkBox">
+                       <property name="text">
+                        <string>Save Can</string>
+                       </property>
+                      </widget>
+                     </item>
                     </layout>
                    </item>
                    <item>
@@ -805,8 +813,11 @@ QGroupBox::title {
                   </item>
                   <item>
                    <widget class="QGroupBox" name="event_binning_group_box">
+                    <property name="enabled">
+                     <bool>true</bool>
+                    </property>
                     <property name="toolTip">
-                     <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;When checked, the data reduction will run in the compatibility mode. This means that the reduction workflow is equivalent to the previous legacy reduction workflow. Most of the time you don't want to have this checked as the unchecked state will provide more precise results.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+                     <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;When checked, the data reduction will run in the compatibility mode. This means that the reduction workflow is equivalent to the previous legacy reduction workflow. &lt;/p&gt;&lt;p&gt;ONLY UNCHECK THIS IF NOT PERFORMING BIN MASKING&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
                     </property>
                     <property name="title">
                      <string>&amp;Use compatibility mode</string>
@@ -815,11 +826,14 @@ QGroupBox::title {
                      <bool>true</bool>
                     </property>
                     <property name="checked">
-                     <bool>false</bool>
+                     <bool>true</bool>
                     </property>
                     <layout class="QGridLayout" name="gridLayout_16">
                      <item row="0" column="0">
                       <widget class="QLabel" name="event_binning_label">
+                       <property name="enabled">
+                        <bool>true</bool>
+                       </property>
                        <property name="toolTip">
                         <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Rebin string for the initial time-of-flight workspace. This is only needed when using the compaptibility mode (ie when following the old reduction model). Note that this will only have an effect on event workspaces.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
                        </property>
@@ -830,6 +844,9 @@ QGroupBox::title {
                      </item>
                      <item row="0" column="1">
                       <widget class="QLineEdit" name="event_binning_line_edit">
+                       <property name="enabled">
+                        <bool>true</bool>
+                       </property>
                        <property name="toolTip">
                         <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Rebin string for the initial time-of-flight workspace. This is only needed when using the compaptibility mode (ie when following the old reduction model). Note that this will only have an effect on event workspaces.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
                        </property>
@@ -2215,7 +2232,6 @@ QGroupBox::title {
   </customwidget>
  </customwidgets>
  <tabstops>
-  <tabstop>instrument_combo_box</tabstop>
   <tabstop>manage_directories_button</tabstop>
   <tabstop>user_file_button</tabstop>
   <tabstop>batch_button</tabstop>
diff --git a/scripts/Interface/ui/sans_isis/settings_diagnostic_tab.py b/scripts/Interface/ui/sans_isis/settings_diagnostic_tab.py
index 91b0d39cf84cb3db63d6f6986bae38cc97fbbe52..7c5b924a3a62bf2b505813628cd66f1b026f6f0d 100644
--- a/scripts/Interface/ui/sans_isis/settings_diagnostic_tab.py
+++ b/scripts/Interface/ui/sans_isis/settings_diagnostic_tab.py
@@ -21,7 +21,7 @@ from six import with_metaclass
 from PyQt4 import QtGui
 
 from sans.gui_logic.gui_common import (GENERIC_SETTINGS, JSON_SUFFIX, load_file)
-import ui_settings_diagnostic_tab
+from . import ui_settings_diagnostic_tab
 
 if six.PY3:
     unicode = str
diff --git a/scripts/Interface/ui/sans_isis/summation_settings_widget.py b/scripts/Interface/ui/sans_isis/summation_settings_widget.py
index 07beaf43ec46e40980dab793c3e67f24b1b775f7..caf7e4e3d3f7dd12b6af478c2bd911ea4cad61ae 100644
--- a/scripts/Interface/ui/sans_isis/summation_settings_widget.py
+++ b/scripts/Interface/ui/sans_isis/summation_settings_widget.py
@@ -9,7 +9,7 @@ from __future__ import (absolute_import, division, print_function)
 from PyQt4 import QtGui
 from PyQt4.QtCore import pyqtSignal
 
-import ui_summation_settings_widget
+from . import ui_summation_settings_widget
 from sans.gui_logic.models.binning_type import BinningType
 
 
diff --git a/scripts/Interface/ui/sans_isis/work_handler.py b/scripts/Interface/ui/sans_isis/work_handler.py
index a093cd5142b5e9c5318f1a66588a8ce399d084f5..3ad548b5612a0188a789b97a73266d1e1cd66d1d 100644
--- a/scripts/Interface/ui/sans_isis/work_handler.py
+++ b/scripts/Interface/ui/sans_isis/work_handler.py
@@ -14,14 +14,16 @@ The "worker" handles running the function via a unique process ID; "listeners" m
 each process which are then notified upon certain actions (such as an error being thrown by the
 worker, or the worker finishing its task) using the nested class WorkListener.
 """
+from __future__ import absolute_import
 
 from PyQt4.QtCore import pyqtSlot, QThreadPool
 from abc import ABCMeta, abstractmethod
 from six import with_metaclass
-from worker import Worker
 import functools
 import uuid
 
+from .worker import Worker
+
 
 class WorkHandler(object):
     """
diff --git a/scripts/Muon/GUI/Common/context_example/context_example_model.py b/scripts/Muon/GUI/Common/context_example/context_example_model.py
index 5f860dc3274e4b1395e360c179b2080864070eea..e2a5338f5a4f0994fc666fe07ffcab5922141438 100644
--- a/scripts/Muon/GUI/Common/context_example/context_example_model.py
+++ b/scripts/Muon/GUI/Common/context_example/context_example_model.py
@@ -21,11 +21,21 @@ class ContextExampleModel(object):
     def getSubContext(self):
         subContext = {}
         group_names = []
+        group_dets = []
         tmp = self._context.get(Groups)
         for group in tmp:
             group_names.append(group.name)
+            group_dets.append(group.dets)
         subContext["Group Names"] = group_names
+        subContext["Group dets"] = group_dets
+
         pair = self._context.get(Pairs)[0]  # there is only one
+        # do some validation on pairs
+        if pair.FGroup not in group_names:
+            pair.setFGroup(group_names[0])
+        if pair.BGroup not in group_names:
+            pair.setBGroup(group_names[1])
+
         subContext["Pair_F"] = pair.FGroup
         subContext["Pair_B"] = pair.BGroup
         subContext["Pair_alpha"] = pair.alpha
@@ -34,14 +44,19 @@ class ContextExampleModel(object):
 
     def updateContext(self, subContext):
         group_names = subContext["Group Names"]
+        group_dets = subContext["Group dets"]
         groups = []
-        for name in group_names:
-            groups.append(group_object.group(name))
+        for k in range(len(group_names)):
+            groups.append(group_object.group(group_names[k], group_dets[k]))
         self._context.set(Groups, groups)
 
         alpha = subContext["Pair_alpha"]
         F_group = subContext["Pair_F"]
+        if F_group not in group_names:
+            F_group = group_names[0]
         B_group = subContext["Pair_B"]
+        if B_group not in group_names:
+            B_group = group_names[0]
         name = "pair test"
         pair = pair_object.pair(name, F_group, B_group, alpha)
         self._context.set(Pairs, [pair])
diff --git a/scripts/Muon/GUI/Common/context_example/context_example_view.py b/scripts/Muon/GUI/Common/context_example/context_example_view.py
index fe249aad739bd1b20352c7d5c5ee6f2925f9b6a3..c06283ec817f1f84392a68705a314f1fc742dd4e 100644
--- a/scripts/Muon/GUI/Common/context_example/context_example_view.py
+++ b/scripts/Muon/GUI/Common/context_example/context_example_view.py
@@ -95,6 +95,9 @@ class ContextExampleView(QtGui.QWidget):
         self.ws1.setText(group_name[1])
         self.ws2.setText(group_name[2])
 
+        #store the detectors
+        self.dets = context["Group dets"]
+
         # update combo boxes (clear and repopulate)
         self.g1.clear()
         self.g2.clear()
@@ -120,6 +123,7 @@ class ContextExampleView(QtGui.QWidget):
         context = {}
         context["Group Names"] = [
             self.ws0.text(), self.ws1.text(), self.ws2.text()]
+        context["Group dets"] = self.dets
         context["Pair_F"] = str(self.g1.currentText())
         context["Pair_B"] = str(self.g2.currentText())
         context["Pair_alpha"] = float(self.alpha.text())
diff --git a/scripts/Muon/GUI/Common/context_example/context_example_widget.py b/scripts/Muon/GUI/Common/context_example/context_example_widget.py
index ca9f70084a5e1fe2fc3fb4e7c1c56778fbaabd29..750271218e6379d27c8649307d4a34997408c6f7 100644
--- a/scripts/Muon/GUI/Common/context_example/context_example_widget.py
+++ b/scripts/Muon/GUI/Common/context_example/context_example_widget.py
@@ -47,7 +47,7 @@ class ContextExampleWidget(object):
     def updateContext(self):
         self._presenter.updateContext()
 
-    def loadFromContext(self, context):
+    def loadFromContext(self):
         # extract relevant info from context via model
         model = self._presenter.model
         sub_context = model.getSubContext()
diff --git a/scripts/Muon/GUI/Common/dummy_label/dummy_label_widget.py b/scripts/Muon/GUI/Common/dummy_label/dummy_label_widget.py
index b2e518b23a3b5b86a15eac7a8939d8181cc116e8..a1d12aaf1aa28732894b3274aa52924b9d800eb7 100644
--- a/scripts/Muon/GUI/Common/dummy_label/dummy_label_widget.py
+++ b/scripts/Muon/GUI/Common/dummy_label/dummy_label_widget.py
@@ -33,7 +33,7 @@ class DummyLabelWidget(object):
     def updateContext(self):
         self._presenter.updateContext()
 
-    def loadFromContext(self, context):
+    def loadFromContext(self):
         # extract relevant info from context via model
         model = self._presenter.model
         sub_context = model.getSubContext()
diff --git a/scripts/Muon/GUI/Common/group_object.py b/scripts/Muon/GUI/Common/group_object.py
index 089cf2f0da880923675c4a0c554a5fd7c4d49823..d4fe34107223e81a00d3022c29a26be2fd690513 100644
--- a/scripts/Muon/GUI/Common/group_object.py
+++ b/scripts/Muon/GUI/Common/group_object.py
@@ -5,6 +5,9 @@
 #     & Institut Laue - Langevin
 # SPDX - License - Identifier: GPL - 3.0 +
 
+from __future__ import (absolute_import, division, print_function)
+import pythonTSV as TSVHelper
+
 
 class group(object):
 
@@ -16,6 +19,13 @@ class group(object):
     def name(self):
         return self._name
 
+    @property
+    def dets(self):
+        return self._dets
+
+    def Print(self):
+        print(self._name, self._dets)
+
     def setName(self, name):
         self._name = name
 
@@ -27,3 +37,18 @@ class group(object):
             return True
 
         return False
+
+    def save(self, TSV):
+        TSVHelper.writeLine(TSV, self._name)
+        TSV.storeInt(len(self._dets))
+        for detector in self._dets:
+            TSV.storeInt(detector)
+
+    def load(self, TSV, name):
+        self._name = name
+        safeName = TSVHelper.makeLineNameSafe(name)
+        TSV.selectLine(safeName)
+        num = TSV.readInt()
+        self._dets = []
+        for k in range(num):
+            self._dets.append(TSV.readInt())
diff --git a/scripts/Muon/GUI/Common/load_utils.py b/scripts/Muon/GUI/Common/load_utils.py
deleted file mode 100644
index f2abef92b58bab318e50cb9be52a587e7eb3a803..0000000000000000000000000000000000000000
--- a/scripts/Muon/GUI/Common/load_utils.py
+++ /dev/null
@@ -1,93 +0,0 @@
-# Mantid Repository : https://github.com/mantidproject/mantid
-#
-# Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
-#     NScD Oak Ridge National Laboratory, European Spallation Source
-#     & Institut Laue - Langevin
-# SPDX - License - Identifier: GPL - 3.0 +
-from __future__ import (absolute_import, division, print_function)
-import mantid.simpleapi as mantid
-
-
-class LoadUtils(object):
-    """
-    A simple class for identifing the current run
-    and it can return the name, run and instrument.
-    The current run is the same as the one in MonAnalysis
-    """
-    def __init__(self, parent=None):
-        exists,tmpWS = self.MuonAnalysisExists()
-        if exists:
-            self.setUp(tmpWS)
-        else:
-            raise RuntimeError("No data loaded. \n Please load data using Muon Analysis")
-
-    def setUp(self,tmpWS):
-        # get everything from the ADS
-        self.options = mantid.AnalysisDataService.getObjectNames()
-        self.options = [item.replace(" ","") for item in self.options]
-        self.N_points = len(tmpWS.readX(0))
-        self.instrument=tmpWS.getInstrument().getName()
-        self.runName=self.instrument+str(tmpWS.getRunNumber()).zfill(8)
-
-    # get methods
-    def getNPoints(self):
-        return self.N_points
-
-    def getCurrentWS(self):
-        return self.runName, self.options
-
-    def getRunName(self):
-        return self.runName
-
-    def getInstrument(self):
-        return self.instrument
-
-    # check if data matches current
-    def digit(self,x):
-        return int(filter(str.isdigit,x) or 0)
-
-    def hasDataChanged(self):
-        exists,ws = self.MuonAnalysisExists()
-        if exists:
-            current = ws.getInstrument().getName()+str(ws.getRunNumber()).zfill(8)
-            if self.runName != current:
-                mantid.logger.error("Active workspace has changed. Reloading the data")
-                self.setUp(ws)
-                return True
-        return False
-
-    # check if muon analysis exists
-    def MuonAnalysisExists(self):
-        # if period data look for the first period
-        if mantid.AnalysisDataService.doesExist("MuonAnalysis_1"):
-            tmpWS=mantid.AnalysisDataService.retrieve("MuonAnalysis_1")
-            return True, tmpWS
-            # if its not period data
-        elif mantid.AnalysisDataService.doesExist("MuonAnalysis"):
-            tmpWS=mantid.AnalysisDataService.retrieve("MuonAnalysis")
-            return True,tmpWS
-        else:
-            return False,None
-
-    # Get the groups/pairs for active WS
-    # ignore raw files
-    def getWorkspaceNames(self):
-        # gets all WS in the ADS
-        runName,options = self.getCurrentWS()
-        final_options=[]
-        # only keep the relevant WS (same run as Muon Analysis)
-        for pick in options:
-            if ";" in pick and "Raw" not in pick and runName in pick:
-                final_options.append(pick)
-        return final_options
-
-    # Get the groups/pairs for active WS
-    def getGroupedWorkspaceNames(self):
-        # gets all WS in the ADS
-        runName,options = self.getCurrentWS()
-        final_options=[]
-        # only keep the relevant WS (same run as Muon Analysis)
-        for pick in options:
-            if "MuonAnalysisGrouped_" in pick and ";" not in pick:
-                final_options.append(pick)
-        return final_options
diff --git a/scripts/Muon/GUI/Common/message_box.py b/scripts/Muon/GUI/Common/message_box.py
index f74a942970565b9523ec2a4020d66a3bca7bf6bf..95539fe2338f2b9585cf001546d06ceb582a431c 100644
--- a/scripts/Muon/GUI/Common/message_box.py
+++ b/scripts/Muon/GUI/Common/message_box.py
@@ -7,7 +7,7 @@
 import PyQt4.QtGui as QtGui
 
 
-def warning(error):
-
-    ex = QtGui.QWidget()
-    QtGui.QMessageBox.warning(ex, "Error", str(error))
+def warning(error, parent=None):
+    if not parent:
+        parent = QtGui.QWidget()
+    QtGui.QMessageBox.warning(parent, "Error", str(error))
diff --git a/scripts/Muon/GUI/Common/mock_widget.py b/scripts/Muon/GUI/Common/mock_widget.py
index 45df940ce2cd49a0e9fd308dc08ee8f17ca8d5ca..d5be17d997eb983e2022716390ed78d00a11be0a 100644
--- a/scripts/Muon/GUI/Common/mock_widget.py
+++ b/scripts/Muon/GUI/Common/mock_widget.py
@@ -4,13 +4,20 @@
 #     NScD Oak Ridge National Laboratory, European Spallation Source
 #     & Institut Laue - Langevin
 # SPDX - License - Identifier: GPL - 3.0 +
+from __future__ import unicode_literals
 
 import PyQt4.QtGui as QtGui
 
+_QAPP = None
+
 
 def mockQapp():
-    qapp = QtGui.QApplication.instance()
-    if qapp is None:
-        return QtGui.QApplication([''])
-    else:
-        return qapp
+    """
+    Constructs a QApplication object if one does not already exist
+    :return: The QApplication object
+    """
+    global _QAPP
+    if _QAPP is None:
+        _QAPP = QtGui.QApplication([''])
+
+    return _QAPP
diff --git a/scripts/Muon/GUI/Common/muon_context/muon_context.py b/scripts/Muon/GUI/Common/muon_context/muon_context.py
index ccdd8ef3dca5449c3eaa0bf06885186265030257..6d9e515937dd104023500f807f0c4fc2c3f5231e 100644
--- a/scripts/Muon/GUI/Common/muon_context/muon_context.py
+++ b/scripts/Muon/GUI/Common/muon_context/muon_context.py
@@ -7,34 +7,41 @@
 
 # Muon context - contains all of the values from the GUI
 from __future__ import (absolute_import, division, print_function)
+from collections import OrderedDict
+import copy
+
 from Muon.GUI.Common import group_object
 from Muon.GUI.Common import pair_object
 
+import pythonTSV as TSVHelper
+from mantidqtpython import MantidQt
+
 # constant variable names
 Tab2Text = "some text"
-HelpText = "help text"
-LoadText = "load dummy"
+HelpText = "help_text"
+LoadText = "load-dummy"
 Groups = "groups"
 Pairs = "pairs"
 
 
 class MuonContext(object):
 
-    def __init__(self):
-        self.common_context = {}
-        self.common_context[Tab2Text] = "boo - start up"
-        self.common_context[HelpText] = "Help dummy"
-        self.common_context[LoadText] = "load dummy"
+    def __init__(self, name):
+        self._name = name
+        self.common_context = OrderedDict()
+        self.common_context[Tab2Text] = "boo-start up"
+        self.common_context[HelpText] = "Help_dummy"
+        self.common_context[LoadText] = "load_dummy"
         self.common_context[Groups] = [group_object.group(
-                                       "fwd"),
-                                       group_object.group("bwd"),
-                                       group_object.group("top")]
+                                       "fwd", [1, 2]),
+                                       group_object.group("bwd", [3, 4, 5]),
+                                       group_object.group("top", [1, 2, 3, 4, 5])]
         self.common_context[
             Pairs] = [
                 pair_object.pair(
-                    "test pair",
+                    "test_pair",
+                    "fwd",
                     "bwd",
-                    "top",
                     0.9)]
 
     def set(self, key, value):
@@ -49,9 +56,84 @@ class MuonContext(object):
         print(LoadText, self.common_context[LoadText])
         print(Groups)
         for group in self.common_context[Groups]:
-            print(group.name)
+            group.Print()
         print
         print(Pairs)
         for pair in self.common_context[Pairs]:
             print(pair.name, pair.FGroup, pair.BGroup, pair.alpha)
         print()
+
+    def save(self):
+        # save ....
+        TSVSec = MantidQt.API.TSVSerialiser()
+        TSV0 = MantidQt.API.TSVSerialiser()
+        keys = self.common_context.keys()
+
+        TSV0.writeLine("keys")
+        TSV0.storeInt(len(keys))
+        for key in keys:
+            TSV0.storeString(key)
+        for key in keys:
+            TSVHelper.writeLine(TSV0, key)
+            value = self.common_context[key]
+            try:
+                TSVHelper.saveToTSV(TSV0, value)
+            except:
+                try:
+                    self.saveCustom(TSV0, key, value)
+                except:
+                    pass
+        lines = TSV0.outputLines()
+        safeName = TSVHelper.makeLineNameSafe(self._name)
+        TSVSec.writeSection(safeName, lines)
+        return TSVSec.outputLines()
+
+    def saveCustom(self, TSV0, key, value):
+        tmpTSV = MantidQt.API.TSVSerialiser()
+        tmpTSV.writeLine("members")
+        tmpTSV.storeInt(len(value))
+        # store all of the names/keys on one line
+        for obj in value:
+            tmpTSV.storeString(obj.name)
+        # the below method writes a new line
+        for obj in value:
+            obj.save(tmpTSV)
+        safeKey = TSVHelper.makeLineNameSafe(key)
+        TSV0.writeSection(safeKey, tmpTSV.outputLines())
+
+    def loadFromProject(self, project):
+        full_load = MantidQt.API.TSVSerialiser(project)
+        # get section
+        safeName = TSVHelper.makeLineNameSafe(self._name)
+        secs = full_load.sections(safeName)
+
+        load = MantidQt.API.TSVSerialiser(secs[0])
+        load.selectLine("keys")
+        numKeys = load.readInt()
+        keys = []
+        for k in range(numKeys):
+            tmp = load.readString()
+            keys.append(tmp)
+        for key in keys:
+            value = self.common_context[key]
+            try:
+                self.common_context[
+                    key] = TSVHelper.loadFromTSV(load, key, value)
+            except:
+                self.customLoad(load, key, value)
+                pass
+
+    def customLoad(self, load, key, value):
+        safeKey = TSVHelper.makeLineNameSafe(key)
+        sec = load.sections(safeKey)
+        tmpTSV = MantidQt.API.TSVSerialiser(sec[0])
+        tmpTSV.selectLine("members")
+        num = tmpTSV.readInt()
+        names = []
+        for k in range(num):
+            names.append(tmpTSV.readString())
+        loaded_values = []
+        for name in names:
+            loaded_values.append(copy.deepcopy(value[0]))
+            loaded_values[-1].load(tmpTSV, name)
+        self.common_context[key] = loaded_values
diff --git a/scripts/Muon/GUI/Common/muon_load_data.py b/scripts/Muon/GUI/Common/muon_load_data.py
new file mode 100644
index 0000000000000000000000000000000000000000..aabae61dda5f4abd8211d6fcdc9c9550d49a6591
--- /dev/null
+++ b/scripts/Muon/GUI/Common/muon_load_data.py
@@ -0,0 +1,134 @@
+# Mantid Repository : https://github.com/mantidproject/mantid
+#
+# Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
+#     NScD Oak Ridge National Laboratory, European Spallation Source
+#     & Institut Laue - Langevin
+# SPDX - License - Identifier: GPL - 3.0 +
+from __future__ import (absolute_import, division, print_function)
+
+import Muon.GUI.Common.utilities.load_utils as load_utils
+
+
+class MuonLoadData:
+    """
+    Lightweight 'struct' to store the results of loading from a load widget. Hard-code all required parameters for each
+    entry in the __init__ method below (this way, extending for new parameters is easy).
+
+    - Can be extended to add as many parameters as needed; all getting/setting is done through keyword arguments.
+    - Can be used as an iterator, with elements being dictionaries of parameter:value pairs.
+        see for example Iterator Types (https://docs.python.org/3/library/stdtypes.html)
+
+    The keywords in all methods are "greedy" and will match any entries, with an OR like behaviour for
+    multiple keywords. So for example "run=1234, filename ="file.nxs" would match to an entry with run=1234 OR
+    filename = "file.nxs".
+
+    - Clients responsibility to prevent duplicated entries.
+    - Clients responsibility to ensure the entries are correct (i.e. no validation is performed).
+
+    The instance of this class is intended to be shared between all models in the load widget (the parent, run and file
+    widgets).
+    """
+
+    def __init__(self):
+        """
+        Hard code any parameters and their default values that are needed to be stored. The name given here
+        can then be used as a keyword into any of the methods of the class. Use singular
+        nouns.
+        """
+        self.params = {"run": [], "workspace": [], "filename": []}
+        self.defaults = {"run": 0, "workspace": [], "filename": ""}
+        if self.params.keys() != self.defaults.keys():
+            raise AttributeError("params and defaults dictionaries do not have the same keys.")
+
+    def __iter__(self):
+        self._n = -1
+        self._max = len(self.params["run"])
+        return self
+
+    def __next__(self):
+        if self._n < self._max - 1:
+            self._n += 1
+            return {key: val[self._n] for key, val in self.params.items()}
+        else:
+            raise StopIteration
+
+    # for Python 2/3 compatibility
+    next = __next__
+
+    # Getters
+
+    def num_items(self):
+        """Total number of entries"""
+        return len(next(iter(self.params.values())))
+
+    def get_parameter(self, param_name):
+        """Get list of entries for a given parameter"""
+        return self.params.get(param_name, None)
+
+    # Adding/removing data
+
+    def add_data(self, **kwargs):
+        for key, value_list in self.params.items():
+            # if keyword not supplied, use default defined in __init__
+            value_list += [kwargs.get(key, self.defaults[key])]
+
+    def remove_data(self, **kwargs):
+        indices = [i for i, j in enumerate(self._matches(**kwargs)) if not j]
+        for key, vals in self.params.items():
+            self.params[key] = [vals[i] for i in indices]
+
+    def clear(self):
+        self.params = {key: [] for key, _ in self.params.items()}
+
+    def remove_nth_last_entry(self, n):
+        """Remove the nth last entry given to the instance by add_data, n=1 refers to the most
+        recently added."""
+        keep_indices = [i for i in range(self.num_items()) if i != self.num_items() - n]
+        for key, vals in self.params.items():
+            self.params[key] = [vals[i] for i in keep_indices]
+
+    def remove_current_data(self):
+        """Remove the most recently added data item"""
+        self.remove_nth_last_entry(1)
+
+    def remove_last_added_data(self):
+        """Remove the data item before the current one"""
+        self.remove_nth_last_entry(2)
+
+    # Searching
+
+    def _matches(self, **kwargs):
+        checks = [kwargs.get(key, None) for key in self.params.keys()]
+        data_values_zip = zip(*self.params.values())
+
+        return [True if
+                sum([data_value == search_value for (data_value, search_value) in zip(list(data_values), checks)]) > 0
+                else False
+                for data_values in
+                data_values_zip]
+
+    def contains_n(self, **kwargs):
+        """Counts the number of matching entries where at least one of kwargs matches"""
+        return sum(self._matches(**kwargs))
+
+    def contains(self, **kwargs):
+        """Does the data contain a match to at least one of the supplied keyword values"""
+        n_matches = self.contains_n(**kwargs)
+        if n_matches > 0:
+            return True
+        else:
+            return False
+
+    def get_data(self, **kwargs):
+        if self.contains_n(**kwargs) == 1:
+            indices = [i for i, val in enumerate(self._matches(**kwargs)) if val is True]
+            index = indices[0]
+            return {key: val[index] for key, val in self.params.items()}
+
+    def get_latest_data(self):
+        if self.num_items() > 0:
+            return {key: val[-1] for key, val in self.params.items()}
+        else:
+            ret = {key: None for key in self.params.keys()}
+            ret["workspace"] = load_utils.empty_loaded_data()
+            return ret
diff --git a/scripts/Muon/GUI/Common/pair_object.py b/scripts/Muon/GUI/Common/pair_object.py
index 544918714a4285bb0effaebfa78705a4ccc01108..0c901c289c3db5f73e7b25afdaf92935d48e61c7 100644
--- a/scripts/Muon/GUI/Common/pair_object.py
+++ b/scripts/Muon/GUI/Common/pair_object.py
@@ -5,6 +5,8 @@
 #     & Institut Laue - Langevin
 # SPDX - License - Identifier: GPL - 3.0 +
 
+import pythonTSV as TSVHelper
+
 
 class pair(object):
 
@@ -47,3 +49,17 @@ class pair(object):
             return True
 
         return False
+
+    def save(self, TSV):
+        TSVHelper.writeLine(TSV, self._name)
+        TSV.storeString(self._F_group)
+        TSV.storeString(self._B_group)
+        TSV.storeDouble(self._alpha)
+
+    def load(self, TSV, name):
+        self._name = name
+        safeName = TSVHelper.makeLineNameSafe(name)
+        TSV.selectLine(safeName)
+        self._F_group = TSV.readString()
+        self._B_group = TSV.readString()
+        self._alpha = TSV.readDouble()
diff --git a/scripts/Muon/GUI/Common/table_utils.py b/scripts/Muon/GUI/Common/table_utils.py
deleted file mode 100644
index 799c5ccca09175c650eae941f02b5d9cbd2af8db..0000000000000000000000000000000000000000
--- a/scripts/Muon/GUI/Common/table_utils.py
+++ /dev/null
@@ -1,86 +0,0 @@
-# Mantid Repository : https://github.com/mantidproject/mantid
-#
-# Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
-#     NScD Oak Ridge National Laboratory, European Spallation Source
-#     & Institut Laue - Langevin
-# SPDX - License - Identifier: GPL - 3.0 +
-from __future__ import (absolute_import, division, print_function)
-from PyQt4 import QtCore, QtGui
-import os
-
-
-"""
-This module contains the methods for
-adding information to tables.
-"""
-
-
-def setRowName(table,row,name):
-    text = QtGui.QTableWidgetItem((name))
-    text.setFlags(QtCore.Qt.ItemIsEnabled)
-    table.setItem(row,0, text)
-
-
-def addComboToTable(table,row,options,col=1):
-    combo=QtGui.QComboBox()
-    combo.addItems(options)
-    table.setCellWidget(row,col,combo)
-    return combo
-
-
-def addDoubleToTable(table,value,row,col=1):
-    numberWidget = QtGui.QTableWidgetItem(str(value))
-    table.setItem(row,col, numberWidget)
-    return numberWidget
-
-
-def addCheckBoxToTable(table,state,row,col=1):
-    box = QtGui.QTableWidgetItem()
-    box.setFlags(QtCore.Qt.ItemIsUserCheckable |QtCore.Qt.ItemIsEnabled)
-    if state:
-        box.setCheckState(QtCore.Qt.Checked)
-    else:
-        box.setCheckState(QtCore.Qt.Unchecked)
-
-    table.setItem(row,col, box)
-    return box
-
-
-def addSpinBoxToTable(table,default,row,col=1):
-    box = QtGui.QSpinBox()
-    if default > 99:
-        box.setMaximum(default*10)
-    box.setValue(default)
-    table.setCellWidget(row,col,box)
-    return box
-
-
-# This is a work around a Windows 10
-# bug that stops tables having underlines for
-# the headers.
-def setTableHeaders(table):
-        # is it not windows
-        if os.name != "nt":
-            return
-        version=QtCore.QSysInfo.WindowsVersion
-        WINDOWS_10=160
-        if(version==WINDOWS_10):
-            styleSheet= \
-                "QHeaderView::section{"\
-                +"border-top:0px solid #D8D8D8;"\
-                +"border-left:0px solid #D8D8D8;"\
-                +"border-right:1px solid #D8D8D8;"\
-                +"border-bottom: 1px solid #D8D8D8;"\
-                +"background-color:white;"\
-                +"padding:4px;"\
-                +"}"\
-                +"QTableCornerButton::section{"\
-                +"border-top:0px solid #D8D8D8;"\
-                +"border-left:0px solid #D8D8D8;"\
-                +"border-right:1px solid #D8D8D8;"\
-                +"border-bottom: 1px solid #D8D8D8;"\
-                +"background-color:white;"\
-                +"}"
-            table.setStyleSheet(styleSheet)
-            return styleSheet
-        return
diff --git a/scripts/Muon/GUI/Common/thread_model.py b/scripts/Muon/GUI/Common/thread_model.py
index ed79652b6d7b4b4cea596a48ac9d2a842a5ad793..79537833de5427ddc454322244a78de1d0bd8f9f 100644
--- a/scripts/Muon/GUI/Common/thread_model.py
+++ b/scripts/Muon/GUI/Common/thread_model.py
@@ -6,48 +6,112 @@
 # SPDX - License - Identifier: GPL - 3.0 +
 from __future__ import (absolute_import, division, print_function)
 
-from PyQt4.QtCore import QThread
-from PyQt4 import QtCore
-from Muon.GUI.Common import message_box
+from PyQt4 import QtGui, QtCore
+from PyQt4.QtCore import pyqtSignal as Signal
+from PyQt4.QtCore import pyqtSlot
+from Muon.GUI.Common.message_box import warning
 
 
-class ThreadModel(QThread):
+class WorkerSignals(QtCore.QObject):
     """
-    A wrapper to allow threading with
-    the MaxEnt models.
+    Defines the signals available from a running ThreadModelWorker.
+    """
+    started = Signal()
+    finished = Signal()
+    error = Signal(str)
+    cancel = Signal()
+    # Use this to start the worker
+    start = Signal()
+
+
+class ThreadModelWorker(QtCore.QObject):
+    """
+    The worker objects wraps the code that is to be run on the QThread, allowing us to "move" the instance of this class
+    into the thread (this prevents us needing to inherit from QThread, with is not a QObject, which causes many issues
+    for parenting widgets and managing the threading).
     """
-    exceptionSignal = QtCore.pyqtSignal(object)
 
     def __init__(self, model):
-        QThread.__init__(self)
+        super(ThreadModelWorker, self).__init__()
+        self.signals = WorkerSignals()
         self.model = model
 
-    def __del__(self):
-        self.wait()
-
+    # This decorator is needed for the method to be successfully run on another thread.
+    # https://stackoverflow.com/questions/20752154/pyqt-connecting-a-signal-to-a-slot-to-start-a-background-operation/20818401#20818401
+    @pyqtSlot()
     def run(self):
-        self.user_cancel = False
+        self.signals.started.emit()
         try:
             self.model.execute()
             self.model.output()
         except KeyboardInterrupt:
-            pass
+            self.signals.cancel.emit()
         except Exception as error:
-            if self.user_cancel:
-                print("User ended job")
-            else:
-                self.sendSignal(error)
-            pass
+            try:
+                self.signals.error.emit(error.args[0])
+            except IndexError:
+                self.signals.error.emit("")
+        finally:
+            self.signals.finished.emit()
+            QtCore.QThread.currentThread().quit()
 
-    def sendSignal(self, error):
-        self.exceptionSignal.emit(error)
+
+class ThreadModel(QtGui.QWidget):
+    """
+    A wrapper to allow threading with
+    the MaxEnt models.
+    """
+    exceptionSignal = QtCore.pyqtSignal(str)
+
+    def __init__(self, model):
+        super(ThreadModel, self).__init__()
+        self.model = model
+
+        # The ThreadModelWorker wrapping the code to be executed
+        self._worker = ThreadModelWorker(self.model)
+        # The QThread instance on which the execution of the code will be performed
+        self._thread = QtCore.QThread(self)
+
+        # callbacks for the .started() and .finished() signals of the worker
+        self.start_slot = lambda: 0
+        self.end_slot = lambda: 0
+        self._exception_callback = self._default_exception_callback
+
+        self.check_model_has_correct_attributes()
+
+    def check_model_has_correct_attributes(self):
+        if hasattr(self.model, "execute") and hasattr(self.model, "output"):
+            return
+        raise AttributeError("Please ensure the model passed to ThreadModel has implemented"
+                             " execute() and output() methods")
+
+    def setup_thread_and_start(self):
+        # create the ThreadModelWorker and connect its signals up
+        self._worker.signals.start.connect(self._worker.run)
+        self._worker.signals.started.connect(self.start_slot)
+        self._worker.signals.finished.connect(self.end_slot)
+        self._worker.signals.finished.connect(self.threadWrapperTearDown)
+        self._worker.signals.error.connect(self.warning)
+
+        # Create the thread and pass it the worker
+        self._thread.start()
+        self._worker.moveToThread(self._thread)
+
+        # start the worker code inside the thread
+        self._worker.signals.start.emit()
+
+    def start(self):
+        # keep this method to maintain consistency with older usages of the ThreadModel
+        self.setup_thread_and_start()
+
+    def warning(self, message):
+        self._exception_callback(message)
 
     def join(self):
         if self.exception is not None:
             raise self.exception
 
     def cancel(self):
-        self.user_cancel = True
         self.model.cancel()
 
     # if there is one set of inputs (1 alg)
@@ -56,14 +120,36 @@ class ThreadModel(QThread):
 
     # if there are multiple inputs (alg>1)
     def loadData(self, inputs):
+        if not hasattr(self.model, "loadData"):
+            raise AttributeError("The model passed to ThreadModel has not implemented"
+                                 " loadData() method, which it is attempting to call.")
         self.model.loadData(inputs)
 
-    def threadWrapperSetUp(self, startSlot, endSlot):
-        self.started.connect(startSlot)
-        self.finished.connect(endSlot)
-        self.exceptionSignal.connect(message_box.warning)
+    def threadWrapperSetUp(self,
+                           on_thread_start_callback=lambda: 0,
+                           on_thread_end_callback=lambda: 0,
+                           on_thread_exception_callback=None):
+
+        assert hasattr(on_thread_start_callback, '__call__')
+        assert hasattr(on_thread_end_callback, '__call__')
+
+        self.start_slot = on_thread_start_callback
+        self.end_slot = on_thread_end_callback
+        if on_thread_exception_callback is not None:
+            assert hasattr(on_thread_exception_callback, '__call__')
+            self._exception_callback = on_thread_exception_callback
+        else:
+            self._exception_callback = self._default_exception_callback
+
+    def threadWrapperTearDown(self):
+        self._thread.wait()
+        self._thread = QtCore.QThread(self)
+        self._worker.signals.started.disconnect(self.start_slot)
+        self._worker.signals.finished.disconnect(self.end_slot)
+        self._worker.signals.finished.disconnect(self.threadWrapperTearDown)
+        self._worker.signals.error.disconnect(self.warning)
+        self.start_slot = lambda: 0
+        self.end_slot = lambda: 0
 
-    def threadWrapperTearDown(self,startSlot,endSlot):
-        self.started.disconnect(startSlot)
-        self.finished.disconnect(endSlot)
-        self.exceptionSignal.disconnect(message_box.warning)
+    def _default_exception_callback(self, message):
+        warning(message)
diff --git a/scripts/Muon/GUI/Common/utilities/__init__.py b/scripts/Muon/GUI/Common/utilities/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/scripts/Muon/GUI/Common/utilities/algorithm_utils.py b/scripts/Muon/GUI/Common/utilities/algorithm_utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..11dce5cc396361d5046a4a4738aade64b668ec15
--- /dev/null
+++ b/scripts/Muon/GUI/Common/utilities/algorithm_utils.py
@@ -0,0 +1,101 @@
+# Mantid Repository : https://github.com/mantidproject/mantid
+#
+# Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
+#     NScD Oak Ridge National Laboratory, European Spallation Source
+#     & Institut Laue - Langevin
+# SPDX - License - Identifier: GPL - 3.0 +
+from __future__ import (absolute_import, division, print_function)
+
+import mantid.simpleapi as mantid
+
+
+def run_MuonPreProcess(parameter_dict):
+    """
+    Apply the MuonPreProcess algorithm with the properties supplied through
+    the input dictionary of {proeprty_name:property_value} pairs.
+    Returns the calculated workspace.
+    """
+    print("Pre-process : ", {key: val for key, val in parameter_dict.items() if key != "InputWorkspace"})
+    if "DeadTimeTable" in parameter_dict.keys():
+        print("DTC : ", type(parameter_dict["DeadTimeTable"]), parameter_dict["DeadTimeTable"].toDict())
+    alg = mantid.AlgorithmManager.create("MuonPreProcess")
+    alg.initialize()
+    alg.setAlwaysStoreInADS(False)
+    alg.setProperty("OutputWorkspace", "__notUsed")
+    alg.setProperties(parameter_dict)
+    alg.execute()
+    return alg.getProperty("OutputWorkspace").value
+
+
+def run_MuonGroupingCounts(parameter_dict):
+    """
+    Apply the MuonGroupingCounts algorithm with the properties supplied through
+    the input dictionary of {proeprty_name:property_value} pairs.
+    Returns the calculated workspace.
+    """
+    alg = mantid.AlgorithmManager.create("MuonGroupingCounts")
+    alg.initialize()
+    alg.setAlwaysStoreInADS(False)
+    alg.setProperty("OutputWorkspace", "__notUsed")
+    alg.setProperties(parameter_dict)
+    alg.execute()
+    return alg.getProperty("OutputWorkspace").value
+
+
+def run_MuonPairingAsymmetry(parameter_dict):
+    """
+    Apply the MuonPairingAsymmetry algorithm with the properties supplied through
+    the input dictionary of {proeprty_name:property_value} pairs.
+    Returns the calculated workspace.
+    """
+    alg = mantid.AlgorithmManager.create("MuonPairingAsymmetry")
+    alg.initialize()
+    alg.setAlwaysStoreInADS(False)
+    alg.setProperty("OutputWorkspace", "__notUsed")
+    alg.setProperties(parameter_dict)
+    alg.execute()
+    return alg.getProperty("OutputWorkspace").value
+
+
+def run_AppendSpectra(ws1, ws2):
+    """
+    Apply the AppendSpectra algorithm to two given workspaces (no checks made).
+    Returns the appended workspace.
+    """
+    alg = mantid.AlgorithmManager.create("AppendSpectra")
+    alg.initialize()
+    alg.setAlwaysStoreInADS(False)
+    alg.setProperty("OutputWorkspace", "__notUsed")
+    alg.setProperty("InputWorkspace1", ws1)
+    alg.setProperty("InputWorkspace2", ws2)
+    alg.execute()
+    return alg.getProperty("OutputWorkspace").value
+
+
+def run_AlphaCalc(parameter_dict):
+    """
+    Apply the AlphaCalc algorithm with the properties supplied through
+    the input dictionary of {proeprty_name:property_value} pairs.
+    Returns the calculated value of alpha.
+    """
+    alg = mantid.AlgorithmManager.create("AlphaCalc")
+    alg.initialize()
+    alg.setAlwaysStoreInADS(False)
+    alg.setProperties(parameter_dict)
+    alg.execute()
+    return alg.getProperty("Alpha").value
+
+
+def run_Plus(parameter_dict):
+    """
+    Apply the AlphaCalc algorithm with the properties supplied through
+    the input dictionary of {proeprty_name:property_value} pairs.
+    Returns the calculated value of alpha.
+    """
+    alg = mantid.AlgorithmManager.create("Plus")
+    alg.initialize()
+    alg.setAlwaysStoreInADS(False)
+    alg.setProperty("OutputWorkspace", "__notUsed")
+    alg.setProperties(parameter_dict)
+    alg.execute()
+    return alg.getProperty("OutputWorkspace").value
diff --git a/scripts/Muon/GUI/Common/utilities/load_utils.py b/scripts/Muon/GUI/Common/utilities/load_utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..fd58fbd2ce14ca417123fb2ef11ff9f572ad4ae9
--- /dev/null
+++ b/scripts/Muon/GUI/Common/utilities/load_utils.py
@@ -0,0 +1,254 @@
+# Mantid Repository : https://github.com/mantidproject/mantid
+#
+# Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
+#     NScD Oak Ridge National Laboratory, European Spallation Source
+#     & Institut Laue - Langevin
+# SPDX - License - Identifier: GPL - 3.0 +
+from __future__ import (absolute_import, division, print_function)
+
+import os
+import mantid.simpleapi as mantid
+from mantid.api import WorkspaceGroup
+from mantid.api import ITableWorkspace
+from mantid.simpleapi import mtd
+from mantid import api
+from mantid.kernel import ConfigServiceImpl
+import Muon.GUI.Common.utilities.muon_file_utils as file_utils
+from Muon.GUI.Common.ADSHandler.muon_workspace_wrapper import MuonWorkspaceWrapper
+
+
+class LoadUtils(object):
+    """
+    A simple class for identifing the current run
+    and it can return the name, run and instrument.
+    The current run is the same as the one in MonAnalysis
+    """
+
+    def __init__(self, parent=None):
+        exists, tmpWS = self.MuonAnalysisExists()
+        if exists:
+            self.setUp(tmpWS)
+        else:
+            raise RuntimeError("No data loaded. \n Please load data using Muon Analysis")
+
+    def setUp(self, tmpWS):
+        # get everything from the ADS
+        self.options = mantid.AnalysisDataService.getObjectNames()
+        self.options = [item.replace(" ", "") for item in self.options]
+        self.N_points = len(tmpWS.readX(0))
+        self.instrument = tmpWS.getInstrument().getName()
+
+        self.runName = self.instrument + str(tmpWS.getRunNumber()).zfill(8)
+
+    # get methods
+    def getNPoints(self):
+        return self.N_points
+
+    def getCurrentWS(self):
+        return self.runName, self.options
+
+    def getRunName(self):
+        return self.runName
+
+    def getInstrument(self):
+        return self.instrument
+
+    # check if data matches current
+    def digit(self, x):
+        return int(filter(str.isdigit, x) or 0)
+
+    def hasDataChanged(self):
+        exists, ws = self.MuonAnalysisExists()
+        if exists:
+            current = ws.getInstrument().getName() + str(ws.getRunNumber()).zfill(8)
+            if self.runName != current:
+                mantid.logger.error("Active workspace has changed. Reloading the data")
+                self.setUp(ws)
+                return True
+        return False
+
+    # check if muon analysis exists
+    def MuonAnalysisExists(self):
+        # if period data look for the first period
+        if mantid.AnalysisDataService.doesExist("MuonAnalysis_1"):
+            tmpWS = mantid.AnalysisDataService.retrieve("MuonAnalysis_1")
+            return True, tmpWS
+            # if its not period data
+        elif mantid.AnalysisDataService.doesExist("MuonAnalysis"):
+            tmpWS = mantid.AnalysisDataService.retrieve("MuonAnalysis")
+            return True, tmpWS
+        else:
+            return False, None
+
+    # Get the groups/pairs for active WS
+    # ignore raw files
+    def getWorkspaceNames(self):
+        # gets all WS in the ADS
+        runName, options = self.getCurrentWS()
+        final_options = []
+        # only keep the relevant WS (same run as Muon Analysis)
+        for pick in options:
+            if ";" in pick and "Raw" not in pick and runName in pick:
+                final_options.append(pick)
+        return final_options
+
+    # Get the groups/pairs for active WS
+    def getGroupedWorkspaceNames(self):
+        # gets all WS in the ADS
+        runName, options = self.getCurrentWS()
+        final_options = []
+        # only keep the relevant WS (same run as Muon Analysis)
+        for pick in options:
+            if "MuonAnalysisGrouped_" in pick and ";" not in pick:
+                final_options.append(pick)
+        return final_options
+
+
+def get_default_instrument():
+    default_instrument = ConfigServiceImpl.Instance().getInstrument().name()
+    if default_instrument not in file_utils.allowed_instruments:
+        default_instrument = 'MUSR'
+    return default_instrument
+
+
+def run_LoadInstrument(parameter_dict):
+    """
+    Apply the LoadInstrument algorithm with the properties supplied through
+    the input dictionary of {proeprty_name:property_value} pairs.
+    Returns the calculated value of alpha.
+    """
+    alg = mantid.AlgorithmManager.create("LoadInstrument")
+    alg.initialize()
+    alg.setAlwaysStoreInADS(False)
+    alg.setProperties(parameter_dict)
+    alg.execute()
+    return alg.getProperty("Workspace").value
+
+
+def __default_workspace():
+    default_instrument = get_default_instrument()
+    workspace = api.WorkspaceFactoryImpl.Instance().create("Workspace2D", 2, 10, 10)
+    workspace = run_LoadInstrument(
+        {"Workspace": workspace,
+         "RewriteSpectraMap": True,
+         "InstrumentName": default_instrument})
+    return MuonWorkspaceWrapper(workspace)
+
+
+# Dictionary of (property name):(property value) pairs to put into Load algorithm
+# NOT including "OutputWorkspace" and "Filename"
+DEFAULT_INPUTS = {
+    "DeadTimeTable": "__notUsed",
+    "DetectorGroupingTable": "__notUsed"}
+# List of property names to be extracted from the result of the Load algorithm
+DEFAULT_OUTPUTS = ["OutputWorkspace",
+                   "DeadTimeTable",
+                   "DetectorGroupingTable",
+                   "TimeZero",
+                   "FirstGoodData",
+                   "MainFieldDirection"]
+# List of default values for the DEFAULT_OUTPUTS list
+DEFAULT_OUTPUT_VALUES = [__default_workspace(),
+                         None,  # api.WorkspaceFactoryImpl.Instance().createTable("TableWorkspace"),
+                         api.WorkspaceFactoryImpl.Instance().createTable("TableWorkspace"),
+                         0.0,
+                         0.0, "Unknown direction"]
+
+
+def is_workspace_group(workspace):
+    return isinstance(workspace, WorkspaceGroup)
+
+
+def get_run_from_multi_period_data(workspace_list):
+    """Checks if multi-period data has a single consistent
+    run number and returns it, otherwise raises ValueError."""
+    runs = [ws.getRunNumber() for ws in workspace_list]
+    unique_runs = list(set(runs))
+    if len(unique_runs) != 1:
+        raise ValueError("Multi-period data contains >1 unique run number.")
+    else:
+        return unique_runs[0]
+
+
+def load_dead_time_from_filename(filename):
+    """
+    From a neXus file, load the dead time ITableWorkspace from it and add to the ADS
+    with a name <Instrument><Run>_deadTimes , e.g. EMU0001234_deadTimes.
+
+    :param filename: The full path to the .nxs file.
+    :return: The name of the workspace in the ADS.
+    """
+    loaded_data, run, _ = load_workspace_from_filename(filename)
+
+    if is_workspace_group(loaded_data["OutputWorkspace"]):
+        dead_times = loaded_data["DataDeadTimeTable"][0]
+    else:
+        dead_times = loaded_data["DataDeadTimeTable"]
+
+    if dead_times is None:
+        return ""
+    assert isinstance(dead_times, ITableWorkspace)
+
+    instrument = loaded_data["OutputWorkspace"].workspace.getInstrument().getName()
+    name = str(instrument) + file_utils.format_run_for_file(run) + "_deadTimes"
+    api.AnalysisDataService.Instance().addOrReplace(name, dead_times)
+
+    return name
+
+
+def load_workspace_from_filename(filename,
+                                 input_properties=DEFAULT_INPUTS,
+                                 output_properties=DEFAULT_OUTPUTS):
+    try:
+        alg = create_load_algorithm(filename, input_properties)
+        alg.execute()
+    except:
+        alg = create_load_algorithm(filename.split(os.sep)[-1], input_properties)
+        alg.execute()
+
+    workspace = alg.getProperty("OutputWorkspace").value
+    if is_workspace_group(workspace):
+        # handle multi-period data
+        load_result = _get_algorithm_properties(alg, output_properties)
+        load_result["OutputWorkspace"] = [MuonWorkspaceWrapper(ws) for ws in load_result["OutputWorkspace"]]
+        run = get_run_from_multi_period_data(workspace)
+
+    else:
+        # single period data
+        load_result = _get_algorithm_properties(alg, output_properties)
+        load_result["OutputWorkspace"] = MuonWorkspaceWrapper(load_result["OutputWorkspace"])
+        run = int(workspace.getRunNumber())
+
+    load_result["DataDeadTimeTable"] = load_result["DeadTimeTable"]
+    load_result["DeadTimeTable"] = None
+
+    filename = alg.getProperty("Filename").value
+
+    return load_result, run, filename
+
+
+def empty_loaded_data():
+    return dict(zip(DEFAULT_OUTPUTS + ["DataDeadTimeTable"], DEFAULT_OUTPUT_VALUES + [None]))
+
+
+def create_load_algorithm(filename, property_dictionary):
+    alg = mantid.AlgorithmManager.create("LoadMuonNexus")
+    alg.initialize()
+    alg.setAlwaysStoreInADS(False)
+    alg.setProperty("OutputWorkspace", "__notUsed")
+    alg.setProperty("Filename", filename)
+    alg.setProperties(property_dictionary)
+    return alg
+
+
+def _get_algorithm_properties(alg, property_dict):
+    return {key: alg.getProperty(key).value for key in alg.keys() if key in property_dict}
+
+
+def get_table_workspace_names_from_ADS():
+    """
+    Return a list of names of TableWorkspace objects which are in the ADS.
+    """
+    names = api.AnalysisDataService.Instance().getObjectNames()
+    table_names = [name for name in names if isinstance(mtd[name], ITableWorkspace)]
+    return table_names
diff --git a/scripts/Muon/GUI/Common/utilities/muon_file_utils.py b/scripts/Muon/GUI/Common/utilities/muon_file_utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..0713effa5c692f1cc10e89c59b72372025b8a813
--- /dev/null
+++ b/scripts/Muon/GUI/Common/utilities/muon_file_utils.py
@@ -0,0 +1,112 @@
+from __future__ import (absolute_import, division, print_function)
+
+import os
+
+allowed_instruments = ["EMU", "MUSR", "CHRONUS", "HIFI"]
+allowed_extensions = ["nxs"]
+FILE_SEP = os.sep
+
+
+def filter_for_extensions(extensions):
+    """Filter for file browser"""
+    str_list = ["*." + str(ext) for ext in extensions]
+    return "Files (" + ", ".join(str_list) + ")"
+
+
+def get_instrument_directory(instrument):
+    """
+    If instrument is supported, returns the directory name on the ISIS network
+    in which its data can be found
+    """
+    if instrument in allowed_instruments:
+        instrument_directory = instrument
+        if instrument == "CHRONUS":
+            instrument_directory = "NDW1030"
+        return instrument_directory
+    else:
+        return None
+
+
+def check_file_exists(filename):
+    return os.path.isfile(filename)
+
+
+def get_current_run_filename(instrument):
+    """
+    If instrument is supported, attempts to find the file on the ISIS network which
+    contains the data from its current (most up-to-date) run.
+    """
+
+    instrument_directory = get_instrument_directory(instrument)
+    if instrument_directory is None:
+        return ""
+
+    autosave_file_name = _instrument_data_directory(instrument) + FILE_SEP + "autosave.run"
+    autosave_points_to = ""
+    if not check_file_exists(autosave_file_name):
+        raise ValueError("Cannot find file : " + autosave_file_name)
+    with open(autosave_file_name, 'r') as autosave_file:
+        for line in autosave_file:
+            if os.path.isfile(line):
+                autosave_points_to = line
+    if autosave_points_to == "":
+        # Default to auto_A (replicates MuonAnalysis 1.0 behaviour)
+        current_run_filename = FILE_SEP + FILE_SEP + instrument_directory + FILE_SEP + "data" \
+                               + FILE_SEP + instrument_directory + "auto_A.tmp"
+    else:
+        current_run_filename = FILE_SEP + FILE_SEP + instrument_directory + FILE_SEP + "data" \
+                               + FILE_SEP + autosave_points_to
+    return current_run_filename
+
+
+def format_run_for_file(run):
+    return "{0:08d}".format(run)
+
+
+def _instrument_data_directory(instrument):
+    """The directory which stores the data for a particular instrument"""
+    return FILE_SEP + FILE_SEP + get_instrument_directory(instrument) + FILE_SEP + "data"
+
+
+def file_path_for_instrument_and_run(instrument, run):
+    """Returns the path to the data file for a given instrument/run"""
+    base_dir = _instrument_data_directory(instrument)
+    file_name = instrument + format_run_for_file(run) + ".nxs"
+    return base_dir.lower() + FILE_SEP + file_name
+
+
+def remove_duplicated_files_from_list(file_list):
+    """
+    Split filenames from their paths, and remove duplicates, keeping the
+    first ocurrence in the list.
+
+    Example
+
+    input :
+        ["C:\dir1\file1.nxs","C:\dir1\file2.nxs","C:\dir1\dir2\file1.nxs"]
+
+    output :
+        ["C:\dir1\file1.nxs","C:\dir1\file2.nxs"]
+    """
+    files = [os.path.basename(full_path) for full_path in file_list]
+    unique_files = [file_list[n] for n, file_name in enumerate(files) if file_name not in files[:n]]
+    return unique_files
+
+
+def parse_user_input_to_files(input_text, extensions=allowed_extensions):
+    """
+    Parse user input from load file widget into list of filenames.
+
+    Example
+
+    input_text = "C:\dir1\dir2\file1.nxs;C:\dir1\file2.nxs"
+
+    output :
+        ["file1.nxs", "file2.nxs"]
+    """
+    input_list = input_text.split(";")
+    filenames = []
+    for text in input_list:
+        if os.path.splitext(text)[-1].lower() in ["." + ext for ext in extensions]:
+            filenames += [text]
+    return filenames
diff --git a/scripts/Muon/GUI/Common/utilities/run_string_utils.py b/scripts/Muon/GUI/Common/utilities/run_string_utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..447330e5123375a307e779b50cbc898b181afd6b
--- /dev/null
+++ b/scripts/Muon/GUI/Common/utilities/run_string_utils.py
@@ -0,0 +1,161 @@
+from __future__ import (absolute_import, division, print_function)
+
+from itertools import groupby
+from operator import itemgetter
+import functools
+import re
+
+delimiter = ","
+range_separator = "-"
+run_string_regex = "^[0-9]*([0-9]+[,-]{0,1})*[0-9]+$"
+max_run_list_size = 1000
+valid_float_regex = "^[0-9]+([.][0-9]*)?$"
+valid_name_regex = "^\w+$"
+valid_alpha_regex = "^[0-9]*[.]?[0-9]+$"
+
+
+def _remove_duplicates_from_list(run_list):
+    return list(set(run_list))
+
+
+# Python 3 does not support tuple parameter unpacking
+# e.g. lambda (x,y): x+y
+# throws in Python 3 but is ok in 2, use this wrapper to avoid the error
+# e.g. lambda_tuple_unpacking( lambda x,y: x+y)
+# works in Python 2/3
+def lambda_tuple_unpacking(lam):
+    @functools.wraps(lam)
+    def f_inner(args):
+        return lam(*args)
+
+    return f_inner
+
+
+def run_list_to_string(run_list):
+    """
+    Converts a list of runs into a formatted string using a delimiter/range separator
+    :param run_list: list of integers
+    :return: string representation
+    """
+    run_list = _remove_duplicates_from_list(run_list)
+    run_list = [i for i in run_list if i >= 0]
+    run_list.sort()
+    if len(run_list) > max_run_list_size:
+        raise IndexError("Too many runs (" + str(len(run_list)) + ") must be <" + str(max_run_list_size))
+
+    range_list = []
+    # use groupby to group run_list into sublists of sequential integers
+    for _, grouped_list in groupby(enumerate(run_list), key=lambda_tuple_unpacking(lambda i, x: i - x)):
+        concurrent_range = list(map(itemgetter(1), grouped_list))
+        if len(concurrent_range) > 1:
+            range_list += [str(concurrent_range[0]) + range_separator + str(concurrent_range[-1])]
+        else:
+            range_list += [str(concurrent_range[0])]
+    return delimiter.join(range_list)
+
+
+def validate_run_string(run_string):
+    """
+    Use a regular expression to check if a string represents a series of runs
+    :param run_string: string representation of a series of runs
+    :return: bool
+    """
+    if run_string == "":
+        return True
+    if re.match(run_string_regex, run_string) is not None:
+        return True
+    return False
+
+
+def run_string_to_list(run_string):
+    """
+    Does the opposite of run_list_to_string(), taking a string representation of a series of runs
+    and producing an ordered list of unique runs. Calls validate_run_string().
+    :param run_string: string, a series of runs
+    :return: list of integers
+    """
+    if not validate_run_string(run_string):
+        raise IndexError(run_string + " is not a valid run string")
+    run_list = []
+    if run_string == "":
+        return run_list
+    run_string_list = run_string.split(delimiter)
+    for runs in run_string_list:
+        split_runs = runs.split(range_separator)
+        if len(runs) == 1:
+            run_list += [int(runs)]
+        else:
+            range_max = int(split_runs[-1])
+            range_min = int(split_runs[0])
+            if (range_max - range_min) > max_run_list_size:
+                raise IndexError(
+                    "Too many runs (" + str(range_max - range_min) + ") must be <" + str(max_run_list_size))
+            else:
+                run_list += [range_min + i for i in range(range_max - range_min + 1)]
+    run_list = _remove_duplicates_from_list(run_list)
+    run_list.sort()
+    return run_list
+
+
+def increment_run_list(run_list, increment_by=1):
+    """
+    Takes a list of runs, and increments the list by adding a given number of runs, starting at one
+    after the highest run in the list.
+    :param run_list: list of integers
+    :param increment_by: integer, the number of runs to increase the list by
+    :return: modified list of integers
+    """
+    if increment_by <= 0:
+        return run_list
+    highest_run = max(run_list)
+    run_list.append(highest_run + 1)
+    return increment_run_list(run_list, increment_by - 1)
+
+
+def decrement_run_list(run_list, decrement_by=1):
+    """
+    Takes a list of runs, and decrements the list by adding a given number of runs, starting at one
+    before the lowest run in the list. Stops at 0.
+    :param run_list: list of integers
+    :param decrement_by: integer, the number of runs to increase the list by
+    :return: modified list of integers
+    """
+    if decrement_by <= 0:
+        return run_list
+    lowest_run = min(run_list)
+    if lowest_run < 1:
+        return run_list
+    run_list.append(lowest_run - 1)
+    return decrement_run_list(run_list, decrement_by - 1)
+
+
+def increment_run(run, increment_by=1):
+    return run + increment_by
+
+
+def increment_run_string(run_string, increment_by=1):
+    """
+    Takes a string representation of runs, and adds a number of runs starting at one after the highest.
+    :param run_string:
+    :param increment_by: integer, number of sequential runs to add
+    :return: modified string of runs (reformatted according to run_list_to_string())
+    """
+    run_list = run_string_to_list(run_string)
+    run_list = increment_run_list(run_list, increment_by)
+    return run_list_to_string(run_list)
+
+
+def decrement_run(run, decrement_by=1):
+    return max(0, run - decrement_by)
+
+
+def decrement_run_string(run_string, decrement_by=1):
+    """
+    Takes a string representation of runs, and adds a number of runs starting at one before the lowest and decrementing.
+    :param run_string:
+    :param decrement_by: integer, number of sequential runs to add
+    :return: modified string of runs (reformatted according to run_list_to_string())
+    """
+    run_list = run_string_to_list(run_string)
+    run_list = decrement_run_list(run_list, decrement_by)
+    return run_list_to_string(run_list)
diff --git a/scripts/Muon/GUI/Common/utilities/table_utils.py b/scripts/Muon/GUI/Common/utilities/table_utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..69228bcbad3f355bbc2bd4efcf1955990fc3b254
--- /dev/null
+++ b/scripts/Muon/GUI/Common/utilities/table_utils.py
@@ -0,0 +1,148 @@
+# Mantid Repository : https://github.com/mantidproject/mantid
+#
+# Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
+#     NScD Oak Ridge National Laboratory, European Spallation Source
+#     & Institut Laue - Langevin
+# SPDX - License - Identifier: GPL - 3.0 +
+from __future__ import (absolute_import, division, print_function)
+
+import os
+from functools import wraps
+
+from PyQt4 import QtCore, QtGui
+
+"""
+This module contains the methods for
+adding information to tables.
+"""
+
+
+class ValidatedTableItem(QtGui.QTableWidgetItem):
+    """
+    An extension of the QTableWidgetItem class, which modifies the setData method to first check that the entered
+    text is valid; and only runs setData if the validator returns True.
+
+    Essentially functions identically to the QTableWidgetItem, but only sets valid entries. If no validator is supplied
+    then the behaviour is identical.
+
+    Example usage:
+
+    def greater_than_zero_validator(string_val):
+        return float(string_val) > 0
+
+    table_item = ValidatedTableItem(greater_than_zero_validator)
+    """
+
+    @staticmethod
+    def validator_before_set(func, validator):
+        @wraps(func)
+        def wrapper(*args, **kw):
+            try:
+                if isinstance(args[1], unicode):
+                    # within MantidPlot, type is unicode
+                    validator_arg = args[1]
+                else:
+                    # when standalone, type is QtCore.QVariant
+                    validator_arg = args[1].toString()
+
+                if validator(validator_arg):
+                    res = func(*args, **kw)
+                else:
+                    res = None
+                return res
+            except Exception as e:
+                print("EXCEPTION FROM ValidatedTableItem : ", e.args[0])
+
+        return wrapper
+
+    @staticmethod
+    def default_validator(_text):
+        return True
+
+    def __init__(self, validator=default_validator):
+        """
+        :param validator: A predicate function (returns True/False) taking a single string as argument
+        """
+        super(ValidatedTableItem, self).__init__(0)
+        self._validator = validator
+        self._modify_setData()
+
+    def validator(self, text):
+        return self._validator(text)
+
+    def _modify_setData(self):
+        """
+        Modify the setData method.
+        """
+        setattr(self, "setData", self.validator_before_set(self.setData, self.validator))
+
+
+def setRowName(table, row, name):
+    text = QtGui.QTableWidgetItem((name))
+    text.setFlags(QtCore.Qt.ItemIsEnabled)
+    table.setItem(row, 0, text)
+
+
+def addComboToTable(table,row,options,col=1):
+    combo=QtGui.QComboBox()
+    combo.addItems(options)
+    table.setCellWidget(row,col,combo)
+    return combo
+
+
+def addDoubleToTable(table,value,row,col=1):
+    numberWidget = QtGui.QTableWidgetItem(str(value))
+    table.setItem(row,col, numberWidget)
+    return numberWidget
+
+
+def addCheckBoxToTable(table,state,row,col=1):
+    box = QtGui.QTableWidgetItem()
+    box.setFlags(QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsEnabled)
+    if state:
+        box.setCheckState(QtCore.Qt.Checked)
+    else:
+        box.setCheckState(QtCore.Qt.Unchecked)
+
+    table.setItem(row,col, box)
+    return box
+
+
+def addSpinBoxToTable(table,default,row,col=1):
+    box = QtGui.QSpinBox()
+    if default > 99:
+        box.setMaximum(default * 10)
+    box.setValue(default)
+    table.setCellWidget(row,col,box)
+    return box
+
+
+# This is a work around a Windows 10
+# bug that stops tables having underlines for
+# the headers.
+def setTableHeaders(table):
+    # is it not windows
+    if os.name != "nt":
+        return
+    version = QtCore.QSysInfo.WindowsVersion
+    WINDOWS_10 = 160
+    if (version == WINDOWS_10):
+        styleSheet = \
+            "QHeaderView::section{" \
+            + "border-top:0px solid #D8D8D8;" \
+            + "border-left:0px solid #D8D8D8;" \
+            + "border-right:1px solid #D8D8D8;" \
+            + "border-bottom: 1px solid #D8D8D8;" \
+            + "background-color:white;" \
+            + "padding:4px;" \
+            + "}" \
+            + "QTableCornerButton::section{" \
+            + "border-top:0px solid #D8D8D8;" \
+            + "border-left:0px solid #D8D8D8;" \
+            + "border-right:1px solid #D8D8D8;" \
+            + "border-bottom: 1px solid #D8D8D8;" \
+            + "background-color:white;" \
+            + "}"
+        table.setStyleSheet(styleSheet)
+        return styleSheet
+    return
diff --git a/scripts/Muon/GUI/Common/utilities/xml_utils.py b/scripts/Muon/GUI/Common/utilities/xml_utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..effe8ca0e9b82519738b248e0770988e086d82ca
--- /dev/null
+++ b/scripts/Muon/GUI/Common/utilities/xml_utils.py
@@ -0,0 +1,122 @@
+# Mantid Repository : https://github.com/mantidproject/mantid
+#
+# Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
+#     NScD Oak Ridge National Laboratory, European Spallation Source
+#     & Institut Laue - Langevin
+# SPDX - License - Identifier: GPL - 3.0 +
+from __future__ import (absolute_import, division, print_function)
+
+import os
+import xml.etree.ElementTree as ET
+import Muon.GUI.Common.run_string_utils as run_string_utils
+
+from Muon.GUI.Common.muon_group import MuonGroup
+from Muon.GUI.Common.muon_pair import MuonPair
+
+
+def _create_XML_subElement_for_groups(root_node, groups):
+    group_nodes = []
+    for group in groups:
+        child = ET.SubElement(root_node, 'group', name=group.name)
+        id_string = run_string_utils.run_list_to_string(group.detectors)
+        ids = ET.SubElement(child, 'ids', val=id_string)
+        child.extend(ids)
+        group_nodes += [child]
+    return group_nodes
+
+
+def _create_XML_subElement_for_pairs(root_node, pairs):
+    pair_nodes = []
+    for pair in pairs:
+        child = ET.SubElement(root_node, 'pair', name=pair.name)
+        fwd_group = ET.SubElement(child, 'forward-group', val=pair.forward_group)
+        bwd_group = ET.SubElement(child, 'backward-group', val=pair.backward_group)
+        alpha = ET.SubElement(child, 'alpha', val=str(pair.alpha))
+        child.extend(fwd_group)
+        child.extend(bwd_group)
+        child.extend(alpha)
+        pair_nodes += [child]
+    return pair_nodes
+
+
+def save_grouping_to_XML(groups, pairs, filename, save=True):
+    """
+    Save a set of muon group and pair parameters to XML format file. Fewer checks are performed
+    than with the XML loading.
+
+    :param groups: A list of MuonGroup objects to save.
+    :param pairs: A list of MuonPair objects to save.
+    :param filename: The name of the XML file to save to.
+    :param save: Whether to actually save the file.
+    :return: the XML tree (used in testing).
+    """
+    # some basic checks
+    if filename == "":
+        raise AttributeError("File must be specified for saving to XML")
+    if os.path.splitext(filename)[-1].lower() != ".xml":
+        raise AttributeError("File extension must be XML")
+    if sum([0 if isinstance(group, MuonGroup) else 1 for group in groups]) > 0:
+        raise AttributeError("groups must be MuonGroup type")
+    if sum([0 if isinstance(pair, MuonPair) else 1 for pair in pairs]) > 0:
+        raise AttributeError("pairs must be MuonPair type")
+
+    root = ET.Element("detector-grouping")
+
+    # handle groups
+    group_nodes = _create_XML_subElement_for_groups(root, groups)
+    for child in group_nodes:
+        root.extend(child)
+
+    # handle pairs
+    pair_nodes = _create_XML_subElement_for_pairs(root, pairs)
+    for child in pair_nodes:
+        root.extend(child)
+
+    tree = ET.ElementTree(root)
+    if save:
+        tree.write(filename)
+    return tree
+
+
+def load_grouping_from_XML(filename):
+    """
+    Load group/pair data from an XML file (which can be produced using the save_grouping_to_XML() function
+
+    :param filename: Full filepath to an xml file.
+    :return: (groups, pairs), lists of MuonGroup, MuonPair objects respectively.
+    """
+    tree = ET.parse(filename)
+    root = tree.getroot()
+
+    group_names, group_ids = _get_groups_from_XML(root)
+    pair_names, pair_groups, pair_alphas = _get_pairs_from_XML(root)
+    groups, pairs = [], []
+
+    for i, group_name in enumerate(group_names):
+        groups += [MuonGroup(group_name=group_name,
+                             detector_ids=group_ids[i])]
+    for i, pair_name in enumerate(pair_names):
+        pairs += [MuonPair(pair_name=pair_name,
+                           forward_group_name=pair_groups[i][0],
+                           backward_group_name=pair_groups[i][1],
+                           alpha=pair_alphas[i])]
+    return groups, pairs
+
+
+def _get_groups_from_XML(root):
+    names, ids = [], []
+    for child in root:
+        if child.tag == "group":
+            names += [child.attrib['name']]
+            ids += [run_string_utils.run_string_to_list(child.find('ids').attrib['val'])]
+    return names, ids
+
+
+def _get_pairs_from_XML(root):
+    names, groups, alphas = [], [], []
+    for child in root:
+        if child.tag == "pair":
+            names += [child.attrib['name']]
+            groups += [[child.find('forward-group').attrib['val'], child.find('backward-group').attrib['val']]]
+            alphas += [child.find('alpha').attrib['val']]
+    return names, groups, alphas
diff --git a/scripts/Muon/GUI/ElementalAnalysis/Plotting/edit_windows/remove_plot_window.py b/scripts/Muon/GUI/ElementalAnalysis/Plotting/edit_windows/remove_plot_window.py
index 7b3f6707dc28965b1cfd1f5209b248dbd3f9c5e4..76cb69c918f0d1e6f6bc0cda5b6e9b5731caa47f 100644
--- a/scripts/Muon/GUI/ElementalAnalysis/Plotting/edit_windows/remove_plot_window.py
+++ b/scripts/Muon/GUI/ElementalAnalysis/Plotting/edit_windows/remove_plot_window.py
@@ -2,7 +2,7 @@ from __future__ import (absolute_import, division, print_function)
 
 from qtpy import QtCore, QtWidgets
 
-from Muon.GUI.Common import table_utils
+from Muon.GUI.Common.utilities import table_utils
 
 
 class RemovePlotWindowView(QtWidgets.QDialog):
diff --git a/scripts/Muon/GUI/FrequencyDomainAnalysis/FFT/fft_model.py b/scripts/Muon/GUI/FrequencyDomainAnalysis/FFT/fft_model.py
index 8959f9b2798530296e2bd76c2fab5b2e379098df..74de38354799a8a87756e286eea59d4cc18cbb7a 100644
--- a/scripts/Muon/GUI/FrequencyDomainAnalysis/FFT/fft_model.py
+++ b/scripts/Muon/GUI/FrequencyDomainAnalysis/FFT/fft_model.py
@@ -100,6 +100,8 @@ class FFTModel(object):
         self.alg = mantid.AlgorithmManager.create("PaddingAndApodization")
         self.alg.initialize()
         self.alg.setAlwaysStoreInADS(False)
+        self.alg.setRethrows(True)
+
         for name, value in iteritems(preInputs):
             self.alg.setProperty(name, value)
         self.alg.execute()
@@ -115,6 +117,8 @@ class FFTModel(object):
         self.alg = mantid.AlgorithmManager.create("FFT")
         self.alg.initialize()
         self.alg.setAlwaysStoreInADS(False)
+        self.alg.setRethrows(True)
+
         for name, value in iteritems(FFTInputs):
             self.alg.setProperty(name, value)
         self.alg.execute()
@@ -134,6 +138,7 @@ class FFTModel(object):
         self.alg = mantid.AlgorithmManager.create("CalMuonDetectorPhases")
         self.alg.initialize()
         self.alg.setAlwaysStoreInADS(False)
+        self.alg.setRethrows(True)
 
         self.alg.setProperty("FirstGoodData", inputs["FirstGoodData"])
         self.alg.setProperty("LastGoodData", inputs["LastGoodData"])
@@ -141,6 +146,7 @@ class FFTModel(object):
         self.alg.setProperty("InputWorkspace", "MuonAnalysis")
         self.alg.setProperty("DetectorTable", "PhaseTable")
         self.alg.setProperty("DataFitted", "fits")
+
         self.alg.execute()
         mantid.AnalysisDataService.addOrReplace(
             "PhaseTable",
@@ -155,6 +161,8 @@ class FFTModel(object):
         self.alg = mantid.AlgorithmManager.create("PhaseQuad")
         self.alg.initialize()
         self.alg.setChild(False)
+        self.alg.setRethrows(True)
+
         self.alg.setProperty("InputWorkspace", "MuonAnalysis")
         self.alg.setProperty("PhaseTable", "PhaseTable")
         self.alg.setProperty("OutputWorkspace", "__phaseQuad__")
diff --git a/scripts/Muon/GUI/FrequencyDomainAnalysis/FFT/fft_view.py b/scripts/Muon/GUI/FrequencyDomainAnalysis/FFT/fft_view.py
index 447c31c0150c6111af11b5ba1dbe625353017b07..4bbf5400c663af285cd8de3a50de38f7bc902b8e 100644
--- a/scripts/Muon/GUI/FrequencyDomainAnalysis/FFT/fft_view.py
+++ b/scripts/Muon/GUI/FrequencyDomainAnalysis/FFT/fft_view.py
@@ -10,7 +10,7 @@ from PyQt4 import QtCore, QtGui
 
 import mantid.simpleapi as mantid
 
-from Muon.GUI.Common import table_utils
+from Muon.GUI.Common.utilities import table_utils
 
 
 class FFTView(QtGui.QWidget):
diff --git a/scripts/Muon/GUI/FrequencyDomainAnalysis/MaxEnt/maxent_model.py b/scripts/Muon/GUI/FrequencyDomainAnalysis/MaxEnt/maxent_model.py
index 730a758d520c4361c4ad457ad084d9ff20b40ae8..836f1fc541fb6e27486e9f488356fdb6a5d41a50 100644
--- a/scripts/Muon/GUI/FrequencyDomainAnalysis/MaxEnt/maxent_model.py
+++ b/scripts/Muon/GUI/FrequencyDomainAnalysis/MaxEnt/maxent_model.py
@@ -72,6 +72,7 @@ class MaxEntModel(object):
         self.alg.setAlwaysStoreInADS(False)
         for name, value in iteritems(inputs):
             self.alg.setProperty(name, value)
+        self.alg.setRethrows(True)
         self.alg.execute()
         self.addOutput(inputs, self.alg, "OutputWorkspace")
         self.addOutput(inputs, self.alg, "OutputPhaseTable")
@@ -87,6 +88,7 @@ class MaxEntModel(object):
         self.alg = mantid.AlgorithmManager.create("CalMuonDetectorPhases")
         self.alg.initialize()
         self.alg.setAlwaysStoreInADS(False)
+        self.alg.setRethrows(True)
 
         for name, value in iteritems(inputs):
             self.alg.setProperty(name, value)
diff --git a/scripts/Muon/GUI/FrequencyDomainAnalysis/MaxEnt/maxent_view.py b/scripts/Muon/GUI/FrequencyDomainAnalysis/MaxEnt/maxent_view.py
index 56cd75fa3b307f454bfc32a7dced3e122f585213..3db2d608b3c127e59414102369f6e7c69dc65572 100644
--- a/scripts/Muon/GUI/FrequencyDomainAnalysis/MaxEnt/maxent_view.py
+++ b/scripts/Muon/GUI/FrequencyDomainAnalysis/MaxEnt/maxent_view.py
@@ -8,7 +8,7 @@ from __future__ import (absolute_import, division, print_function)
 
 from PyQt4 import QtCore, QtGui
 
-from Muon.GUI.Common import table_utils
+from Muon.GUI.Common.utilities import table_utils
 
 
 class MaxEntView(QtGui.QWidget):
diff --git a/scripts/Muon/GUI/MuonAnalysis/dock/dock_widget.py b/scripts/Muon/GUI/MuonAnalysis/dock/dock_widget.py
index e50f98460c84b56208cd81c02810e86bed32cdca..77ea9f91fee04583bcb31f6baafcd80066e31758 100644
--- a/scripts/Muon/GUI/MuonAnalysis/dock/dock_widget.py
+++ b/scripts/Muon/GUI/MuonAnalysis/dock/dock_widget.py
@@ -75,9 +75,9 @@ class DockWidget(QtGui.QWidget):
         self.label.updateContext()
         self.context_example.updateContext()
 
-    def loadFromContext(self, context):
-        self.label.loadFromContext(context)
-        self.context_example.loadFromContext(context)
+    def loadFromContext(self):
+        self.label.loadFromContext()
+        self.context_example.loadFromContext()
 
     # needed for docking
     @property
diff --git a/scripts/Muon_Analysis_2.py b/scripts/Muon_Analysis_2.py
index c7f9f016ba68db1fb96ec1b9d3328540454abde5..54d8b2cfe9ae8c3aa5b0f4a61de3dcb227fbcfc7 100644
--- a/scripts/Muon_Analysis_2.py
+++ b/scripts/Muon_Analysis_2.py
@@ -14,7 +14,10 @@ import PyQt4.QtCore as QtCore
 
 from Muon.GUI.Common.dummy_label.dummy_label_widget import DummyLabelWidget
 from Muon.GUI.MuonAnalysis.dock.dock_widget import DockWidget
-from Muon.GUI.Common.muon_context.muon_context import *#MuonContext
+from Muon.GUI.Common.muon_context.muon_context import *  # MuonContext
+from save_python import getWidgetIfOpen
+
+Name = "Muon_Analysis_2"
 
 muonGUI = None
 
@@ -24,12 +27,12 @@ class MuonAnalysis2Gui(QtGui.QMainWindow):
     def __init__(self, parent=None):
         super(MuonAnalysis2Gui, self).__init__(parent)
 
-        self._context = MuonContext()
+        self._context = MuonContext(Name)
 
-        self.loadWidget = DummyLabelWidget(self._context ,LoadText, self)
-        self.dockWidget = DockWidget(self._context,self)
+        self.loadWidget = DummyLabelWidget(self._context, LoadText, self)
+        self.dockWidget = DockWidget(self._context, self)
 
-        self.helpWidget = DummyLabelWidget(self._context,HelpText, self)
+        self.helpWidget = DummyLabelWidget(self._context, HelpText, self)
 
         splitter = QtGui.QSplitter(QtCore.Qt.Vertical)
         splitter.addWidget(self.loadWidget.widget)
@@ -37,23 +40,32 @@ class MuonAnalysis2Gui(QtGui.QMainWindow):
         splitter.addWidget(self.helpWidget.widget)
 
         self.setCentralWidget(splitter)
-        self.setWindowTitle("Muon Analysis version 2")
+        self.setWindowTitle(Name)
 
         self.dockWidget.setUpdateContext(self.update)
 
+    def saveToProject(self):
+        return self._context.save()
+
     def update(self):
         # update load
         self.loadWidget.updateContext()
         self.dockWidget.updateContext()
         self.helpWidget.updateContext()
 
-        self._context.printContext()
-        self.dockWidget.loadFromContext(self._context)
+        self.dockWidget.loadFromContext()
+
+    def loadFromContext(self, project):
+        self._context.loadFromProject(project)
+        self.loadWidget.loadFromContext()
+        self.dockWidget.loadFromContext()
+        self.helpWidget.loadFromContext()
 
     # cancel algs if window is closed
     def closeEvent(self, event):
         self.dockWidget.closeEvent(event)
         global muonGUI
+        muonGUI.deleteLater()
         muonGUI = None
 
 
@@ -66,31 +78,43 @@ def qapp():
 
 
 def main():
+    widget = getWidgetIfOpen(Name)
+    if widget is not None:
+        # if GUI is open bring to front
+        widget.raise_()
+        return widget
     app = qapp()
     try:
-        global muonGUI
-        muonGUI = MuonAnalysis2Gui()
-        muonGUI.resize(700, 700)
-        muonGUI.show()
+        muon = MuonAnalysis2Gui()
+        muon.resize(700, 700)
+        muon.setProperty("launcher", Name)
+        muon.show()
+        muon.setAccessibleName(Name)
         app.exec_()
-        return muonGUI
+        return muon
     except RuntimeError as error:
-        muonGUI = QtGui.QWidget()
-        QtGui.QMessageBox.warning(muonGUI, "Muon Analysis version 2", str(error))
-        return muonGUI
+        muon = QtGui.QWidget()
+        QtGui.QMessageBox.warning(muon, Name, str(error))
+        return muon
 
 
 def saveToProject():
-    if muonGUI is None:
+    widget = getWidgetIfOpen(Name)
+    if widget is None:
         return ""
-    project = "test"
+    widget.update()
+    project = widget.saveToProject()
     return project
 
 
 def loadFromProject(project):
+    global muonGUI
     muonGUI = main()
-    muonGUI.dockWidget.loadFromProject(project)
+    muonGUI.loadFromContext(project)
     return muonGUI
 
 if __name__ == '__main__':
-    muonGUI = main()
+    muon = main()
+    # cannot assign straight to muonGUI
+    # prevents reopening to the same GUI
+    muonGUI = muon
diff --git a/scripts/SANS/sans/algorithm_detail/batch_execution.py b/scripts/SANS/sans/algorithm_detail/batch_execution.py
index ca1d56c742f2c02f871eaf0b5d7cf0bea06d221b..6a5fd12eda8b9fa3bd11d4a3dd5ad6e4f50691a4 100644
--- a/scripts/SANS/sans/algorithm_detail/batch_execution.py
+++ b/scripts/SANS/sans/algorithm_detail/batch_execution.py
@@ -13,8 +13,10 @@ from sans.common.enums import (SANSDataType, SaveType, OutputMode, ISISReduction
 from sans.common.constants import (TRANS_SUFFIX, SANS_SUFFIX, ALL_PERIODS,
                                    LAB_CAN_SUFFIX, LAB_CAN_COUNT_SUFFIX, LAB_CAN_NORM_SUFFIX,
                                    HAB_CAN_SUFFIX, HAB_CAN_COUNT_SUFFIX, HAB_CAN_NORM_SUFFIX,
+                                   LAB_SAMPLE_SUFFIX, HAB_SAMPLE_SUFFIX,
                                    REDUCED_HAB_AND_LAB_WORKSPACE_FOR_MERGED_REDUCTION,
-                                   REDUCED_CAN_AND_PARTIAL_CAN_FOR_OPTIMIZATION)
+                                   CAN_COUNT_AND_NORM_FOR_OPTIMIZATION,
+                                   CAN_AND_SAMPLE_WORKSPACE)
 from sans.common.file_information import (get_extension_for_file_type, SANSFileInformationFactory)
 from sans.state.data import StateData
 try:
@@ -28,7 +30,7 @@ except (Exception, Warning):
 # ----------------------------------------------------------------------------------------------------------------------
 # Functions for the execution of a single batch iteration
 # ----------------------------------------------------------------------------------------------------------------------
-def single_reduction_for_batch(state, use_optimizations, output_mode, plot_results, output_graph):
+def single_reduction_for_batch(state, use_optimizations, output_mode, plot_results, output_graph, save_can=False):
     """
     Runs a single reduction.
 
@@ -39,6 +41,7 @@ def single_reduction_for_batch(state, use_optimizations, output_mode, plot_resul
     :param state: a SANSState object
     :param use_optimizations: if true then the optimizations of child algorithms are enabled.
     :param output_mode: the output mode
+    :param save_can: bool. whether or not to save out can workspaces
     """
     # ------------------------------------------------------------------------------------------------------------------
     # Load the data
@@ -65,7 +68,8 @@ def single_reduction_for_batch(state, use_optimizations, output_mode, plot_resul
     # Run reductions (one at a time)
     # ------------------------------------------------------------------------------------------------------------------
     single_reduction_name = "SANSSingleReduction"
-    single_reduction_options = {"UseOptimizations": use_optimizations}
+    single_reduction_options = {"UseOptimizations": use_optimizations,
+                                "SaveCan": save_can}
     reduction_alg = create_managed_non_child_algorithm(single_reduction_name, **single_reduction_options)
     reduction_alg.setChild(False)
     # Perform the data reduction
@@ -107,6 +111,9 @@ def single_reduction_for_batch(state, use_optimizations, output_mode, plot_resul
         reduction_package.unfitted_transmission_can = get_workspace_from_algorithm(reduction_alg,
                                                                                    "OutputWorkspaceUnfittedTransmissionCan")
 
+        reduction_package.reduced_lab_sample = get_workspace_from_algorithm(reduction_alg, "OutputWorkspaceLABSample")
+        reduction_package.reduced_hab_sample = get_workspace_from_algorithm(reduction_alg, "OutputWorkspaceHABSample")
+
         reduction_package.out_scale_factor = reduction_alg.getProperty("OutScaleFactor").value
         reduction_package.out_shift_factor = reduction_alg.getProperty("OutShiftFactor").value
 
@@ -115,7 +122,7 @@ def single_reduction_for_batch(state, use_optimizations, output_mode, plot_resul
         # -----------------------------------
         # The workspaces are already on the ADS, but should potentially be grouped
         # -----------------------------------
-        group_workspaces_if_required(reduction_package)
+        group_workspaces_if_required(reduction_package, output_mode, save_can)
 
     # --------------------------------
     # Perform output of all workspaces
@@ -129,18 +136,17 @@ def single_reduction_for_batch(state, use_optimizations, output_mode, plot_resul
     # 3. Both:
     #    * This means that we need to save out the reduced data
     #    * The data is already on the ADS, so do nothing
-
     if output_mode is OutputMode.SaveToFile:
-        save_to_file(reduction_packages)
+        save_to_file(reduction_packages, save_can)
         delete_reduced_workspaces(reduction_packages)
     elif output_mode is OutputMode.Both:
-        save_to_file(reduction_packages)
+        save_to_file(reduction_packages, save_can)
 
     # -----------------------------------------------------------------------
     # Clean up other workspaces if the optimizations have not been turned on.
     # -----------------------------------------------------------------------
     if not use_optimizations:
-        delete_optimization_workspaces(reduction_packages, workspaces, monitors)
+        delete_optimization_workspaces(reduction_packages, workspaces, monitors, save_can)
 
     out_scale_factors = [reduction_package.out_scale_factor for reduction_package in reduction_packages]
     out_shift_factors = [reduction_package.out_shift_factor for reduction_package in reduction_packages]
@@ -148,6 +154,20 @@ def single_reduction_for_batch(state, use_optimizations, output_mode, plot_resul
     return out_scale_factors, out_shift_factors
 
 
+def load_workspaces_from_states(state):
+    workspace_to_name = {SANSDataType.SampleScatter: "SampleScatterWorkspace",
+                         SANSDataType.SampleTransmission: "SampleTransmissionWorkspace",
+                         SANSDataType.SampleDirect: "SampleDirectWorkspace",
+                         SANSDataType.CanScatter: "CanScatterWorkspace",
+                         SANSDataType.CanTransmission: "CanTransmissionWorkspace",
+                         SANSDataType.CanDirect: "CanDirectWorkspace"}
+
+    workspace_to_monitor = {SANSDataType.SampleScatter: "SampleScatterMonitorWorkspace",
+                            SANSDataType.CanScatter: "CanScatterMonitorWorkspace"}
+
+    workspaces, monitors = provide_loaded_data(state, True, workspace_to_name, workspace_to_monitor)
+
+
 # ----------------------------------------------------------------------------------------------------------------------
 # Function for plotting
 # ----------------------------------------------------------------------------------------------------------------------
@@ -691,6 +711,10 @@ def set_properties_for_reduction_algorithm(reduction_alg, reduction_package, wor
                          "OutputWorkspaceLABCanNorm", "reduced_lab_can_norm_name", "reduced_lab_can_norm_base_name",
                          multi_reduction_type, LAB_CAN_NORM_SUFFIX)
 
+        _set_output_name(_reduction_alg, _reduction_package, _is_group, ISISReductionMode.LAB,
+                         "OutputWorkspaceLABSample", "reduced_lab_sample_name", "reduced_lab_sample_base_name",
+                         multi_reduction_type, LAB_SAMPLE_SUFFIX)
+
     def _set_hab(_reduction_alg, _reduction_package, _is_group):
         # Hab Can Workspace
         _set_output_name(_reduction_alg, _reduction_package, _is_group, ISISReductionMode.HAB,
@@ -707,6 +731,10 @@ def set_properties_for_reduction_algorithm(reduction_alg, reduction_package, wor
                          "OutputWorkspaceHABCanNorm", "reduced_hab_can_norm_name", "reduced_hab_can_norm_base_name",
                          multi_reduction_type, HAB_CAN_NORM_SUFFIX)
 
+        _set_output_name(_reduction_alg, _reduction_package, _is_group, ISISReductionMode.HAB,
+                         "OutputWorkspaceHABSample", "reduced_hab_sample_name", "reduced_hab_sample_base_name",
+                         multi_reduction_type, HAB_SAMPLE_SUFFIX)
+
     # Go through the elements of the reduction package and set them on the reduction algorithm
     # Set the SANSState
     state = reduction_package.state
@@ -833,7 +861,7 @@ def get_workspace_from_algorithm(alg, output_property_name):
 # ----------------------------------------------------------------------------------------------------------------------
 # Functions for outputs to the ADS and saving the file
 # ----------------------------------------------------------------------------------------------------------------------
-def group_workspaces_if_required(reduction_package):
+def group_workspaces_if_required(reduction_package, output_mode, save_can):
     """
     The output workspaces have already been published to the ADS by the algorithm. Now we might have to
     bundle them into a group if:
@@ -841,6 +869,8 @@ def group_workspaces_if_required(reduction_package):
     * They are reduced LAB and HAB workspaces of a Merged reduction
     * They are can workspaces - they are all grouped into a single group
     :param reduction_package: a list of reduction packages
+    :param output_mode: one of OutputMode. SaveToFile, PublishToADS, Both.
+    :param save_can: a bool. If true save out can and sample workspaces.
     """
     is_part_of_multi_period_reduction = reduction_package.is_part_of_multi_period_reduction
     is_part_of_event_slice_reduction = reduction_package.is_part_of_event_slice_reduction
@@ -868,14 +898,31 @@ def group_workspaces_if_required(reduction_package):
             add_to_group(reduced_lab, reduction_package.reduced_lab_base_name)
             add_to_group(reduced_hab, reduction_package.reduced_hab_base_name)
 
+    # Can group workspace depends on if save_can is checked and output_mode
+    # Logic table for which group to save CAN into
+    # CAN | FILE | In OPTIMIZATION group
+    # ----------------------------------
+    #  Y  |   Y  | YES
+    #  N  |   Y  | YES
+    #  Y  |   N  | NO
+    #  N  |   N  | YES
+
+    if save_can and output_mode is not OutputMode.SaveToFile:
+        CAN_WORKSPACE_GROUP = CAN_AND_SAMPLE_WORKSPACE
+    else:
+        CAN_WORKSPACE_GROUP = CAN_COUNT_AND_NORM_FOR_OPTIMIZATION
+
     # Add the can workspaces (used for optimizations) to a Workspace Group (if they exist)
-    add_to_group(reduction_package.reduced_lab_can, REDUCED_CAN_AND_PARTIAL_CAN_FOR_OPTIMIZATION)
-    add_to_group(reduction_package.reduced_lab_can_count, REDUCED_CAN_AND_PARTIAL_CAN_FOR_OPTIMIZATION)
-    add_to_group(reduction_package.reduced_lab_can_norm, REDUCED_CAN_AND_PARTIAL_CAN_FOR_OPTIMIZATION)
+    add_to_group(reduction_package.reduced_lab_can, CAN_WORKSPACE_GROUP)
+    add_to_group(reduction_package.reduced_lab_can_count, CAN_COUNT_AND_NORM_FOR_OPTIMIZATION)
+    add_to_group(reduction_package.reduced_lab_can_norm, CAN_COUNT_AND_NORM_FOR_OPTIMIZATION)
+
+    add_to_group(reduction_package.reduced_hab_can, CAN_WORKSPACE_GROUP)
+    add_to_group(reduction_package.reduced_hab_can_count, CAN_COUNT_AND_NORM_FOR_OPTIMIZATION)
+    add_to_group(reduction_package.reduced_hab_can_norm, CAN_COUNT_AND_NORM_FOR_OPTIMIZATION)
 
-    add_to_group(reduction_package.reduced_hab_can, REDUCED_CAN_AND_PARTIAL_CAN_FOR_OPTIMIZATION)
-    add_to_group(reduction_package.reduced_hab_can_count, REDUCED_CAN_AND_PARTIAL_CAN_FOR_OPTIMIZATION)
-    add_to_group(reduction_package.reduced_hab_can_norm, REDUCED_CAN_AND_PARTIAL_CAN_FOR_OPTIMIZATION)
+    add_to_group(reduction_package.reduced_lab_sample, CAN_AND_SAMPLE_WORKSPACE)
+    add_to_group(reduction_package.reduced_hab_sample, CAN_AND_SAMPLE_WORKSPACE)
 
     if reduction_package.state.adjustment.show_transmission:
         add_to_group(reduction_package.calculated_transmission, reduction_package.calculated_transmission_base_name)
@@ -918,13 +965,14 @@ def add_to_group(workspace, name_of_group_workspace):
         group_alg.execute()
 
 
-def save_to_file(reduction_packages):
+def save_to_file(reduction_packages, save_can):
     """
     Extracts all workspace names which need to be saved and saves them into a file.
 
     @param reduction_packages: a list of reduction packages which contain all the relevant information for saving
+    @param save_can: a bool. When true save the unsubtracted can and sample workspaces
     """
-    workspaces_names_to_save = get_all_names_to_save(reduction_packages)
+    workspaces_names_to_save = get_all_names_to_save(reduction_packages, save_can=save_can)
 
     state = reduction_packages[0].state
     save_info = state.save
@@ -954,10 +1002,26 @@ def delete_reduced_workspaces(reduction_packages):
         reduced_lab = reduction_package.reduced_lab
         reduced_hab = reduction_package.reduced_hab
         reduced_merged = reduction_package.reduced_merged
-        _delete_workspaces(delete_alg, [reduced_lab, reduced_hab, reduced_merged])
 
+        # Remove samples
+        reduced_lab_sample = reduction_package.reduced_lab_sample
+        reduced_hab_sample = reduction_package.reduced_hab_sample
+
+        # Remove transmissions
+        calculated_transmission = reduction_package.calculated_transmission
+        unfitted_transmission = reduction_package.unfitted_transmission
+        calculated_transmission_can = reduction_package.calculated_transmission_can
+        unfitted_transmission_can = reduction_package.unfitted_transmission_can
 
-def delete_optimization_workspaces(reduction_packages, workspaces, monitors):
+        workspaces_to_delete = [reduced_lab, reduced_hab, reduced_merged,
+                                reduced_lab_sample, reduced_hab_sample,
+                                calculated_transmission, unfitted_transmission,
+                                calculated_transmission_can, unfitted_transmission_can]
+
+        _delete_workspaces(delete_alg, workspaces_to_delete)
+
+
+def delete_optimization_workspaces(reduction_packages, workspaces, monitors, save_can):
     """
     Deletes all workspaces which are used for optimizations. This can be loaded workspaces or can optimizations
 
@@ -992,20 +1056,22 @@ def delete_optimization_workspaces(reduction_packages, workspaces, monitors):
 
     for reduction_package in reduction_packages:
         # Delete can optimizations
-        optimizations_to_delete = [reduction_package.reduced_lab_can,
-                                   reduction_package.reduced_lab_can_count,
+        optimizations_to_delete = [reduction_package.reduced_lab_can_count,
                                    reduction_package.reduced_lab_can_norm,
-                                   reduction_package.reduced_hab_can,
                                    reduction_package.reduced_hab_can_count,
                                    reduction_package.reduced_hab_can_norm]
+        if not save_can:
+            optimizations_to_delete.extend([reduction_package.reduced_lab_can,
+                                            reduction_package.reduced_hab_can])
         _delete_workspaces(delete_alg, optimizations_to_delete)
 
 
-def get_all_names_to_save(reduction_packages):
+def get_all_names_to_save(reduction_packages, save_can):
     """
     Extracts all the output names from a list of reduction packages. The main
 
     @param reduction_packages: a list of reduction packages
+    @param save_can: a bool, whether or not to save unsubtracted can workspace
     @return: a list of workspace names to save.
     """
     names_to_save = []
@@ -1013,9 +1079,29 @@ def get_all_names_to_save(reduction_packages):
         reduced_lab = reduction_package.reduced_lab
         reduced_hab = reduction_package.reduced_hab
         reduced_merged = reduction_package.reduced_merged
+        reduced_lab_can = reduction_package.reduced_lab_can
+        reduced_hab_can = reduction_package.reduced_hab_can
+        reduced_lab_sample = reduction_package.reduced_lab_sample
+        reduced_hab_sample = reduction_package.reduced_hab_sample
+
+        if save_can:
+            if reduced_merged:
+                names_to_save.append(reduced_merged.name())
+            if reduced_lab:
+                names_to_save.append(reduced_lab.name())
+            if reduced_hab:
+                names_to_save.append(reduced_hab.name())
+            if reduced_lab_can:
+                names_to_save.append(reduced_lab_can.name())
+            if reduced_hab_can:
+                names_to_save.append(reduced_hab_can.name())
+            if reduced_lab_sample:
+                names_to_save.append(reduced_lab_sample.name())
+            if reduced_hab_sample:
+                names_to_save.append(reduced_hab_sample.name())
 
         # If we have merged reduction then store the
-        if reduced_merged:
+        elif reduced_merged:
             names_to_save.append(reduced_merged.name())
         else:
             if reduced_lab:
diff --git a/scripts/SANS/sans/algorithm_detail/save_workspace.py b/scripts/SANS/sans/algorithm_detail/save_workspace.py
index 12dfc4740648530b74e54e52e400581201a840b4..5c5050c0ea474be332df58c16b3768a6d81d9955 100644
--- a/scripts/SANS/sans/algorithm_detail/save_workspace.py
+++ b/scripts/SANS/sans/algorithm_detail/save_workspace.py
@@ -11,6 +11,7 @@ from mantid.dataobjects import EventWorkspace
 from sans.common.general_functions import create_unmanaged_algorithm
 from sans.common.constants import EMPTY_NAME
 from sans.common.enums import SaveType
+# from sans.algorithm_detail.strip_end_nans_and_infs import strip_end_nans
 
 ZERO_ERROR_DEFAULT = 1e6
 
@@ -102,6 +103,11 @@ def remove_zero_errors_from_workspace(workspace):
     if not isinstance(workspace, MatrixWorkspace) or isinstance(workspace,EventWorkspace):
         raise ValueError('Cannot remove zero errors from a workspace which is not a MatrixWorkspace.')
 
+    # Uncomment the next line and tests fail for checking error values should not be zero, and
+    # comparing loaded workspace to calculated workspace. If we want to remove RuntimeWarning for nan
+    # values strip_end_nans should be moved up the workflow
+    # workspace = strip_end_nans(workspace, None)
+
     # Iterate over the workspace and replace the zero values with a large default value
     number_of_spectra = workspace.getNumberHistograms()
     errors = workspace.dataE
diff --git a/scripts/SANS/sans/common/constants.py b/scripts/SANS/sans/common/constants.py
index d788403722ce7bf169ec0ee9f1ad0abe7fd07e61..22bf6eb4774ea79d5a57d90d9687e79362dc14df 100644
--- a/scripts/SANS/sans/common/constants.py
+++ b/scripts/SANS/sans/common/constants.py
@@ -58,10 +58,14 @@ CALIBRATION_WORKSPACE_TAG = "sans_applied_calibration_file"
 LAB_CAN_SUFFIX = "_lab_can"
 LAB_CAN_COUNT_SUFFIX = "_lab_can_count"
 LAB_CAN_NORM_SUFFIX = "_lab_can_norm"
+LAB_SAMPLE_SUFFIX = "_lab_sample"
 
 HAB_CAN_SUFFIX = "_hab_can"
 HAB_CAN_COUNT_SUFFIX = "_hab_can_count"
 HAB_CAN_NORM_SUFFIX = "_hab_can_norm"
+HAB_SAMPLE_SUFFIX = "_hab_sample"
 
 REDUCED_HAB_AND_LAB_WORKSPACE_FOR_MERGED_REDUCTION = "LAB_and_HAB_workspaces_from_merged_reduction"
 REDUCED_CAN_AND_PARTIAL_CAN_FOR_OPTIMIZATION = "reduced_can_and_partial_can_workspaces"
+CAN_COUNT_AND_NORM_FOR_OPTIMIZATION = "optimization"
+CAN_AND_SAMPLE_WORKSPACE = "unsubtracted_can_and_sam"
diff --git a/scripts/SANS/sans/common/general_functions.py b/scripts/SANS/sans/common/general_functions.py
index c710030f8a2301f270399ba076a2ba34c927edd3..8c75e8d1ce900887c908d92b23bebe7f470e8a44 100644
--- a/scripts/SANS/sans/common/general_functions.py
+++ b/scripts/SANS/sans/common/general_functions.py
@@ -493,9 +493,9 @@ def parse_event_slice_setting(string_to_parse):
         line = re.sub(range_marker_pattern, "", line)
         value = float(line)
         if is_lower_bound:
-            return [value, -1]
+            return [value, -1.]
         else:
-            return [-1, value]
+            return [-1., value]
 
     # Check if the input actually exists.
     if not string_to_parse:
diff --git a/scripts/SANS/sans/gui_logic/gui_common.py b/scripts/SANS/sans/gui_logic/gui_common.py
index 3603957bc11b4797aced669c34755da3a6a6bdfa..7bbaa0b6ad0e0eb5ee601823051e94cdf16aa60d 100644
--- a/scripts/SANS/sans/gui_logic/gui_common.py
+++ b/scripts/SANS/sans/gui_logic/gui_common.py
@@ -169,6 +169,8 @@ def get_instrument_from_gui_selection(gui_selection):
         return SANSInstrument.SANS2D
     elif gui_selection == 'ZOOM':
         return SANSInstrument.ZOOM
+    elif gui_selection == 'NoInstrument':
+        return SANSInstrument.NoInstrument
     else:
         raise RuntimeError("Instrument selection is not valid.")
 
diff --git a/scripts/SANS/sans/gui_logic/models/batch_process_runner.py b/scripts/SANS/sans/gui_logic/models/batch_process_runner.py
index bcad3c229214c162f5ca9c7fcdaa908df969aa4c..6408b14f0df9927e804de5daf4e6f460d8a40953 100644
--- a/scripts/SANS/sans/gui_logic/models/batch_process_runner.py
+++ b/scripts/SANS/sans/gui_logic/models/batch_process_runner.py
@@ -6,6 +6,7 @@
 # SPDX - License - Identifier: GPL - 3.0 +
 from PyQt4.QtCore import pyqtSlot, QThreadPool, pyqtSignal, QObject
 from sans.sans_batch import SANSBatchReduction
+from sans.algorithm_detail.batch_execution import load_workspaces_from_states
 from ui.sans_isis.worker import Worker
 from sans.common.enums import ISISReductionMode
 
@@ -32,19 +33,27 @@ class BatchProcessRunner(QObject):
     def on_error(self, error):
         self._worker = None
 
-    def process_states(self, states, use_optimizations, output_mode, plot_results, output_graph):
+    def process_states(self, states, use_optimizations, output_mode, plot_results, output_graph, save_can=False):
         self._worker = Worker(self._process_states_on_thread, states, use_optimizations, output_mode, plot_results,
-                              output_graph)
+                              output_graph, save_can)
         self._worker.signals.finished.connect(self.on_finished)
         self._worker.signals.error.connect(self.on_error)
 
         QThreadPool.globalInstance().start(self._worker)
 
-    def _process_states_on_thread(self, states, use_optimizations, output_mode, plot_results, output_graph):
+    def load_workspaces(self, states):
+        self._worker = Worker(self._load_workspaces_on_thread, states)
+        self._worker.signals.finished.connect(self.on_finished)
+        self._worker.signals.error.connect(self.on_error)
+
+        QThreadPool.globalInstance().start(self._worker)
+
+    def _process_states_on_thread(self, states, use_optimizations, output_mode, plot_results, output_graph,
+                                  save_can=False):
         for key, state in states.items():
             try:
                 out_scale_factors, out_shift_factors = \
-                    self.batch_processor([state], use_optimizations, output_mode, plot_results, output_graph)
+                    self.batch_processor([state], use_optimizations, output_mode, plot_results, output_graph, save_can)
                 if state.reduction.reduction_mode == ISISReductionMode.Merged:
                     out_shift_factors = out_shift_factors[0]
                     out_scale_factors = out_scale_factors[0]
@@ -52,5 +61,14 @@ class BatchProcessRunner(QObject):
                     out_shift_factors = []
                     out_scale_factors = []
                 self.row_processed_signal.emit(key, out_shift_factors, out_scale_factors)
+
+            except Exception as e:
+                self.row_failed_signal.emit(key, str(e))
+
+    def _load_workspaces_on_thread(self, states):
+        for key, state in states.items():
+            try:
+                load_workspaces_from_states(state)
+                self.row_processed_signal.emit(key, [], [])
             except Exception as e:
                 self.row_failed_signal.emit(key, str(e))
diff --git a/scripts/SANS/sans/gui_logic/models/state_gui_model.py b/scripts/SANS/sans/gui_logic/models/state_gui_model.py
index dd3a0f5933b5f156368d83bed49256451640b19c..a6c09a8fa75c91b317f74251479b3cf3e34b1e59 100644
--- a/scripts/SANS/sans/gui_logic/models/state_gui_model.py
+++ b/scripts/SANS/sans/gui_logic/models/state_gui_model.py
@@ -17,7 +17,7 @@ from sans.user_file.settings_tags import (OtherId, DetectorId, LimitsId, SetId,
                                           monitor_spectrum, simple_range, monitor_file, det_fit_range,
                                           q_rebin_values, fit_general, mask_angle_entry, range_entry, position_entry)
 from sans.common.enums import (ReductionDimensionality, ISISReductionMode, RangeStepType, SaveType,
-                               DetectorType, DataType, FitType)
+                               DetectorType, DataType, FitType, SANSInstrument)
 
 
 class StateGuiModel(object):
@@ -53,13 +53,16 @@ class StateGuiModel(object):
     # FRONT TAB
     # ==================================================================================================================
     # ==================================================================================================================
+    @property
+    def instrument(self):
+        return self.get_simple_element(element_id=DetectorId.instrument, default_value=SANSInstrument.NoInstrument)
 
     # ------------------------------------------------------------------------------------------------------------------
     # Compatibility Mode Options
     # ------------------------------------------------------------------------------------------------------------------
     @property
     def compatibility_mode(self):
-        return self.get_simple_element(element_id=OtherId.use_compatibility_mode, default_value=False)
+        return self.get_simple_element(element_id=OtherId.use_compatibility_mode, default_value=True)
 
     @compatibility_mode.setter
     def compatibility_mode(self, value):
diff --git a/scripts/SANS/sans/gui_logic/presenter/run_tab_presenter.py b/scripts/SANS/sans/gui_logic/presenter/run_tab_presenter.py
index b6ae9778012bcec03524ebc61a4d762b462785ee..8045593f010d781a440838b5ded3698515bbc634 100644
--- a/scripts/SANS/sans/gui_logic/presenter/run_tab_presenter.py
+++ b/scripts/SANS/sans/gui_logic/presenter/run_tab_presenter.py
@@ -26,8 +26,8 @@ from sans.gui_logic.presenter.settings_diagnostic_presenter import (SettingsDiag
 from sans.gui_logic.presenter.masking_table_presenter import (MaskingTablePresenter)
 from sans.gui_logic.presenter.beam_centre_presenter import BeamCentrePresenter
 from sans.gui_logic.presenter.add_runs_presenter import OutputDirectoryObserver as SaveDirectoryObserver
-from sans.gui_logic.gui_common import (get_reduction_mode_strings_for_gui, get_instrument_strings_for_gui)
-from sans.common.enums import (BatchReductionEntry, RangeStepType, SampleShape, FitType, RowState)
+from sans.gui_logic.gui_common import (get_reduction_mode_strings_for_gui)
+from sans.common.enums import (BatchReductionEntry, RangeStepType, SampleShape, FitType, RowState, SANSInstrument)
 from sans.user_file.user_file_reader import UserFileReader
 from sans.command_interface.batch_csv_file_parser import BatchCsvParser
 from sans.common.constants import ALL_PERIODS
@@ -67,6 +67,9 @@ class RunTabPresenter(object):
         def on_processed_clicked(self):
             self._presenter.on_processed_clicked()
 
+        def on_load_clicked(self):
+            self._presenter.on_load_clicked()
+
         def on_multi_period_selection(self, show_periods):
             self._presenter.on_multiperiod_changed(show_periods)
 
@@ -167,10 +170,6 @@ class RunTabPresenter(object):
         reduction_mode_list = get_reduction_mode_strings_for_gui()
         self._view.set_reduction_modes(reduction_mode_list)
 
-        # Set the possible instruments
-        instrument_list = get_instrument_strings_for_gui()
-        self._view.set_instruments(instrument_list)
-
         # Set the step type options for wavelength
         range_step_types = [RangeStepType.to_string(RangeStepType.Lin),
                             RangeStepType.to_string(RangeStepType.Log),
@@ -222,6 +221,7 @@ class RunTabPresenter(object):
 
             # Default gui setup
             self._default_gui_setup()
+            self._view.disable_process_buttons()
 
             # Set appropriate view for the state diagnostic tab presenter
             self._settings_diagnostic_tab_presenter.set_view(self._view.settings_diagnostic_tab)
@@ -277,6 +277,10 @@ class RunTabPresenter(object):
             self._masking_table_presenter.on_update_rows()
             self._workspace_diagnostic_presenter.on_user_file_load(user_file_path)
 
+            # 6. Warning if user file did not contain a recognised instrument
+            if self._view.instrument == SANSInstrument.NoInstrument:
+                raise RuntimeError("User file did not contain a SANS Instrument.")
+
         except Exception as e:
             self.sans_logger.error("Loading of the user file failed. {}".format(str(e)))
             self.display_warning_box('Warning', 'Loading of the user file failed.', str(e))
@@ -389,10 +393,7 @@ class RunTabPresenter(object):
             self.sans_logger.information("Starting processing of batch table.")
 
             # 1. Set up the states and convert them into property managers
-            selected_rows = self._view.get_selected_rows()
-            selected_rows = selected_rows if selected_rows else range(self._table_model.get_number_of_rows())
-            for row in selected_rows:
-                self._table_model.reset_row_state(row)
+            selected_rows = self._get_selected_rows()
             states, errors = self.get_states(row_index=selected_rows)
 
             for row, error in errors.items():
@@ -417,19 +418,48 @@ class RunTabPresenter(object):
             # Check if results should be plotted
             plot_results = self._view.plot_results
 
+            save_can = self._view.save_can
+
             # Get the name of the graph to output to
             output_graph = self.output_graph
 
             self.progress = 0
             setattr(self._view, 'progress_bar_value', self.progress)
             setattr(self._view, 'progress_bar_maximum', len(states))
-            self.batch_process_runner.process_states(states, use_optimizations, output_mode, plot_results, output_graph)
+
+            self.batch_process_runner.process_states(states, use_optimizations, output_mode, plot_results,
+                                                     output_graph, save_can)
 
         except Exception as e:
             self._view.enable_buttons()
             self.sans_logger.error("Process halted due to: {}".format(str(e)))
             self.display_warning_box('Warning', 'Process halted', str(e))
 
+    def on_load_clicked(self):
+        try:
+            self._view.disable_buttons()
+            self._processing = True
+            self.sans_logger.information("Starting load of batch table.")
+
+            selected_rows = self._get_selected_rows()
+            states, errors = self.get_states(row_index=selected_rows)
+
+            for row, error in errors.items():
+                self.on_processing_error(row, error)
+
+            if not states:
+                self.on_processing_finished(None)
+                return
+
+            self.progress = 0
+            setattr(self._view, 'progress_bar_value', self.progress)
+            setattr(self._view, 'progress_bar_maximum', len(states))
+            self.batch_process_runner.load_workspaces(states)
+        except Exception as e:
+            self._view.enable_buttons()
+            self.sans_logger.error("Process halted due to: {}".format(str(e)))
+            self.display_warning_box("Warning", "Process halted", str(e))
+
     def on_multiperiod_changed(self, show_periods):
         if show_periods:
             self._view.show_period_columns()
@@ -581,6 +611,15 @@ class RunTabPresenter(object):
     # ------------------------------------------------------------------------------------------------------------------
     # Table Model and state population
     # ------------------------------------------------------------------------------------------------------------------
+    def _get_selected_rows(self):
+        selected_rows = self._view.get_selected_rows()
+        selected_rows = selected_rows if selected_rows else range(self._table_model.get_number_of_rows())
+        for row in selected_rows:
+            self._table_model.reset_row_state(row)
+        self.update_view_from_table_model()
+
+        return selected_rows
+
     def get_states(self, row_index=None, file_lookup=True):
         """
         Gathers the state information for all rows.
@@ -608,7 +647,7 @@ class RunTabPresenter(object):
 
         if errors:
             self.sans_logger.warning("Errors in getting states...")
-            for _, v in errors.item():
+            for _, v in errors.items():
                 self.sans_logger.warning("{}".format(v))
 
         return states, errors
@@ -630,6 +669,8 @@ class RunTabPresenter(object):
         return None
 
     def _update_view_from_state_model(self):
+        self._set_on_view("instrument")
+
         # Front tab view
         self._set_on_view("zero_error_free")
         self._set_on_view("save_types")
@@ -983,6 +1024,10 @@ class RunTabPresenter(object):
         if not instrument:
             instrument = self._view.instrument
 
+        if instrument == SANSInstrument.NoInstrument:
+            self._view.disable_process_buttons()
+        else:
+            self._view.enable_process_buttons()
         self._view.set_instrument_settings(instrument)
         self._beam_centre_presenter.on_update_instrument(instrument)
         self._workspace_diagnostic_presenter.set_instrument_settings(instrument)
diff --git a/scripts/SANS/sans/sans_batch.py b/scripts/SANS/sans/sans_batch.py
index 20a71a1582100d78d6d3e5c230607c525a3805df..77820a4d6711296e2cf3c4ed3b853dff3a40f34d 100644
--- a/scripts/SANS/sans/sans_batch.py
+++ b/scripts/SANS/sans/sans_batch.py
@@ -17,7 +17,8 @@ class SANSBatchReduction(object):
     def __init__(self):
         super(SANSBatchReduction, self).__init__()
 
-    def __call__(self, states, use_optimizations=True, output_mode=OutputMode.PublishToADS, plot_results = False, output_graph=''):
+    def __call__(self, states, use_optimizations=True, output_mode=OutputMode.PublishToADS, plot_results = False,
+                 output_graph='', save_can=False):
         """
         This is the start of any reduction.
 
@@ -30,16 +31,17 @@ class SANSBatchReduction(object):
         """
         self.validate_inputs(states, use_optimizations, output_mode, plot_results, output_graph)
 
-        return self._execute(states, use_optimizations, output_mode, plot_results, output_graph)
+        return self._execute(states, use_optimizations, output_mode, plot_results, output_graph, save_can=save_can)
 
     @staticmethod
-    def _execute(states, use_optimizations, output_mode, plot_results, output_graph):
+    def _execute(states, use_optimizations, output_mode, plot_results, output_graph, save_can=False):
         # Iterate over each state, load the data and perform the reduction
         out_scale_factors_list = []
         out_shift_factors_list = []
         for state in states:
             out_scale_factors, out_shift_factors = \
-                single_reduction_for_batch(state, use_optimizations, output_mode, plot_results, output_graph)
+                single_reduction_for_batch(state, use_optimizations, output_mode, plot_results, output_graph,
+                                           save_can=save_can)
             out_shift_factors_list.append(out_shift_factors)
             out_scale_factors_list.append(out_scale_factors)
         return out_scale_factors_list, out_shift_factors_list
diff --git a/scripts/SANS/sans/test_helper/user_file_test_helper.py b/scripts/SANS/sans/test_helper/user_file_test_helper.py
index 46f4dc000acd27bae955d2c7bb203636780084c9..78badedbcefc8f53c5e6ce8aa919b8531739ff4c 100644
--- a/scripts/SANS/sans/test_helper/user_file_test_helper.py
+++ b/scripts/SANS/sans/test_helper/user_file_test_helper.py
@@ -83,6 +83,82 @@ base_user_file = ("PRINT for changer\n"
                   "TUBECALIBFILE=TUBE_SANS2D_BOTH_31681_25Sept15.nxs"
                   )
 
+base_user_file_with_instrument = ("PRINT for changer\n"
+                                  "SANS2D"
+                                  "MASK/CLEAR \n"
+                                  "MASK/CLEAR/TIME\n"
+                                  "L/WAV 1.5 12.5 0.125/LIN\n"
+                                  "L/Q .001,.001, .0126, -.08, .2\n"
+                                  "!L/Q .001 .8 .08/log\n"
+                                  "L/QXY 0 0.05 .001/lin\n"
+                                  "BACK/M1 35000 65000\n"
+                                  "BACK/M2 85000 98000\n"
+                                  "BACK/MON/TIMES 3500 4500\n"
+                                  "BACK/TRANS 123 466\n"
+                                  "DET/REAR\n"
+                                  "GRAVITY/{}\n"
+                                  "!FIT/TRANS/OFF\n"
+                                  "FIT/TRANS/LOG 1.5 12.5\n"
+                                  "FIT/MONITOR 1000 2000\n"
+                                  "mask/rear h0\n"
+                                  "mask/rear h190>h191\n"
+                                  "mask/rear h167>h172\n"
+                                  "mask/rear v0\n"
+                                  "mask/rear v191\n"
+                                  "mask/front h0\n"
+                                  "mask/front h190>h191\n"
+                                  "mask/front v0\n"
+                                  "mask/front v191\n"
+                                  "! dead wire near top\n"
+                                  "mask/front h156>h159\n"
+                                  "!masking off beamstop arm - 12mm wide @ 19degrees\n"
+                                  "!mask/rear/line 12 19\n"
+                                  "! spot on rhs beam stop at 11m\n"
+                                  "! mask h57>h66+v134>v141\n"
+                                  "!\n"
+                                  "! mask for Bragg at 12m, 26/03/11, 3 time channels\n"
+                                  "mask/time 17500 22000\n"
+                                  "!\n"
+                                  "L/R 12 15\n"
+                                  "L/Q/RCut 200\n"
+                                  "L/Q/WCut 8.0\n"
+                                  "!PRINT REMOVED RCut=200 WCut=8\n"
+                                  "!\n"
+                                  "MON/DIRECT=DIRECTM1_15785_12m_31Oct12_v12.dat\n"
+                                  "MON/TRANS/SPECTRUM=1/INTERPOLATE\n"
+                                  "MON/SPECTRUM=1/INTERPOLATE\n"
+                                  "!TRANS/TRANSPEC=3\n"
+                                  "TRANS/TRANSPEC=4/SHIFT=-70\n"
+                                  "TRANS/RADIUS=7.0\n"
+                                  "TRANS/ROI=test.xml, test2.xml\n"
+                                  "TRANS/MASK=test3.xml, test4.xml\n"
+                                  "!\n"
+                                  "set centre 155.45 -169.6\n"
+                                  "!\n"
+                                  "! 25/10/13 centre gc 22021, fit gdw20 22023\n"
+                                  "set scales 0.074 1.0 1.0 1.0 1.0\n"
+                                  "! correction to actual sample position, notionally 81mm before shutter\n"
+                                  "SAMPLE/OFFSET +53.0\n"
+                                  "! Correction to SANS2D encoders in mm\n"
+                                  "DET/CORR/REAR/X -16.0\n"
+                                  "DET/CORR/REAR/Z 47.0\n"
+                                  "DET/CORR/FRONT/X -44.0\n"
+                                  "DET/CORR/FRONT/Y -20.0\n"
+                                  "DET/CORR/FRONT/Z 47.0\n"
+                                  "DET/CORR/FRONT/ROT 0.0\n"
+                                  "!\n"
+                                  "!! 01/10/13 MASKSANS2d_133F M3 by M1 trans Hellsing, Rennie, Jackson, L1=L2=12m A1=20 and A2=8mm\n"
+                                  "L/EVENTSTIME 7000.0,500.0,60000.0\n"
+                                  "SAMPLE/PATH/ON\n"
+                                  "QRESOL/ON \n"
+                                  "QRESOL/DELTAR=11 \n"
+                                  "QRESOL/LCOLLIM=12 \n"
+                                  "QRESOL/MODERATOR=moderator_rkh_file.txt\n"
+                                  "QRESOL/A1=13\n"
+                                  "QRESOL/A2=14\n"
+                                  "TUBECALIBFILE=TUBE_SANS2D_BOTH_31681_25Sept15.nxs"
+                                  )
+
 
 def create_user_file(user_file_content):
     temp = tempfile.NamedTemporaryFile(mode='w+', delete=False)
@@ -97,3 +173,5 @@ def make_sample_user_file(gravity ='ON'):
 
 sample_user_file = make_sample_user_file(gravity ='ON')
 sample_user_file_gravity_OFF = make_sample_user_file(gravity ='OFF')
+
+sample_user_file_with_instrument = base_user_file_with_instrument.format("OFF")
diff --git a/scripts/SANS/sans/user_file/settings_tags.py b/scripts/SANS/sans/user_file/settings_tags.py
index 835614a81cf783fe63494bd7888acfca0477870f..e98b35aea4cf5387c3513f5d1cfab406bd36ee10 100644
--- a/scripts/SANS/sans/user_file/settings_tags.py
+++ b/scripts/SANS/sans/user_file/settings_tags.py
@@ -59,7 +59,7 @@ det_fit_range = namedtuple('det_fit_range', 'start, stop, use_fit')
 # --- DET
 @serializable_enum("reduction_mode", "rescale", "shift", "rescale_fit", "shift_fit", "correction_x", "correction_y",
                    "correction_z", "correction_rotation", "correction_radius", "correction_translation",
-                   "correction_x_tilt", "correction_y_tilt", "merge_range")
+                   "correction_x_tilt", "correction_y_tilt", "merge_range", "instrument")
 class DetectorId(object):
     pass
 
diff --git a/scripts/SANS/sans/user_file/user_file_parser.py b/scripts/SANS/sans/user_file/user_file_parser.py
index df62789e3c51840dfc7551d1abd17d32b3d6b228..3fc89d1237cff0ae8be47879c7ece340c48981f7 100644
--- a/scripts/SANS/sans/user_file/user_file_parser.py
+++ b/scripts/SANS/sans/user_file/user_file_parser.py
@@ -12,7 +12,7 @@ import re
 from math import copysign
 
 
-from sans.common.enums import (ISISReductionMode, DetectorType, RangeStepType, FitType, DataType)
+from sans.common.enums import (ISISReductionMode, DetectorType, RangeStepType, FitType, DataType, SANSInstrument)
 from sans.user_file.settings_tags import (DetectorId, BackId, range_entry, back_single_monitor_entry,
                                           single_entry_with_detector, mask_angle_entry, LimitsId,
                                           simple_range, complex_range, MaskId, mask_block, mask_block_cross,
@@ -251,6 +251,44 @@ class BackParser(UserFileComponentParser):
         return "\\s*" + BackParser.get_type() + "\\s*/\\s*"
 
 
+class InstrParser(object):
+    """
+    InstrParser looks to find the instrument.
+    Compared to other parsers, this is a naive implementation
+    which expects a line in the user file to explicitly state the instrument,
+    with no other data.
+    Because of this, we are trying to match exact strings, and so do not use regex.
+    """
+    Type = "INSTR"
+    _INSTRUMENTS = ["LOQ", "LARMOR", "SANS2D", "ZOOM", "NOINSTRUMENT"]
+
+    @staticmethod
+    def parse_line(line):
+        if line == "LOQ":
+            ret_val = SANSInstrument.LOQ
+        elif line == "LARMOR":
+            ret_val = SANSInstrument.LARMOR
+        elif line == "SANS2D":
+            ret_val = SANSInstrument.SANS2D
+        elif line == "ZOOM":
+            ret_val = SANSInstrument.ZOOM
+        else:
+            raise RuntimeError("InstrParser: Unknown command for INSTR: {0}".format(line))
+
+        return {DetectorId.instrument: ret_val}
+
+    @staticmethod
+    def get_type():
+        return InstrParser.Type
+
+    @staticmethod
+    def get_type_pattern(line):
+        if line in InstrParser._INSTRUMENTS:
+            return True
+        else:
+            return False
+
+
 class DetParser(UserFileComponentParser):
     """
     The DetParser handles the following structure
@@ -2306,6 +2344,10 @@ class UserFileParser(object):
     def _get_correct_parser(self, line):
         line = line.strip()
         line = line.upper()
+
+        if InstrParser.get_type_pattern(line):
+            return InstrParser()
+
         for key in self._parsers:
             parser = self._parsers[key]
             if re.match(parser.get_type_pattern(), line, re.IGNORECASE) is not None:
diff --git a/scripts/pythonTSV.py b/scripts/pythonTSV.py
new file mode 100644
index 0000000000000000000000000000000000000000..f13ca614ae5c6b93953d2886f9c2bb709a605737
--- /dev/null
+++ b/scripts/pythonTSV.py
@@ -0,0 +1,79 @@
+# Mantid Repository : https://github.com/mantidproject/mantid
+#
+# Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
+#     NScD Oak Ridge National Laboratory, European Spallation Source
+#     & Institut Laue - Langevin
+# SPDX - License - Identifier: GPL - 3.0 +
+# pylint: disable=invalid-name
+from __future__ import (absolute_import, division, print_function)
+
+"""
+Some simple helpers for dealing with the TSV
+"""
+
+
+def saveToTSV(TSV, value):
+    """
+    This will record the value based on its type,
+    will only record default TSV data types
+    """
+    if isinstance(value, int):
+        TSV.storeInt(value)
+    elif isinstance(value, float):
+        TSV.storeDouble(value)
+    elif isinstance(value, bool):
+        TSV.storeBool(value)
+    elif isinstance(value, str):
+        TSV.storeString(value)
+    else:
+        raise TypeError("Value is not recognised by TSVSerialiser")
+
+
+def loadFromTSV(TSV, key, value):
+    """
+    Will return the stored data from a TSV
+    with from the line called key and with
+    a data type of value
+    """
+    safeKey = makeLineNameSafe(key)
+    TSV.selectLine(safeKey)
+    if isinstance(value, int):
+        return TSV.readInt()
+    elif isinstance(value, float):
+        return TSV.readDouble()
+    elif isinstance(value, bool):
+        return TSV.readBool()
+    elif isinstance(value, str):
+        return TSV.readString()
+    else:
+        raise TypeError("Value is not recognised by TSVSerialiser")
+
+"""
+The line name cannot contain:
+spaces, underscores or dashes
+"""
+
+
+def makeLineNameSafe(oldName):
+    newName = removeUnsafeCharacter(oldName, " ")
+    newName = removeUnsafeCharacter(newName, "_")
+    newName = removeUnsafeCharacter(newName, "-")
+    return newName
+
+
+"""
+write a lines and make sure it is safe
+"""
+
+
+def writeLine(TSV, name):
+    newName = makeLineNameSafe(name)
+    TSV.writeLine(newName)
+
+
+def removeUnsafeCharacter(oldName, character):
+    tmp = oldName.split(character)
+    newName = ""
+    for word in tmp:
+        newName += word[0].upper() + word[1:]
+    return newName
diff --git a/scripts/save_python.py b/scripts/save_python.py
new file mode 100644
index 0000000000000000000000000000000000000000..b4674eb9f83c730accb3f32ed9e0f46a5ff0ebe5
--- /dev/null
+++ b/scripts/save_python.py
@@ -0,0 +1,18 @@
+# Mantid Repository : https://github.com/mantidproject/mantid
+#
+# Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
+#     NScD Oak Ridge National Laboratory, European Spallation Source
+#     & Institut Laue - Langevin
+# SPDX - License - Identifier: GPL - 3.0 +
+# pylint: disable=invalid-name
+from __future__ import (absolute_import, division, print_function)
+
+import PyQt4.QtGui as QtGui
+
+
+def getWidgetIfOpen(name):
+    allWidgets = QtGui.QApplication.allWidgets()
+    for widget in allWidgets:
+        if widget.accessibleName() == name:
+            return widget
+    return None
diff --git a/scripts/test/Muon/AxisChangerView_test.py b/scripts/test/Muon/AxisChangerView_test.py
index 1dc81dca66934e5fd6e15d31deee34a0be7e9c4c..c7608bca328057cf8da79575d6b40a3aae9b3230 100644
--- a/scripts/test/Muon/AxisChangerView_test.py
+++ b/scripts/test/Muon/AxisChangerView_test.py
@@ -6,11 +6,7 @@
 # SPDX - License - Identifier: GPL - 3.0 +
 import unittest
 
-import os
-os.environ["QT_API"] = "pyqt"  # noqa E402
-
 from Muon.GUI.ElementalAnalysis.Plotting.AxisChanger.axis_changer_view import AxisChangerView
-
 from Muon.GUI.Common import mock_widget
 
 try:
diff --git a/scripts/test/Muon/CMakeLists.txt b/scripts/test/Muon/CMakeLists.txt
index 45e5f0f795dc814fda051ca5922c620249710b03..bd26e5cf9f5cea842597acd8e0c5fa630cbab0f1 100644
--- a/scripts/test/Muon/CMakeLists.txt
+++ b/scripts/test/Muon/CMakeLists.txt
@@ -17,11 +17,13 @@ set ( TEST_PY_FILES
    PlottingPresenter_test.py
    PlottingUtils_test.py
    PlottingView_test.py
-   #thread_model_test.py
    transformWidget_test.py
+   utilities/load_utils_test.py
+   utilities/thread_model_test.py
    utilities/muon_workspace_wrapper_test.py
    utilities/muon_workspace_wrapper_directory_test.py
    subplotObject_test.py
+   muon_load_data_test.py
 )
 
 check_tests_valid ( ${CMAKE_CURRENT_SOURCE_DIR} ${TEST_PY_FILES} )
diff --git a/scripts/test/Muon/FFTPresenter_test.py b/scripts/test/Muon/FFTPresenter_test.py
index 275a9674e7826aa6f3161d5b2d2023f2e68ca7b7..e0a1f94ac0923ed21d1d530055bbb71da7ac1439 100644
--- a/scripts/test/Muon/FFTPresenter_test.py
+++ b/scripts/test/Muon/FFTPresenter_test.py
@@ -6,7 +6,7 @@
 # SPDX - License - Identifier: GPL - 3.0 +
 import sys
 
-from Muon.GUI.Common import load_utils
+from Muon.GUI.Common.utilities import load_utils
 from Muon.GUI.Common import thread_model
 from Muon.GUI.FrequencyDomainAnalysis.FFT import fft_presenter
 from Muon.GUI.FrequencyDomainAnalysis.FFT import fft_view
diff --git a/scripts/test/Muon/MaxEntPresenter_test.py b/scripts/test/Muon/MaxEntPresenter_test.py
index 198ec6956ada869c93ca316b6efc2a44a1c937bf..d3941394c1bca927285bfa60dff17c253b689070 100644
--- a/scripts/test/Muon/MaxEntPresenter_test.py
+++ b/scripts/test/Muon/MaxEntPresenter_test.py
@@ -8,7 +8,7 @@ from __future__ import (absolute_import, division, print_function)
 
 import sys
 
-from  Muon.GUI.Common import load_utils
+from  Muon.GUI.Common.utilities import load_utils
 from  Muon.GUI.Common import thread_model
 from  Muon.GUI.FrequencyDomainAnalysis.MaxEnt import maxent_presenter
 from  Muon.GUI.FrequencyDomainAnalysis.MaxEnt import maxent_view
diff --git a/scripts/test/Muon/PeriodicTablePresenter_test.py b/scripts/test/Muon/PeriodicTablePresenter_test.py
index 11bc54c7b41cfaa39f969f2cc2d077134ca900a1..97eb33ff8603937e7cf0a14dcdf919974d4c6f91 100644
--- a/scripts/test/Muon/PeriodicTablePresenter_test.py
+++ b/scripts/test/Muon/PeriodicTablePresenter_test.py
@@ -23,8 +23,12 @@ except ImportError:
 
 
 class PeriodicTablePresenterTest(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        cls.qapp = mock_widget.mockQapp()
+
     def setUp(self):
-        self._qapp = mock_widget.mockQapp()
         self._model = mock.create_autospec(PeriodicTableModel)
         self.view = PeriodicTableView()
         self.presenter = PeriodicTablePresenter(
diff --git a/scripts/test/Muon/muon_load_data_test.py b/scripts/test/Muon/muon_load_data_test.py
new file mode 100644
index 0000000000000000000000000000000000000000..d6e3a47f0264d226fa6741c24f0e29946fa478fa
--- /dev/null
+++ b/scripts/test/Muon/muon_load_data_test.py
@@ -0,0 +1,66 @@
+# Mantid Repository : https://github.com/mantidproject/mantid
+#
+# Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
+#     NScD Oak Ridge National Laboratory, European Spallation Source
+#     & Institut Laue - Langevin
+# SPDX - License - Identifier: GPL - 3.0 +
+import unittest
+import sys
+from Muon.GUI.Common.muon_load_data import MuonLoadData
+
+if sys.version_info.major == 3:
+    from unittest import mock
+else:
+    import mock
+
+
+class MuonLoadDataTest(unittest.TestCase):
+    def setUp(self):
+        self.muon_load_data = MuonLoadData()
+        self.workspace = mock.MagicMock()
+        self.workspace_last = mock.MagicMock()
+        self.muon_load_data.add_data(run=1, workspace=mock.MagicMock(), filename='path to file')
+        self.muon_load_data.add_data(run=2, workspace=self.workspace, filename='path to file')
+        self.muon_load_data.add_data(run=3, workspace=mock.MagicMock(), filename='matching path')
+        self.muon_load_data.add_data()
+        self.muon_load_data.add_data(run=4, workspace=self.workspace_last, filename='path to file')
+
+    def test_that_can_add_data_to_struct(self):
+        self.assertEqual(self.muon_load_data.num_items(), 5)
+        self.assertEqual(self.muon_load_data.get_parameter('run')[2], 3)
+        self.assertEqual(self.muon_load_data.get_parameter('workspace')[1], self.workspace)
+        self.assertEqual(self.muon_load_data.get_parameter('filename')[0], 'path to file')
+
+    def test_that_matches_returns_true_for_all_entries_with_one_match(self):
+        match_list = self.muon_load_data._matches(run=1, workspace=self.workspace, filename='matching path')
+
+        self.assertEqual(match_list, [True, True, True, False, False])
+
+    def test_that_matches_with_no_params_matches_none(self):
+        match_list = self.muon_load_data._matches()
+
+        self.assertEqual(match_list, [False, False, False, False, False])
+
+    def test_that_matches_with_unused_parameters_match_none(self):
+        match_list = self.muon_load_data._matches(new_info='new info')
+
+        self.assertEqual(match_list, [False, False, False, False, False])
+
+    def test_that_matches_correctly_with_only_one_parameter_given(self):
+        match_list = self.muon_load_data._matches(filename='path to file')
+
+        self.assertEqual(match_list, [True, True, False, False, True])
+
+    def test_that_get_data_returns_correct_dict(self):
+        data_dict = self.muon_load_data.get_data(run=2)
+
+        self.assertEqual(data_dict, {'workspace': self.workspace, 'filename': 'path to file', 'run': 2})
+
+    def test_that_get_latest_data_returns_correct_dict(self):
+        data_dict = self.muon_load_data.get_latest_data()
+
+        self.assertEqual(data_dict, {'workspace': self.workspace_last, 'filename': 'path to file', 'run': 4})
+
+
+if __name__ == '__main__':
+    unittest.main(buffer=False, verbosity=2)
diff --git a/scripts/test/Muon/transformWidget_test.py b/scripts/test/Muon/transformWidget_test.py
index bef68d09245d885626a1b3ee0ccc03101d69d316..165b13c2634e9f1e9553fa9151dd58e374e34508 100644
--- a/scripts/test/Muon/transformWidget_test.py
+++ b/scripts/test/Muon/transformWidget_test.py
@@ -7,7 +7,7 @@
 import sys
 
 from  Muon.GUI.Common import mock_widget
-from  Muon.GUI.Common import load_utils
+from  Muon.GUI.Common.utilities import load_utils
 from  Muon.GUI.FrequencyDomainAnalysis.FFT import fft_presenter
 from  Muon.GUI.FrequencyDomainAnalysis.Transform import transform_widget
 from  Muon.GUI.FrequencyDomainAnalysis.Transform import transform_view
diff --git a/scripts/test/Muon/utilities/load_utils_test.py b/scripts/test/Muon/utilities/load_utils_test.py
new file mode 100644
index 0000000000000000000000000000000000000000..918d98fe9310d017ed0a9430cc0fe1c98be0eb5e
--- /dev/null
+++ b/scripts/test/Muon/utilities/load_utils_test.py
@@ -0,0 +1,85 @@
+# Mantid Repository : https://github.com/mantidproject/mantid
+#
+# Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
+#     NScD Oak Ridge National Laboratory, European Spallation Source
+#     & Institut Laue - Langevin
+# SPDX - License - Identifier: GPL - 3.0 +
+import Muon.GUI.Common.utilities.load_utils as utils
+import sys
+import os
+from mantid import simpleapi, ConfigService
+from mantid.api import AnalysisDataService, ITableWorkspace
+
+import unittest
+
+if sys.version_info.major > 2:
+    from unittest import mock
+else:
+    import mock
+
+def create_simple_workspace(data_x, data_y, run_number=0):
+    alg = simpleapi.AlgorithmManager.create("CreateWorkspace")
+    alg.initialize()
+    alg.setAlwaysStoreInADS(False)
+    alg.setLogging(False)
+    alg.setProperty("dataX", data_x)
+    alg.setProperty("dataY", data_y)
+    alg.setProperty("OutputWorkspace", "__notUsed")
+    alg.execute()
+    ws = alg.getProperty("OutputWorkspace").value
+    ws.getRun().addProperty('run_number', run_number, 'NonDim', True)
+    return ws
+
+class MuonFileUtilsTest(unittest.TestCase):
+    def test_get_run_from_multi_period_data(self):
+        simple_workspace = create_simple_workspace(data_x=[1,2,3,4], data_y=[10,20,30,40], run_number=74044)
+        workspace_list = [simple_workspace] * 5
+
+        run_number = utils.get_run_from_multi_period_data(workspace_list)
+
+        self.assertEqual(run_number, 74044)
+
+    def test_get_run_from_multi_period_data_raises_a_value_error_if_not_all_run_numbers_same(self):
+        simple_workspace = create_simple_workspace(data_x=[1, 2, 3, 4], data_y=[10, 20, 30, 40], run_number=74044)
+        simple_workspace_1 = create_simple_workspace(data_x=[1, 2, 3, 4], data_y=[10, 20, 30, 40], run_number=74045)
+        workspace_list = [simple_workspace] * 4 + [simple_workspace_1]
+
+        self.assertRaises(ValueError, utils.get_run_from_multi_period_data, workspace_list)
+
+    def test_default_instrument_returns_default_instrument_if_muon_instrument(self):
+        ConfigService['default.instrument'] = 'MUSR'
+
+        instrument = utils.get_default_instrument()
+
+        self.assertEqual(instrument, 'MUSR')
+
+    def test_default_instrument_returns_MUSR_if_default_instrument_is_not_muon_instrument(self):
+        ConfigService['default.instrument'] = 'LOQ'
+
+        instrument = utils.get_default_instrument()
+
+        self.assertEqual(instrument, 'MUSR')
+
+    def test_that_load_dead_time_from_filename_places_table_in_ADS(self):
+        filename = 'MUSR00022725.nsx'
+
+        name = utils.load_dead_time_from_filename(filename)
+        dead_time_table = AnalysisDataService.retrieve(name)
+
+        self.assertEqual(name, 'MUSR00022725_deadTimes')
+        self.assertTrue(AnalysisDataService.doesExist(name))
+        self.assertTrue(isinstance(dead_time_table, ITableWorkspace))
+
+    def test_load_workspace_from_filename_for_existing_file(self):
+        filename = 'MUSR00022725.nsx'
+        load_result, run, filename = utils.load_workspace_from_filename(filename)
+
+        self.assertEqual(load_result['DeadTimeTable'], None)
+        self.assertEqual(load_result['FirstGoodData'], 0.656)
+        self.assertEqual(load_result['MainFieldDirection'], 'Transverse')
+        self.assertAlmostEqual(load_result['TimeZero'], 0.55000, 5)
+        self.assertEqual(run, 22725)
+
+
+if __name__ == '__main__':
+    unittest.main(buffer=False, verbosity=2)
\ No newline at end of file
diff --git a/scripts/test/Muon/thread_model_test.py b/scripts/test/Muon/utilities/thread_model_test.py
similarity index 85%
rename from scripts/test/Muon/thread_model_test.py
rename to scripts/test/Muon/utilities/thread_model_test.py
index cd27478545698f2fc34820b8ff71e0ea8ec5963f..1c32fa0698b2012eaf192ab81e0939289487098a 100644
--- a/scripts/test/Muon/thread_model_test.py
+++ b/scripts/test/Muon/utilities/thread_model_test.py
@@ -6,16 +6,14 @@
 # SPDX - License - Identifier: GPL - 3.0 +
 import unittest
 import sys
+from Muon.GUI.Common.thread_model import ThreadModel
+from Muon.GUI.Common import mock_widget
 
 if sys.version_info.major == 3:
     from unittest import mock
 else:
     import mock
 
-from Muon.GUI.Common.thread_model import ThreadModel
-from Muon.GUI.Common import mock_widget
-from PyQt4.QtGui import QApplication
-
 
 class testModelWithoutExecute:
 
@@ -69,19 +67,14 @@ class LoadFileWidgetViewTest(unittest.TestCase):
         The finished signal of a QThread is connected to the finished method below"""
         QT_APP = mock_widget.mockQapp()
 
-        def __init__(self, thread):
-            if thread:
-                self._thread = thread
-                self._thread.finished.connect(self.finished)
-                self._thread.start()
-                if self._thread.isRunning():
-                    self.QT_APP.exec_()
-
-        def finished(self):
-            self.QT_APP.processEvents()
-            self.QT_APP.exit(0)
+        def __init__(self, thread_model):
+            if thread_model:
+                thread_model.start()
 
     def setUp(self):
+        patcher = mock.patch('Muon.GUI.Common.thread_model.warning')
+        self.addCleanup(patcher.stop)
+        self.warning_box_patcher = patcher.start()
         self.model = testModel()
         self.thread = ThreadModel(self.model)
 
@@ -108,6 +101,8 @@ class LoadFileWidgetViewTest(unittest.TestCase):
         self.model.execute = mock.Mock()
 
         self.Runner(self.thread)
+        self.thread._thread.wait()
+        self.Runner.QT_APP.processEvents()
 
         self.assertEqual(self.model.execute.call_count, 1)
 
@@ -116,6 +111,8 @@ class LoadFileWidgetViewTest(unittest.TestCase):
         self.model.output = mock.Mock()
 
         self.Runner(self.thread)
+        self.thread._thread.wait()
+        self.Runner.QT_APP.processEvents()
 
         self.assertEqual(self.model.output.call_count, 1)
 
@@ -126,6 +123,8 @@ class LoadFileWidgetViewTest(unittest.TestCase):
         self.thread.threadWrapperSetUp(start_slot, end_slot)
 
         self.Runner(self.thread)
+        self.thread._thread.wait()
+        self.Runner.QT_APP.processEvents()
 
         self.assertEqual(start_slot.call_count, 1)
         self.assertEqual(end_slot.call_count, 1)
@@ -156,24 +155,23 @@ class LoadFileWidgetViewTest(unittest.TestCase):
         self.thread.threadWrapperSetUp(start_slot, end_slot)
 
         self.Runner(self.thread)
-        self.Runner(self.thread)
+        self.thread._thread.wait()
+        self.Runner.QT_APP.processEvents()
 
         self.assertEqual(start_slot.call_count, 1)
         self.assertEqual(end_slot.call_count, 1)
 
-    @mock.patch("Muon.GUI.Common.message_box.warning")
-    def test_that_message_box_called_when_execute_throws_even_without_setup_and_teardown_methods(self, mock_box):
-        # Need to instantiate a new thread for patch to work
-        self.thread = ThreadModel(self.model)
-
+    def test_that_message_box_called_when_execute_throws_even_without_setup_and_teardown_methods(self):
         def raise_error():
             raise ValueError()
 
         self.model.execute = mock.Mock(side_effect=raise_error)
 
         self.Runner(self.thread)
+        self.thread._thread.wait()
+        self.Runner.QT_APP.processEvents()
 
-        self.assertEqual(mock_box.call_count, 1)
+        self.assertEqual(self.warning_box_patcher.call_count, 1)
 
     def test_that_passing_non_callables_to_setUp_throws_AssertionError(self):
 
diff --git a/scripts/test/SANS/algorithm_detail/batch_execution_test.py b/scripts/test/SANS/algorithm_detail/batch_execution_test.py
index 85d13a06c264e0fb2c5679412f965c0d7b869f47..a97eda860080637512a79d10a960ea5f6a78a40f 100644
--- a/scripts/test/SANS/algorithm_detail/batch_execution_test.py
+++ b/scripts/test/SANS/algorithm_detail/batch_execution_test.py
@@ -14,43 +14,72 @@ if sys.version_info.major > 2:
 else:
     import mock
 
+
 class GetAllNamesToSaveTest(unittest.TestCase):
-    def test_returns_merged_name_if_present(self):
+    def setUp(self):
         state = mock.MagicMock()
         workspaces = ['Sample', 'Transmission', 'Direct']
         monitors = ['monitor1']
-        reduction_package = ReductionPackage(state, workspaces, monitors)
+        self.reduction_package_merged = ReductionPackage(state, workspaces, monitors)
+        self.reduction_package = ReductionPackage(state, workspaces, monitors)
         merged_workspace = CreateSampleWorkspace(Function='Flat background', NumBanks=1, BankPixelWidth=1, NumEvents=1,
-                                   XMin=1, XMax=14, BinWidth=2)
+                                                 XMin=1, XMax=14, BinWidth=2)
         lab_workspace = CreateSampleWorkspace(Function='Flat background', NumBanks=1, BankPixelWidth=1, NumEvents=1,
-                                   XMin=1, XMax=14, BinWidth=2)
+                                              XMin=1, XMax=14, BinWidth=2)
         hab_workspace = CreateSampleWorkspace(Function='Flat background', NumBanks=1, BankPixelWidth=1, NumEvents=1,
-                                   XMin=1, XMax=14, BinWidth=2)
-        reduction_package.reduced_merged = merged_workspace
-        reduction_package.reduced_lab = lab_workspace
-        reduction_package.reduced_hab = hab_workspace
-        reduction_packages = [reduction_package]
+                                              XMin=1, XMax=14, BinWidth=2)
+        reduced_lab_can = CreateSampleWorkspace(Function='Flat background', NumBanks=1, BankPixelWidth=1, NumEvents=1,
+                                                XMin=1, XMax=14, BinWidth=2)
+        reduced_hab_can = CreateSampleWorkspace(Function='Flat background', NumBanks=1, BankPixelWidth=1, NumEvents=1,
+                                                XMin=1, XMax=14, BinWidth=2)
+        reduced_lab_sample = CreateSampleWorkspace(Function='Flat background', NumBanks=1, BankPixelWidth=1, NumEvents=1,
+                                                XMin=1, XMax=14, BinWidth=2)
+        reduced_hab_sample = CreateSampleWorkspace(Function='Flat background', NumBanks=1, BankPixelWidth=1, NumEvents=1,
+                                                XMin=1, XMax=14, BinWidth=2)
+        self.reduction_package_merged.reduced_merged = merged_workspace
+        self.reduction_package_merged.reduced_lab = lab_workspace
+        self.reduction_package_merged.reduced_hab = hab_workspace
+        self.reduction_package_merged.reduced_lab_can = reduced_lab_can
+        self.reduction_package_merged.reduced_hab_can = reduced_hab_can
+        self.reduction_package_merged.reduced_lab_sample = reduced_lab_sample
+        self.reduction_package_merged.reduced_hab_sample = reduced_hab_sample
 
-        names_to_save = get_all_names_to_save(reduction_packages)
+        self.reduction_package.reduced_lab = lab_workspace
+        self.reduction_package.reduced_hab = hab_workspace
+        self.reduction_package.reduced_lab_can = reduced_lab_can
+        self.reduction_package.reduced_hab_can = reduced_hab_can
+        self.reduction_package.reduced_lab_sample = reduced_lab_sample
+        self.reduction_package.reduced_hab_sample = reduced_hab_sample
 
-        self.assertEqual(names_to_save, set(['merged_workspace']))
+
+    def test_returns_merged_name_if_present(self):
+        reduction_packages = [self.reduction_package_merged]
+        names_to_save = get_all_names_to_save(reduction_packages, save_can=False)
+
+        self.assertEqual(names_to_save, {'merged_workspace'})
 
     def test_hab_and_lab_workspaces_returned_if_merged_workspace_not_present(self):
-        state = mock.MagicMock()
-        workspaces = ['Sample', 'Transmission', 'Direct']
-        monitors = ['monitor1']
-        reduction_package = ReductionPackage(state, workspaces, monitors)
-        lab_workspace = CreateSampleWorkspace(Function='Flat background', NumBanks=1, BankPixelWidth=1, NumEvents=1,
-                                              XMin=1, XMax=14, BinWidth=2)
-        hab_workspace = CreateSampleWorkspace(Function='Flat background', NumBanks=1, BankPixelWidth=1, NumEvents=1,
-                                              XMin=1, XMax=14, BinWidth=2)
-        reduction_package.reduced_lab = lab_workspace
-        reduction_package.reduced_hab = hab_workspace
-        reduction_packages = [reduction_package]
+        reduction_packages = [self.reduction_package]
+        names_to_save = get_all_names_to_save(reduction_packages, save_can=False)
+
+        self.assertEqual(names_to_save, {'lab_workspace', 'hab_workspace'})
+
+    def test_can_workspaces_returned_if_save_can_selected(self):
+        reduction_packages = [self.reduction_package_merged]
+        names_to_save = get_all_names_to_save(reduction_packages, True)
+
+        self.assertEqual(names_to_save, {'merged_workspace', 'lab_workspace', 'hab_workspace',
+                                         'reduced_lab_can', 'reduced_hab_can',
+                                         'reduced_lab_sample', 'reduced_hab_sample'})
+
+    def test_can_workspaces_returned_if_save_can_selected_no_merged(self):
+        reduction_packages = [self.reduction_package]
+        names_to_save = get_all_names_to_save(reduction_packages, True)
 
-        names_to_save = get_all_names_to_save(reduction_packages)
+        self.assertEqual(names_to_save, {'lab_workspace', 'hab_workspace',
+                                         'reduced_lab_can', 'reduced_hab_can',
+                                         'reduced_lab_sample', 'reduced_hab_sample'})
 
-        self.assertEqual(names_to_save, set(['lab_workspace', 'hab_workspace']))
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/scripts/test/SANS/algorithm_detail/scale_helper_test.py b/scripts/test/SANS/algorithm_detail/scale_helper_test.py
index 530504cac91cfa82d3c5118f708fd5ba039706fc..dfc6c868c8132e89f94e6edd2ca76a77b697ecc3 100644
--- a/scripts/test/SANS/algorithm_detail/scale_helper_test.py
+++ b/scripts/test/SANS/algorithm_detail/scale_helper_test.py
@@ -6,8 +6,10 @@
 # SPDX - License - Identifier: GPL - 3.0 +
 from __future__ import (absolute_import, division, print_function)
 import unittest
-import mantid
 import math
+
+from mantid.api import FrameworkManager
+
 from sans.test_helper.test_director import TestDirector
 from sans.algorithm_detail.scale_helpers import (DivideByVolumeFactory, DivideByVolumeISIS, NullDivideByVolume,
                                                  MultiplyByAbsoluteScaleFactory, MultiplyByAbsoluteScaleLOQ,
@@ -20,6 +22,11 @@ from sans.test_helper.file_information_mock import SANSFileInformationMock
 
 
 class ScaleHelperTest(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        FrameworkManager.Instance()
+
     @staticmethod
     def _get_workspace(width=1.0, height=1.0, thickness=1.0, shape=1):
         sample_name = "CreateSampleWorkspace"
diff --git a/scripts/test/SANS/algorithm_detail/strip_end_nans_test.py b/scripts/test/SANS/algorithm_detail/strip_end_nans_test.py
index 9cff3b537e54eed0c5000a8e9c2cffde6d07c413..0b6ae862834a8dda8402e6dc9fa56f89fad1bbb5 100644
--- a/scripts/test/SANS/algorithm_detail/strip_end_nans_test.py
+++ b/scripts/test/SANS/algorithm_detail/strip_end_nans_test.py
@@ -6,12 +6,17 @@
 # SPDX - License - Identifier: GPL - 3.0 +
 from __future__ import (absolute_import, division, print_function)
 import unittest
-import mantid
-from mantid.api import AlgorithmManager
+
+from mantid.api import AlgorithmManager, FrameworkManager
 from sans.algorithm_detail.strip_end_nans_and_infs import strip_end_nans
 
 
 class StripEndNansTest(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        FrameworkManager.Instance()
+
     def _do_test(self, data_x, data_y):
         # Arrange
         alg_ws = AlgorithmManager.createUnmanaged("CreateWorkspace")
diff --git a/scripts/test/SANS/common/general_functions_test.py b/scripts/test/SANS/common/general_functions_test.py
index 434f9242e26295adadd79184f00c2796c1e0e1cd..a50282648ca696f4a607e704c6ac0b0defd47af6 100644
--- a/scripts/test/SANS/common/general_functions_test.py
+++ b/scripts/test/SANS/common/general_functions_test.py
@@ -7,7 +7,7 @@
 from __future__ import (absolute_import, division, print_function)
 import unittest
 from mantid.kernel import (V3D, Quat)
-from mantid.api import AnalysisDataService
+from mantid.api import AnalysisDataService, FrameworkManager
 from sans.common.general_functions import (quaternion_to_angle_and_axis, create_unmanaged_algorithm, add_to_sample_log,
                                            get_standard_output_workspace_name, sanitise_instrument_name,
                                            get_reduced_can_workspace_from_ads, write_hash_into_reduced_can_workspace,
@@ -22,6 +22,11 @@ from sans.state.data import StateData
 
 
 class SANSFunctionsTest(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        FrameworkManager.Instance()
+
     @staticmethod
     def _prepare_workspaces(number_of_workspaces, tagged_workspace_names=None, state=None, reduction_mode=None):
         create_name = "CreateSampleWorkspace"
diff --git a/scripts/test/SANS/common/log_tagger_test.py b/scripts/test/SANS/common/log_tagger_test.py
index 01b123df2b77f047e2cfbce41b8ab4e10b7c2934..6e1e6b0ec3e5a0032e539c0a4fdf643f4b5aab5c 100644
--- a/scripts/test/SANS/common/log_tagger_test.py
+++ b/scripts/test/SANS/common/log_tagger_test.py
@@ -6,12 +6,17 @@
 # SPDX - License - Identifier: GPL - 3.0 +
 from __future__ import (absolute_import, division, print_function)
 import unittest
-import mantid
-from mantid.api import AlgorithmManager
+
+from mantid.api import AlgorithmManager, FrameworkManager
 from sans.common.log_tagger import (has_tag, set_tag, get_tag, has_hash, set_hash, get_hash_value)
 
 
 class SANSLogTaggerTest(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        FrameworkManager.Instance()
+
     @staticmethod
     def _provide_sample_workspace():
         alg = AlgorithmManager.createUnmanaged("CreateSampleWorkspace")
diff --git a/scripts/test/SANS/gui_logic/batch_process_runner_test.py b/scripts/test/SANS/gui_logic/batch_process_runner_test.py
index 560eba21f216263b899228bb77de86d590bf77f9..87b2d9c11efbe062b43429e27bac9397879711e5 100644
--- a/scripts/test/SANS/gui_logic/batch_process_runner_test.py
+++ b/scripts/test/SANS/gui_logic/batch_process_runner_test.py
@@ -30,6 +30,10 @@ class BatchProcessRunnerTest(unittest.TestCase):
         self.batch_mock = batch_patcher.start()
         self.batch_mock.return_value = self.sans_batch_instance
 
+        load_patcher = mock.patch('sans.gui_logic.models.batch_process_runner.load_workspaces_from_states')
+        self.addCleanup(load_patcher.stop)
+        self.load_mock = load_patcher.start()
+
         self.batch_process_runner = BatchProcessRunner(self.notify_progress, self.notify_done, self.notify_error)
         self.states = {0: mock.MagicMock(), 1: mock.MagicMock(), 2: mock.MagicMock()}
 
@@ -70,6 +74,33 @@ class BatchProcessRunnerTest(unittest.TestCase):
         self.batch_process_runner.row_failed_signal.emit.assert_any_call(2, 'failure')
         self.assertEqual(self.batch_process_runner.row_processed_signal.emit.call_count, 0)
 
+    def test_that_load_workspaces_emits_row_processed_signal_after_each_row(self):
+        self.batch_process_runner.row_processed_signal = mock.MagicMock()
+        self.batch_process_runner.row_failed_signal = mock.MagicMock()
+
+        self.batch_process_runner.load_workspaces(self.states)
+        QThreadPool.globalInstance().waitForDone()
+
+        self.assertEqual(self.batch_process_runner.row_processed_signal.emit.call_count, 3)
+        self.batch_process_runner.row_processed_signal.emit.assert_any_call(0, [], [])
+        self.batch_process_runner.row_processed_signal.emit.assert_any_call(1, [], [])
+        self.batch_process_runner.row_processed_signal.emit.assert_any_call(2, [], [])
+        self.assertEqual(self.batch_process_runner.row_failed_signal.emit.call_count, 0)
+
+    def test_that_load_workspaces_emits_row_failed_signal_after_each_failed_row(self):
+        self.batch_process_runner.row_processed_signal = mock.MagicMock()
+        self.batch_process_runner.row_failed_signal = mock.MagicMock()
+        self.load_mock.side_effect = Exception('failure')
+
+        self.batch_process_runner.load_workspaces(self.states)
+        QThreadPool.globalInstance().waitForDone()
+
+        self.assertEqual(self.batch_process_runner.row_failed_signal.emit.call_count, 3)
+        self.batch_process_runner.row_failed_signal.emit.assert_any_call(0, 'failure')
+        self.batch_process_runner.row_failed_signal.emit.assert_any_call(1, 'failure')
+        self.batch_process_runner.row_failed_signal.emit.assert_any_call(2, 'failure')
+        self.assertEqual(self.batch_process_runner.row_processed_signal.emit.call_count, 0)
+
 
 
 if __name__ == '__main__':
diff --git a/scripts/test/SANS/gui_logic/run_tab_presenter_test.py b/scripts/test/SANS/gui_logic/run_tab_presenter_test.py
index c0ca2533823eea780018be17c8c68cc6064a8546..d2cff426e99e24a4153cfc9e8000a65497998063 100644
--- a/scripts/test/SANS/gui_logic/run_tab_presenter_test.py
+++ b/scripts/test/SANS/gui_logic/run_tab_presenter_test.py
@@ -16,10 +16,11 @@ from mantid.kernel import PropertyManagerDataService
 from sans.gui_logic.presenter.run_tab_presenter import RunTabPresenter
 from sans.common.enums import (SANSFacility, ReductionDimensionality, SaveType, ISISReductionMode,
                                RangeStepType, FitType, SANSInstrument, RowState)
-from sans.test_helper.user_file_test_helper import (create_user_file, sample_user_file, sample_user_file_gravity_OFF)
+from sans.test_helper.user_file_test_helper import (create_user_file, sample_user_file, sample_user_file_gravity_OFF,
+                                                    sample_user_file_with_instrument)
 from sans.test_helper.mock_objects import (create_mock_view)
 from sans.test_helper.common import (remove_file)
-from sans.common.enums import BatchReductionEntry
+from sans.common.enums import BatchReductionEntry, SANSInstrument
 from sans.gui_logic.models.table_model import TableModel, TableIndexModel
 from sans.test_helper.file_information_mock import SANSFileInformationMock
 
@@ -67,7 +68,6 @@ class MultiPeriodMock(object):
 class RunTabPresenterTest(unittest.TestCase):
     def setUp(self):
         config.setFacility("ISIS")
-        config.setString("default.instrument", "SANS2D")
 
         patcher = mock.patch('sans.gui_logic.presenter.run_tab_presenter.BatchCsvParser')
         self.addCleanup(patcher.stop)
@@ -89,7 +89,11 @@ class RunTabPresenterTest(unittest.TestCase):
         presenter.set_view(view)
 
         # Act
-        presenter.on_user_file_load()
+        try:
+            presenter.on_user_file_load()
+        except RuntimeError:
+            # Assert that RuntimeError from no instrument is caught
+            self.fail("on_user_file_load raises a RuntimeError which should be caught")
 
         # Assert
         # Note that the event slices are not set in the user file
@@ -144,7 +148,7 @@ class RunTabPresenterTest(unittest.TestCase):
         self.assertTrue(view.radius_limit_min == 12.)
         self.assertTrue(view.radius_limit_min == 12.)
         self.assertTrue(view.radius_limit_max == 15.)
-        self.assertFalse(view.compatibility_mode)
+        self.assertTrue(view.compatibility_mode)
         self.assertTrue(view.show_transmission)
 
         # Assert that Beam Centre View is updated correctly
diff --git a/scripts/test/SANS/gui_logic/state_gui_model_test.py b/scripts/test/SANS/gui_logic/state_gui_model_test.py
index f827f3de4eccf8d2e15b4cb5a40838ae9c0dfb08..58fd5e80b4b0cfe4461aa2d9fdf8669c092480d8 100644
--- a/scripts/test/SANS/gui_logic/state_gui_model_test.py
+++ b/scripts/test/SANS/gui_logic/state_gui_model_test.py
@@ -9,7 +9,7 @@ import unittest
 from sans.gui_logic.models.state_gui_model import StateGuiModel
 from sans.user_file.settings_tags import (OtherId, event_binning_string_values, DetectorId, det_fit_range)
 from sans.common.enums import (ReductionDimensionality, ISISReductionMode, RangeStepType, SampleShape, SaveType,
-                               FitType)
+                               FitType, SANSInstrument)
 from sans.user_file.settings_tags import (det_fit_range)
 
 
@@ -19,13 +19,16 @@ class StateGuiModelTest(unittest.TestCase):
     # FRONT TAB
     # ==================================================================================================================
     # ==================================================================================================================
+    def test_that_default_instrument_is_NoInstrument(self):
+        state_gui_model = StateGuiModel({"test": [1]})
+        self.assertEqual(state_gui_model.instrument, SANSInstrument.NoInstrument)
 
     # ------------------------------------------------------------------------------------------------------------------
     # Compatibility Mode
     # ------------------------------------------------------------------------------------------------------------------
-    def test_that_default_compatibility_mode_is_false(self):
+    def test_that_default_compatibility_mode_is_true(self):
         state_gui_model = StateGuiModel({"test": [1]})
-        self.assertFalse(state_gui_model.compatibility_mode)
+        self.assertTrue(state_gui_model.compatibility_mode)
 
     def test_that_can_set_compatibility_mode(self):
         state_gui_model = StateGuiModel({"test": [1]})
diff --git a/scripts/test/SANS/user_file/user_file_parser_test.py b/scripts/test/SANS/user_file/user_file_parser_test.py
index 3f0ac436b2248757574eaf0052601e70e1f16df9..643b6eac76f8e1279b4bfbe8dfec3112b3a732a5 100644
--- a/scripts/test/SANS/user_file/user_file_parser_test.py
+++ b/scripts/test/SANS/user_file/user_file_parser_test.py
@@ -8,8 +8,8 @@ from __future__ import (absolute_import, division, print_function)
 import unittest
 import mantid
 
-from sans.common.enums import (ISISReductionMode, DetectorType, RangeStepType, FitType, DataType)
-from sans.user_file.user_file_parser import (DetParser, LimitParser, MaskParser, SampleParser, SetParser, TransParser,
+from sans.common.enums import (ISISReductionMode, DetectorType, RangeStepType, FitType, DataType, SANSInstrument)
+from sans.user_file.user_file_parser import (InstrParser, DetParser, LimitParser, MaskParser, SampleParser, SetParser, TransParser,
                                              TubeCalibFileParser, QResolutionParser, FitParser, GravityParser,
                                              MaskFileParser, MonParser, PrintParser, BackParser, SANS2DParser, LOQParser,
                                              UserFileParser, LARMORParser, CompatibilityParser)
@@ -56,6 +56,32 @@ def do_test(parser, valid_settings, invalid_settings, assert_true, assert_raises
 # # -----------------------------------------------------------------
 # # --- Tests -------------------------------------------------------
 # # -----------------------------------------------------------------
+class InstrParserTest(unittest.TestCase):
+    def test_that_gets_type(self):
+        self.assertTrue(InstrParser.get_type(), "INSTR")
+
+    def test_that_instruments_are_recognised(self):
+        self.assertTrue(InstrParser.get_type_pattern("LOQ"))
+        self.assertTrue(InstrParser.get_type_pattern("SANS2D"))
+        self.assertTrue(InstrParser.get_type_pattern("ZOOM"))
+        self.assertTrue(InstrParser.get_type_pattern("LARMOR"))
+        self.assertFalse(InstrParser.get_type_pattern("Not an instrument"))
+        self.assertFalse(InstrParser.get_type_pattern("SANS2D/something else"))
+
+    def test_that_instruments_are_parsed_correctly(self):
+        valid_settings = {"SANS2D": {DetectorId.instrument: SANSInstrument.SANS2D},
+                          "LOQ": {DetectorId.instrument: SANSInstrument.LOQ},
+                          "ZOOM": {DetectorId.instrument: SANSInstrument.ZOOM},
+                          "LARMOR": {DetectorId.instrument: SANSInstrument.LARMOR}}
+
+        invalid_settings = {"NOINSTRUMENT": RuntimeError,
+                            "SANS2D/HAB": RuntimeError,
+                            "!LOQ": RuntimeError}
+
+        instr_parser = InstrParser()
+        do_test(instr_parser, valid_settings, invalid_settings, self.assertTrue, self.assertRaises)
+
+
 class DetParserTest(unittest.TestCase):
     def test_that_gets_type(self):
         self.assertTrue(DetParser.get_type(), "DET")
@@ -1064,13 +1090,13 @@ class UserFileParserTest(unittest.TestCase):
         result = user_file_parser.parse_line("BACK / M3 /OFF")
         assert_valid_result(result, {BackId.monitor_off: 3}, self.assertTrue)
 
-        # SANS2DParser
+        # Instrument parser
         result = user_file_parser.parse_line("SANS2D")
-        self.assertTrue(not result)
+        assert_valid_result(result, {DetectorId.instrument: SANSInstrument.SANS2D}, self.assertTrue)
 
-        # LOQParser
-        result = user_file_parser.parse_line("LOQ")
-        self.assertTrue(not result)
+        # Instrument parser - whitespace
+        result = user_file_parser.parse_line("     ZOOM      ")
+        assert_valid_result(result, {DetectorId.instrument: SANSInstrument.ZOOM}, self.assertTrue)
 
     def test_that_non_existent_parser_throws(self):
         # Arrange
diff --git a/scripts/test/pythonTSVTest.py b/scripts/test/pythonTSVTest.py
new file mode 100644
index 0000000000000000000000000000000000000000..53a5666c1a6759205e04887f57eb6d70d40d40fd
--- /dev/null
+++ b/scripts/test/pythonTSVTest.py
@@ -0,0 +1,148 @@
+# Mantid Repository : https://github.com/mantidproject/mantid
+#
+# Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
+#     NScD Oak Ridge National Laboratory, European Spallation Source
+#     & Institut Laue - Langevin
+# SPDX - License - Identifier: GPL - 3.0 +
+import unittest
+
+from mantidqtpython import MantidQt
+import pythonTSV
+
+Name = "test"
+
+"""
+Returns a TSV
+to read from the input TSV
+"""
+def load_TSV(TSV):
+    string = TSV.outputLines()
+    return MantidQt.API.TSVSerialiser(string)
+
+"""
+Returns a TSV on the line Name
+"""
+def get_loaded_data(TSV,value):
+    load = load_TSV(TSV)
+    load.selectLine(Name)
+    return load
+
+# dummy class
+class Class(object):
+    def __init__(self):
+        self.a = "a"
+
+class PythonTSVTest(unittest.TestCase):
+
+    def test_saveDouble(self):
+        value = 1.1
+        TSV = MantidQt.API.TSVSerialiser()
+        TSV.writeLine(Name)
+        pythonTSV.saveToTSV(TSV,value)
+        load =get_loaded_data(TSV,value)
+        self.assertEquals(value, load.readDouble())
+
+    def test_saveInt(self):
+        value = 1
+        TSV = MantidQt.API.TSVSerialiser()
+        TSV.writeLine(Name)
+        pythonTSV.saveToTSV(TSV,value)
+        load =get_loaded_data(TSV,value)
+        self.assertEquals(value, load.readInt())
+
+    def test_saveString(self):
+        value = "string"
+        TSV = MantidQt.API.TSVSerialiser()
+        TSV.writeLine(Name)
+        pythonTSV.saveToTSV(TSV,value)
+        load =get_loaded_data(TSV,value)
+        self.assertEquals(value, load.readString())
+ 
+    def test_saveBool(self):
+        value = False
+        TSV = MantidQt.API.TSVSerialiser()
+        TSV.writeLine(Name)
+        pythonTSV.saveToTSV(TSV,value)
+        load =get_loaded_data(TSV,value)
+        self.assertEquals(value, load.readBool())
+
+    def test_saveClassFails(self):
+        value = Class()
+        TSV = MantidQt.API.TSVSerialiser()
+        TSV.writeLine(Name)
+        with self.assertRaises(TypeError):
+             pythonTSV.saveToTSV(TSV,value)
+
+    def test_readDouble(self):
+        value = 1.1
+        TSV = MantidQt.API.TSVSerialiser()
+        TSV.writeLine(Name)
+        TSV.storeDouble(value)
+        load =load_TSV(TSV)
+        self.assertEquals(value, pythonTSV.loadFromTSV(load,Name,3.3))
+
+
+    def test_readInt(self):
+        value = 1
+        TSV = MantidQt.API.TSVSerialiser()
+        TSV.writeLine(Name)
+        TSV.storeInt(value)
+        load =load_TSV(TSV)
+        self.assertEquals(value, pythonTSV.loadFromTSV(load,Name,3))
+
+    def test_saveString(self):
+        value = "string"
+        TSV = MantidQt.API.TSVSerialiser()
+        TSV.writeLine(Name)
+        TSV.storeString(value)
+        load =load_TSV(TSV)
+        self.assertEquals(value, pythonTSV.loadFromTSV(load,Name,"test"))
+
+    def test_saveBool(self):
+        value = False
+        TSV = MantidQt.API.TSVSerialiser()
+        TSV.writeLine(Name)
+        TSV.storeBool(value)
+        load =load_TSV(TSV)
+        self.assertEquals(value, pythonTSV.loadFromTSV(load,Name,True))
+
+ 
+    def test_saveClassFails(self):
+        value = Class()
+        TSV = MantidQt.API.TSVSerialiser()
+        TSV.writeLine(Name)
+        TSV.storeString("doesn't matter what is stored")
+        load =load_TSV(TSV)
+        with self.assertRaises(TypeError):
+            pythonTSV.loadFromTSV(load,Name,value)
+
+    def test_makeLineNameSafeSpaces(self):
+        badName = "spaces fail"
+        newName = pythonTSV.makeLineNameSafe(badName)
+        self.assertEquals(newName,"SpacesFail")
+
+    def test_makeLineNameSafeUnderscores(self):
+        badName = "underscores_fail"
+        newName = pythonTSV.makeLineNameSafe(badName)
+        self.assertEquals(newName,"UnderscoresFail")
+
+    def test_makeLineNameSafeDashes(self):
+        badName = "dashes-fail"
+        newName = pythonTSV.makeLineNameSafe(badName)
+        self.assertEquals(newName,"DashesFail")
+
+    def test_writeLine(self):
+        badName = "bad name_example-here"
+        TSV = MantidQt.API.TSVSerialiser()
+        pythonTSV.writeLine(TSV, badName)
+        pi = 3.14159
+        TSV.storeDouble(pi)
+        load = load_TSV(TSV)
+        safeName = pythonTSV.makeLineNameSafe(badName)
+        load.selectLine(safeName)
+        self.assertEquals(pi, load.readDouble())
+        
+
+if __name__ == '__main__':
+    unittest.main()
+
diff --git a/tools/Pylint/run_pylint.py b/tools/Pylint/run_pylint.py
index 8b2bf451b7577eae9c9ba91dd238e55bb4e07241..3e41a68b2110dcc837c5ad2785f18d035e452c26 100644
--- a/tools/Pylint/run_pylint.py
+++ b/tools/Pylint/run_pylint.py
@@ -153,7 +153,7 @@ def parse_arguments(argv):
                       help="If provided, use the given format type "
                            "[default=%s]." % DEFAULT_PYLINT_FORMAT)
     parser.add_option("-m", "--mantidpath", dest="mantidpath", metavar="MANTIDPATH",
-                      help="If provided, use this as the MANTIDPATH, overriding"
+                      help="If provided, add this to the PYTHONPATH, overriding"
                            "anything that is currently set.")
     parser.add_option("-n", "--nofail", action="store_true",dest="nofail",
                       help="If specified, then script will always return an exit status of 0.")
@@ -209,8 +209,7 @@ def setup_environment(mantidpath):
 def setup_mantidpath(mantidpath):
     """
     Setup the environment ready for the subprocess call.
-    Inserts the given path at the front of the PYTHONPATH and
-    sets the MANTIDPATH variable
+    Inserts the given path at the front of the PYTHONPATH
 
     Args:
       mantidpath (str): A string that points to a directory containing
@@ -221,7 +220,6 @@ def setup_mantidpath(mantidpath):
         raise ValueError("Unable to find mantid python module in '%s'"
                          % mantidpath)
 
-    os.environ["MANTIDPATH"] = mantidpath
     cur_pypath = os.environ.get("PYTHONPATH", "")
     # for subprocesses
     os.environ["PYTHONPATH"] = mantidpath + os.pathsep + cur_pypath