diff --git a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/IqtFitSequential.py b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/IqtFitSequential.py
deleted file mode 100644
index 4429717507c7fa10a13723c5a4e4f7c2e7cee05e..0000000000000000000000000000000000000000
--- a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/IqtFitSequential.py
+++ /dev/null
@@ -1,247 +0,0 @@
-from __future__ import (absolute_import, division, print_function)
-
-from mantid import logger, AlgorithmFactory
-from mantid.api import *
-from mantid.kernel import *
-import mantid.simpleapi as ms
-
-
-class IqtFitSequential(PythonAlgorithm):
-    _input_ws = None
-    _function = None
-    _fit_type = None
-    _start_x = None
-    _end_x = None
-    _spec_min = None
-    _spec_max = None
-    _intensities_constrained = None
-    _minimizer = None
-    _max_iterations = None
-    _result_name = None
-    _result_ws = None
-    _parameter_name = None
-    _fit_group_name = None
-
-    def category(self):
-        return "Workflow\\MIDAS"
-
-    def summary(self):
-        return "Fits an \*\_iqt file generated by I(Q,t) sequentially."
-
-    def PyInit(self):
-        self.declareProperty(MatrixWorkspaceProperty('InputWorkspace', '', direction=Direction.Input),
-                             doc='The _iqt.nxs InputWorkspace used by the algorithm')
-
-        self.declareProperty(name='Function', defaultValue='',
-                             doc='The function to use in fitting')
-
-        self.declareProperty(name='FitType', defaultValue='',
-                             doc='The type of fit being carried out')
-
-        self.declareProperty(name='StartX', defaultValue=0.0,
-                             validator=FloatBoundedValidator(0.0),
-                             doc="The first value for X")
-
-        self.declareProperty(name='EndX', defaultValue=0.2,
-                             validator=FloatBoundedValidator(0.0),
-                             doc="The last value for X")
-
-        self.declareProperty(name='SpecMin', defaultValue=0,
-                             validator=IntBoundedValidator(0),
-                             doc='Minimum spectra in the workspace to fit')
-
-        self.declareProperty(name='SpecMax', defaultValue=1,
-                             validator=IntBoundedValidator(0),
-                             doc='Maximum spectra in the workspace to fit')
-
-        self.declareProperty(name='Minimizer', defaultValue='Levenberg-Marquardt',
-                             doc='The minimizer to use in fitting')
-
-        self.declareProperty(name="MaxIterations", defaultValue=500,
-                             validator=IntBoundedValidator(0),
-                             doc="The Maximum number of iterations for the fit")
-
-        self.declareProperty(name='ConstrainIntensities', defaultValue=False,
-                             doc="If the Intensities should be constrained during the fit")
-
-        self.declareProperty(name='ExtractMembers', defaultValue=False,
-                             doc="If true, then each member of the fit will be extracted, into their "
-                                 "own workspace")
-
-        self.declareProperty(MatrixWorkspaceProperty('OutputResultWorkspace', '', direction=Direction.Output),
-                             doc='The output workspace containing the results of the fit data')
-
-        self.declareProperty(ITableWorkspaceProperty('OutputParameterWorkspace', '', direction=Direction.Output),
-                             doc='The output workspace containing the parameters for each fit')
-
-        self.declareProperty(WorkspaceGroupProperty('OutputWorkspaceGroup', '', direction=Direction.Output),
-                             doc='The OutputWorkspace group Data, Calc and Diff, values for the fit of each spectra')
-
-    def validateInputs(self):
-        self._get_properties()
-        issues = dict()
-        if self._start_x >= self._end_x:
-            issues['StartX'] = 'StartX must be more than EndX'
-        return issues
-
-    def _get_properties(self):
-        self._input_ws = self.getProperty('InputWorkspace').value
-        self._function = self.getProperty('Function').value
-        self._fit_type = self.getProperty('FitType').value
-        self._start_x = self.getProperty('StartX').value
-        self._end_x = self.getProperty('EndX').value
-        self._spec_min = self.getProperty('SpecMin').value
-        self._spec_max = self.getProperty('SpecMax').value
-        self._intensities_constrained = self.getProperty('ConstrainIntensities').value
-        self._do_extract_members = self.getProperty('ExtractMembers').value
-        self._minimizer = self.getProperty('Minimizer').value
-        self._max_iterations = self.getProperty('MaxIterations').value
-        self._result_name = self.getPropertyValue('OutputResultWorkspace')
-        self._parameter_name = self.getPropertyValue('OutputParameterWorkspace')
-        self._fit_group_name = self.getPropertyValue('OutputWorkspaceGroup')
-
-    def PyExec(self):
-        from IndirectCommon import (getWSprefix, convertToElasticQ)
-
-        setup_prog = Progress(self, start=0.0, end=0.1, nreports=4)
-        self._fit_type = self._fit_type[:-2]
-        logger.information('Option: ' + self._fit_type)
-        logger.information(self._function)
-
-        setup_prog.report('Cropping workspace')
-        tmp_fit_name = "__IqtFit_ws"
-        crop_alg = self.createChildAlgorithm("CropWorkspace", enableLogging=False)
-        crop_alg.setProperty("InputWorkspace", self._input_ws)
-        crop_alg.setProperty("OutputWorkspace", tmp_fit_name)
-        crop_alg.setProperty("XMin", self._start_x)
-        crop_alg.setProperty("XMax", self._end_x)
-        crop_alg.execute()
-
-        num_hist = self._input_ws.getNumberHistograms()
-        if self._spec_max is None:
-            self._spec_max = num_hist - 1
-
-        # Name stem for generated workspace
-        output_workspace = '%sIqtFit_%s_s%d_to_%d' % (getWSprefix(self._input_ws.name()),
-                                                      self._fit_type, self._spec_min,
-                                                      self._spec_max)
-
-        setup_prog.report('Converting to Histogram')
-        convert_to_hist_alg = self.createChildAlgorithm("ConvertToHistogram", enableLogging=False)
-        convert_to_hist_alg.setProperty("InputWorkspace", crop_alg.getProperty("OutputWorkspace").value)
-        convert_to_hist_alg.setProperty("OutputWorkspace", tmp_fit_name)
-        convert_to_hist_alg.execute()
-        mtd.addOrReplace(tmp_fit_name, convert_to_hist_alg.getProperty("OutputWorkspace").value)
-
-        setup_prog.report('Convert to Elastic Q')
-        convertToElasticQ(tmp_fit_name)
-
-        # Build input string for PlotPeakByLogValue
-        input_str = [tmp_fit_name + ',i%d' % i for i in range(self._spec_min, self._spec_max + 1)]
-        input_str = ';'.join(input_str)
-
-        fit_prog = Progress(self, start=0.1, end=0.8, nreports=2)
-        fit_prog.report('Fitting...')
-        ms.PlotPeakByLogValue(Input=input_str,
-                              OutputWorkspace=output_workspace,
-                              Function=self._function,
-                              Minimizer=self._minimizer,
-                              MaxIterations=self._max_iterations,
-                              StartX=self._start_x,
-                              EndX=self._end_x,
-                              FitType='Sequential',
-                              CreateOutput=True,
-                              OutputCompositeMembers=self._do_extract_members)
-        fit_prog.report('Fitting complete')
-
-        conclusion_prog = Progress(self, start=0.8, end=1.0, nreports=5)
-        # Remove unused workspaces
-        delete_alg = self.createChildAlgorithm("DeleteWorkspace", enableLogging=False)
-        delete_alg.setProperty("Workspace", output_workspace + '_NormalisedCovarianceMatrices')
-        delete_alg.execute()
-        delete_alg.setProperty("Workspace", output_workspace + '_Parameters')
-        delete_alg.execute()
-        delete_alg.setProperty("Workspace", tmp_fit_name)
-        delete_alg.execute()
-
-        conclusion_prog.report('Renaming workspaces')
-        # rename workspaces to match user input
-        rename_alg = self.createChildAlgorithm("RenameWorkspace", enableLogging=False)
-        if output_workspace + "_Workspaces" != self._fit_group_name:
-            rename_alg.setProperty("InputWorkspace", output_workspace + "_Workspaces")
-            rename_alg.setProperty("OutputWorkspace", self._fit_group_name)
-            rename_alg.execute()
-        if output_workspace != self._parameter_name:
-            rename_alg.setProperty("InputWorkspace", output_workspace)
-            rename_alg.setProperty("OutputWorkspace", self._parameter_name)
-            rename_alg.execute()
-
-        # Create *_Result workspace
-        parameter_names = 'A0,Height,Lifetime,Stretching'
-        conclusion_prog.report('Processing indirect fit parameters')
-        pifp_alg = self.createChildAlgorithm("ProcessIndirectFitParameters")
-        pifp_alg.setProperty("InputWorkspace", self._parameter_name)
-        pifp_alg.setProperty("ColumnX", "axis-1")
-        pifp_alg.setProperty("XAxisUnit", "MomentumTransfer")
-        pifp_alg.setProperty("ParameterNames", parameter_names)
-        pifp_alg.setProperty("OutputWorkspace", self._result_name)
-        pifp_alg.execute()
-        self._result_ws = pifp_alg.getProperty("OutputWorkspace").value
-
-        mtd.addOrReplace(self._result_name, self._result_ws)
-
-        # Process generated workspaces
-        wsnames = mtd[self._fit_group_name].getNames()
-        for i, workspace in enumerate(wsnames):
-            output_ws = output_workspace + '_Workspace_%d' % i
-            rename_alg.setProperty("InputWorkspace", workspace)
-            rename_alg.setProperty("OutputWorkspace", output_ws)
-            rename_alg.execute()
-
-        conclusion_prog.report('Copying and transferring sample logs')
-        self._transfer_sample_logs()
-
-        if self._do_extract_members:
-            ms.ExtractQENSMembers(InputWorkspace=self._input_ws,
-                                  ResultWorkspace=self._fit_group_name,
-                                  OutputWorkspace=self._fit_group_name.rsplit('_', 1)[0] + "_Members")
-
-        self.setProperty('OutputParameterWorkspace', self._parameter_name)
-        self.setProperty('OutputWorkspaceGroup', self._fit_group_name)
-        self.setProperty('OutputResultWorkspace', self._result_ws)
-        conclusion_prog.report('Algorithm complete')
-
-    def _transfer_sample_logs(self):
-        """
-        Copy the sample logs from the input workspace and add them to the output workspaces
-        """
-
-        sample_logs = {'start_x': self._start_x, 'end_x': self._end_x, 'fit_type': self._fit_type,
-                       'intensities_constrained': self._intensities_constrained, 'beta_constrained': False}
-
-        copy_log_alg = self.createChildAlgorithm("CopyLogs", enableLogging=False)
-        copy_log_alg.setProperty("InputWorkspace", self._input_ws)
-        copy_log_alg.setProperty("OutputWorkspace", self._fit_group_name)
-        copy_log_alg.execute()
-        copy_log_alg.setProperty("InputWorkspace", self._input_ws)
-        copy_log_alg.setProperty("OutputWorkspace", self._result_ws.name())
-        copy_log_alg.execute()
-
-        log_names = [item for item in sample_logs]
-        log_values = [sample_logs[item] for item in sample_logs]
-
-        add_sample_log_multi = self.createChildAlgorithm("AddSampleLogMultiple", enableLogging=False)
-        add_sample_log_multi.setProperty("Workspace", self._result_ws.name())
-        add_sample_log_multi.setProperty("LogNames", log_names)
-        add_sample_log_multi.setProperty("LogValues", log_values)
-        add_sample_log_multi.execute()
-        add_sample_log_multi.setProperty("Workspace", self._fit_group_name)
-        add_sample_log_multi.setProperty("LogNames", log_names)
-        add_sample_log_multi.setProperty("LogValues", log_values)
-        add_sample_log_multi.execute()
-
-    def _extract_members(self):
-        ms.ExtractMembers()
-
-
-AlgorithmFactory.subscribe(IqtFitSequential)
diff --git a/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/IqtFitSequentialTest.py b/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/IqtFitSequentialTest.py
index a3d94632af81281dae01dc938c0df09642b2570f..98a1a4a7bb538bca98b5ad46a5e1b47acbd1ce43 100644
--- a/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/IqtFitSequentialTest.py
+++ b/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/IqtFitSequentialTest.py
@@ -73,7 +73,7 @@ class IqtFitSequentialTest(unittest.TestCase):
         nbins = sub_ws.blocksize()
         nhists = sub_ws.getNumberHistograms()
         self.assertEquals(nbins, 58)
-        self.assertEquals(nhists, 3)
+        self.assertEquals(nhists, 5)
 
         # Check histogram names
         text_axis = sub_ws.getAxis(1)
@@ -100,7 +100,7 @@ class IqtFitSequentialTest(unittest.TestCase):
         self.assertEquals(round(row['f1.Lifetime'], 7), 0.0287491)
 
     def _validate_matrix_values(self, matrixWS):
-        # Check f0.A0
+        # Check f1.A0
         a0 = matrixWS.readY(0)
         self.assertEquals(round(a0[0], 7), 0.0336564)
         self.assertEquals(round(a0[-1],7), 0.0182411)
@@ -115,7 +115,6 @@ class IqtFitSequentialTest(unittest.TestCase):
         self.assertEquals(round(lifetime[0], 7), 0.0287491)
         self.assertEquals(round(lifetime[-1],7), 0.0034427)
 
-
     def _validate_group_values(self, groupWS):
         sub_ws = groupWS.getItem(0)
         # Check Data
@@ -134,9 +133,6 @@ class IqtFitSequentialTest(unittest.TestCase):
     def _validate_sample_log_values(self, matrixWS):
         run = matrixWS.getRun()
         # Check additionally added logs
-        self.assertEqual(run.getProperty('fit_type').value, '1E')
-        self.assertEqual(run.getProperty('intensities_constrained').value, 'True')
-        self.assertEqual(run.getProperty('beta_constrained').value, 'False')
         self.assertEqual(run.getProperty('end_x').value, 0.24)
         self.assertEqual(run.getProperty('start_x').value, 0.0)
 
@@ -155,12 +151,10 @@ class IqtFitSequentialTest(unittest.TestCase):
         """
         result, params, fit_group = IqtFitSequential(InputWorkspace=self._iqt_ws,
                                                      Function=self._function,
-                                                     FitType='1E_s',
                                                      StartX=0,
                                                      EndX=0.24,
                                                      SpecMin=0,
-                                                     SpecMax=16,
-                                                     ConstrainIntensities=True)
+                                                     SpecMax=16)
         self._validate_output(params, result, fit_group)
 
 #----------------------------------------Failure cases-------------------------------------
@@ -169,72 +163,54 @@ class IqtFitSequentialTest(unittest.TestCase):
         self.assertRaises(ValueError, IqtFitSequential,
                           InputWorkspace=self._iqt_ws,
                           Function=self._function,
-                          FitType='1S_s',
                           EndX=0.2,
                           SpecMin=-1,
                           SpecMax=16,
-                          OutputResultWorkspace='result',
+                          OutputWorkspace='result',
                           OutputParameterWorkspace='table',
                           OutputWorkspaceGroup='fit_group')
 
     def test_maximum_spectra_more_than_workspace_spectra(self):
         self.assertRaises(RuntimeError, IqtFitSequential, InputWorkspace=self._iqt_ws,
                           Function=self._function,
-                          FitType='1S_s',
                           EndX=0.2,
                           SpecMin=0,
                           SpecMax=20,
-                          OutputResultWorkspace='result',
+                          OutputWorkspace='result',
                           OutputParameterWorkspace='table',
                           OutputWorkspaceGroup='fit_group')
 
     def test_minimum_spectra_more_than_maximum_spectra(self):
         self.assertRaises(RuntimeError, IqtFitSequential, InputWorkspace=self._iqt_ws,
                           Function=self._function,
-                          FitType='1S_s',
                           EndX=0.2,
                           SpecMin=10,
                           SpecMax=5,
-                          OutputResultWorkspace='result',
+                          OutputWorkspace='result',
                           OutputParameterWorkspace='table',
                           OutputWorkspaceGroup='fit_group')
 
     def test_minimum_x_less_than_0(self):
-        self.assertRaises(ValueError, IqtFitSequential, InputWorkspace=self._iqt_ws,
+        self.assertRaises(RuntimeError, IqtFitSequential, InputWorkspace=self._iqt_ws,
                           Function=self._function,
-                          FitType='1S_s',
                           StartX=-0.2,
                           EndX=0.2,
                           SpecMin=0,
                           SpecMax=16,
-                          OutputResultWorkspace='result',
+                          OutputWorkspace='result',
                           OutputParameterWorkspace='table',
                           OutputWorkspaceGroup='fit_group')
 
     def test_maximum_x_more_than_workspace_max_x(self):
         self.assertRaises(RuntimeError, IqtFitSequential, InputWorkspace=self._iqt_ws,
                           Function=self._function,
-                          FitType='1S_s',
                           StartX=0,
                           EndX=0.4,
                           SpecMin=0,
                           SpecMax=16,
-                          OutputResultWorkspace='result',
-                          OutputParameterWorkspace='table',
-                          OutputWorkspaceGroup='fit_group')
-
-    def test_minimum_spectra_more_than_maximum_spectra(self):
-        self.assertRaises(RuntimeError, IqtFitSequential, InputWorkspace=self._iqt_ws,
-                          Function=self._function,
-                          FitType='1S_s',
-                          StartX=0.2,
-                          EndX=0.1,
-                          SpecMin=16,
-                          SpecMax=0,
-                          OutputResultWorkspace='result',
+                          OutputWorkspace='result',
                           OutputParameterWorkspace='table',
                           OutputWorkspaceGroup='fit_group')
 
-
 if __name__=="__main__":
     unittest.main()
diff --git a/Framework/WorkflowAlgorithms/CMakeLists.txt b/Framework/WorkflowAlgorithms/CMakeLists.txt
index adbf431c1653738462031a5978e752d5f99ddec3..8968361ce1cc72ad693ad371208a7ada53fbe02a 100644
--- a/Framework/WorkflowAlgorithms/CMakeLists.txt
+++ b/Framework/WorkflowAlgorithms/CMakeLists.txt
@@ -22,6 +22,7 @@ set ( SRC_FILES
 	src/HFIRLoad.cpp
 	src/HFIRSANSNormalise.cpp
 	src/IMuonAsymmetryCalculator.cpp
+	src/IqtFitSequential.cpp
 	src/LoadEventAndCompress.cpp
 	src/MuonGroupAsymmetryCalculator.cpp
 	src/MuonGroupCalculator.cpp
@@ -29,6 +30,7 @@ set ( SRC_FILES
 	src/MuonProcess.cpp
 	src/MuonPairAsymmetryCalculator.cpp
 	src/ProcessIndirectFitParameters.cpp
+	src/QENSFitSequential.cpp
 	src/RefRoi.cpp
 	src/SANSBeamFinder.cpp
 	src/SANSBeamFluxCorrection.cpp
@@ -68,6 +70,7 @@ set ( INC_FILES
 	inc/MantidWorkflowAlgorithms/HFIRLoad.h
 	inc/MantidWorkflowAlgorithms/HFIRSANSNormalise.h
 	inc/MantidWorkflowAlgorithms/IMuonAsymmetryCalculator.h
+	inc/MantidWorkflowAlgorithms/IqtFitSequential.h
 	inc/MantidWorkflowAlgorithms/LoadEventAndCompress.h
 	inc/MantidWorkflowAlgorithms/MuonGroupAsymmetryCalculator.h
 	inc/MantidWorkflowAlgorithms/MuonGroupCalculator.h
@@ -75,6 +78,7 @@ set ( INC_FILES
 	inc/MantidWorkflowAlgorithms/MuonProcess.h
 	inc/MantidWorkflowAlgorithms/MuonPairAsymmetryCalculator.h
 	inc/MantidWorkflowAlgorithms/ProcessIndirectFitParameters.h
+	inc/MantidWorkflowAlgorithms/QENSFitSequential.h
 	inc/MantidWorkflowAlgorithms/RefRoi.h
 	inc/MantidWorkflowAlgorithms/SANSBeamFinder.h
 	inc/MantidWorkflowAlgorithms/SANSBeamFluxCorrection.h
@@ -94,6 +98,7 @@ set ( TEST_FILES
 	LoadEventAndCompressTest.h
 	MuonProcessTest.h
 	ProcessIndirectFitParametersTest.h
+	QENSFitSequentialTest.h
 	SANSSolidAngleCorrectionTest.h
 	StepScanTest.h
 )
diff --git a/Framework/WorkflowAlgorithms/inc/MantidWorkflowAlgorithms/ConvolutionFitSequential.h b/Framework/WorkflowAlgorithms/inc/MantidWorkflowAlgorithms/ConvolutionFitSequential.h
index 83d8bcbe27ec2a2dc2a0015f05fa1a3814779a24..54341333a84d177f01986b4ad9ecb0a01779f80d 100644
--- a/Framework/WorkflowAlgorithms/inc/MantidWorkflowAlgorithms/ConvolutionFitSequential.h
+++ b/Framework/WorkflowAlgorithms/inc/MantidWorkflowAlgorithms/ConvolutionFitSequential.h
@@ -2,10 +2,8 @@
 #define MANTID_ALGORITHMS_CONVOLUTIONFITSEQUENTIAL_H_
 
 #include "MantidAPI/Column.h"
-#include "MantidAPI/DataProcessorAlgorithm.h"
-#include "MantidAPI/MatrixWorkspace.h"
-#include "MantidAPI/WorkspaceGroup.h"
 #include "MantidKernel/System.h"
+#include "MantidWorkflowAlgorithms/QENSFitSequential.h"
 
 namespace Mantid {
 namespace Algorithms {
@@ -34,31 +32,28 @@ namespace Algorithms {
   File change history is stored at: <https://github.com/mantidproject/mantid>
   Code Documentation is available at: <http://doxygen.mantidproject.org>
 */
-class DLLExport ConvolutionFitSequential : public API::DataProcessorAlgorithm {
+class DLLExport ConvolutionFitSequential : public QENSFitSequential {
 public:
   const std::string name() const override;
   int version() const override;
   const std::string category() const override;
   const std::string summary() const override;
+  const std::vector<std::string> seeAlso() const override;
+
+protected:
+  API::ITableWorkspace_sptr performFit(const std::string &input,
+                                       const std::string &output) override;
+  std::map<std::string, std::string> getAdditionalLogStrings() const override;
+  std::map<std::string, std::string> getAdditionalLogNumbers() const override;
 
 private:
-  void init() override;
-  void exec() override;
+  std::map<std::string, std::string> validateInputs() override;
 
-  bool checkForTwoLorentz(const std::string &);
-  std::vector<std::string> findValuesFromFunction(const std::string &);
-  std::vector<std::string> searchForFitParams(const std::string &,
-                                              const std::vector<std::string> &);
-  std::vector<double> squareVector(std::vector<double>);
-  std::vector<double> cloneVector(const std::vector<double> &);
-  void convertInputToElasticQ(API::MatrixWorkspace_sptr &, const std::string &);
+  bool throwIfElasticQConversionFails() const override;
+  bool isFitParameter(const std::string &name) const override;
   void calculateEISF(API::ITableWorkspace_sptr &);
-  std::string convertBackToShort(const std::string &);
-  std::string convertFuncToShort(const std::string &);
-  void extractMembers(Mantid::API::MatrixWorkspace_sptr inputWs,
-                      Mantid::API::WorkspaceGroup_sptr resultGroupWs,
-                      const std::vector<std::string> &convolvedMembers,
-                      const std::string &outputWsName);
+
+  bool m_deltaUsed;
 };
 
 } // namespace Algorithms
diff --git a/Framework/WorkflowAlgorithms/inc/MantidWorkflowAlgorithms/ExtractQENSMembers.h b/Framework/WorkflowAlgorithms/inc/MantidWorkflowAlgorithms/ExtractQENSMembers.h
index eb2cdb782917893f0ba12c8daecdb9db70a1e739..bb25ef3a851ead9e6f5774922828451a8ecb2d90 100644
--- a/Framework/WorkflowAlgorithms/inc/MantidWorkflowAlgorithms/ExtractQENSMembers.h
+++ b/Framework/WorkflowAlgorithms/inc/MantidWorkflowAlgorithms/ExtractQENSMembers.h
@@ -41,8 +41,12 @@ public:
 private:
   void init() override;
   void exec() override;
+  std::map<std::string, std::string> validateInputs() override;
 
-  std::vector<double> getQValues(Mantid::API::MatrixWorkspace_sptr workspace);
+  std::vector<Mantid::API::MatrixWorkspace_sptr> getInputWorkspaces() const;
+
+  std::vector<double>
+  getQValues(const std::vector<Mantid::API::MatrixWorkspace_sptr> &workspaces);
 
   std::vector<std::string>
   getAxisLabels(Mantid::API::MatrixWorkspace_sptr workspace,
diff --git a/Framework/WorkflowAlgorithms/inc/MantidWorkflowAlgorithms/IqtFitSequential.h b/Framework/WorkflowAlgorithms/inc/MantidWorkflowAlgorithms/IqtFitSequential.h
new file mode 100644
index 0000000000000000000000000000000000000000..550a8abcc3dc472da49a284c80c33169bdb88359
--- /dev/null
+++ b/Framework/WorkflowAlgorithms/inc/MantidWorkflowAlgorithms/IqtFitSequential.h
@@ -0,0 +1,46 @@
+#ifndef MANTID_ALGORITHMS_IQTFITSEQUENTIAL_H_
+#define MANTID_ALGORITHMS_IQTFITSEQUENTIAL_H_
+
+#include "MantidWorkflowAlgorithms/QENSFitSequential.h"
+
+namespace Mantid {
+namespace Algorithms {
+
+/**
+  Copyright &copy; 2015 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge
+  National Laboratory & European Spallation Source
+  This file is part of Mantid.
+  Mantid is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 3 of the License, or
+  (at your option) any later version.
+  Mantid is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+  You should have received a copy of the GNU General Public License
+  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+  File change history is stored at: <https://github.com/mantidproject/mantid>
+  Code Documentation is available at: <http://doxygen.mantidproject.org>
+*/
+class DLLExport IqtFitSequential : public QENSFitSequential {
+public:
+  const std::string name() const override;
+  int version() const override;
+  const std::string category() const override;
+  const std::string summary() const override;
+  const std::vector<std::string> seeAlso() const override;
+
+protected:
+  std::vector<API::MatrixWorkspace_sptr> getWorkspaces() const override;
+
+private:
+  std::map<std::string, std::string> validateInputs() override;
+  bool isFitParameter(const std::string &name) const override;
+  bool throwIfElasticQConversionFails() const override;
+};
+
+} // namespace Algorithms
+} // namespace Mantid
+
+#endif
diff --git a/Framework/WorkflowAlgorithms/inc/MantidWorkflowAlgorithms/QENSFitSequential.h b/Framework/WorkflowAlgorithms/inc/MantidWorkflowAlgorithms/QENSFitSequential.h
new file mode 100644
index 0000000000000000000000000000000000000000..bce0351f005f59abbfeb5b22fa77ae9d046cf64b
--- /dev/null
+++ b/Framework/WorkflowAlgorithms/inc/MantidWorkflowAlgorithms/QENSFitSequential.h
@@ -0,0 +1,92 @@
+#ifndef MANTID_ALGORITHMS_QENSFITSEQUENTIAL_H_
+#define MANTID_ALGORITHMS_QENSFITSEQUENTIAL_H_
+
+#include "MantidAPI/DataProcessorAlgorithm.h"
+#include "MantidAPI/ITableWorkspace.h"
+#include "MantidAPI/MatrixWorkspace.h"
+#include "MantidAPI/WorkspaceGroup.h"
+#include "MantidKernel/IValidator.h"
+
+#include <set>
+
+namespace Mantid {
+namespace Algorithms {
+
+/**
+  QENSFitSequential - Performs a sequential QENS fit
+
+  Copyright &copy; 2015 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge
+  National Laboratory & European Spallation Source
+  This file is part of Mantid.
+  Mantid is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 3 of the License, or
+  (at your option) any later version.
+  Mantid is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+  You should have received a copy of the GNU General Public License
+  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+  File change history is stored at: <https://github.com/mantidproject/mantid>
+  Code Documentation is available at: <http://doxygen.mantidproject.org>
+*/
+class DLLExport QENSFitSequential : public API::DataProcessorAlgorithm {
+public:
+  const std::string name() const override;
+  int version() const override;
+  const std::string category() const override;
+  const std::string summary() const override;
+  const std::vector<std::string> seeAlso() const override;
+
+protected:
+  std::map<std::string, std::string> validateInputs() override;
+  virtual std::vector<API::MatrixWorkspace_sptr> getWorkspaces() const;
+  virtual std::map<std::string, std::string> getAdditionalLogStrings() const;
+  virtual std::map<std::string, std::string> getAdditionalLogNumbers() const;
+  virtual API::ITableWorkspace_sptr performFit(const std::string &input,
+                                               const std::string &output);
+
+private:
+  void init() override;
+  void exec() override;
+  void deleteTemporaryWorkspaces(const std::string &outputBaseName);
+  void addAdditionalLogs(API::MatrixWorkspace_sptr result);
+
+  virtual bool throwIfElasticQConversionFails() const;
+  virtual bool isFitParameter(const std::string &parameterName) const;
+  std::vector<std::string> getFitParameterNames() const;
+  std::set<std::string> getUniqueParameterNames() const;
+  std::string getOutputBaseName() const;
+  std::string getInputString(
+      const std::vector<API::MatrixWorkspace_sptr> &workspaces) const;
+  API::MatrixWorkspace_sptr
+  processIndirectFitParameters(API::ITableWorkspace_sptr parameterWorkspace);
+
+  std::vector<API::MatrixWorkspace_sptr> convertInputToElasticQ(
+      const std::vector<API::MatrixWorkspace_sptr> &workspaces) const;
+
+  void renameWorkspaces(API::WorkspaceGroup_sptr outputGroup,
+                        const std::vector<std::string> &spectra);
+  void renameWorkspaces(API::WorkspaceGroup_sptr outputGroup,
+                        const std::vector<std::string> &spectra,
+                        const std::vector<API::MatrixWorkspace_sptr> &names);
+  void copyLogs(API::MatrixWorkspace_sptr resultWorkspace,
+                const std::vector<API::MatrixWorkspace_sptr> &workspaces);
+  void copyLogs(API::MatrixWorkspace_sptr resultWorkspace,
+                API::WorkspaceGroup_sptr resultGroup);
+  void extractMembers(API::WorkspaceGroup_sptr resultGroupWs,
+                      const std::vector<API::MatrixWorkspace_sptr> &workspaces,
+                      const std::string &outputWsName);
+
+  API::IAlgorithm_sptr
+  extractMembersAlgorithm(API::WorkspaceGroup_sptr resultGroupWs,
+                          const std::string &outputWsName) const;
+
+  std::string getTemporaryName() const;
+};
+
+} // namespace Algorithms
+} // namespace Mantid
+
+#endif
diff --git a/Framework/WorkflowAlgorithms/src/ConvolutionFitSequential.cpp b/Framework/WorkflowAlgorithms/src/ConvolutionFitSequential.cpp
index 52f5592090eb6e3a5cee79079e9405404b24e2ce..c1fe67f69520071a29ca4bc3c11fbed6d18ec293 100644
--- a/Framework/WorkflowAlgorithms/src/ConvolutionFitSequential.cpp
+++ b/Framework/WorkflowAlgorithms/src/ConvolutionFitSequential.cpp
@@ -1,10 +1,9 @@
 #include "MantidWorkflowAlgorithms/ConvolutionFitSequential.h"
 
 #include "MantidAPI/AlgorithmManager.h"
-#include "MantidAPI/Axis.h"
+#include "MantidAPI/CompositeFunction.h"
 #include "MantidAPI/FunctionDomain1D.h"
 #include "MantidAPI/FunctionFactory.h"
-#include "MantidAPI/IFunction.h"
 #include "MantidAPI/ITableWorkspace.h"
 #include "MantidAPI/NumericAxis.h"
 #include "MantidAPI/Progress.h"
@@ -19,12 +18,185 @@
 #include "MantidKernel/StringContainsValidator.h"
 #include "MantidKernel/VectorHelper.h"
 
+#include <algorithm>
 #include <cmath>
 
 namespace {
 Mantid::Kernel::Logger g_log("ConvolutionFitSequential");
+
+using namespace Mantid::API;
+using namespace Mantid::Kernel;
+using Mantid::MantidVec;
+
+std::size_t numberOfFunctions(IFunction_sptr function,
+                              const std::string &functionName);
+
+std::size_t numberOfFunctions(CompositeFunction_sptr composite,
+                              const std::string &functionName) {
+  std::size_t count = 0;
+  for (auto i = 0u; i < composite->nFunctions(); ++i)
+    count += numberOfFunctions(composite->getFunction(i), functionName);
+  return count;
+}
+
+std::size_t numberOfFunctions(IFunction_sptr function,
+                              const std::string &functionName) {
+  const auto composite =
+      boost::dynamic_pointer_cast<CompositeFunction>(function);
+  if (composite)
+    return numberOfFunctions(composite, functionName);
+  return function->name() == functionName ? 1 : 0;
+}
+
+bool containsFunction(IFunction_sptr function, const std::string &functionName);
+
+bool containsFunction(CompositeFunction_sptr composite,
+                      const std::string &functionName) {
+  for (auto i = 0u; i < composite->nFunctions(); ++i) {
+    if (containsFunction(composite->getFunction(i), functionName))
+      return true;
+  }
+  return false;
+}
+
+bool containsFunction(IFunction_sptr function,
+                      const std::string &functionName) {
+  const auto composite =
+      boost::dynamic_pointer_cast<CompositeFunction>(function);
+  if (function->name() == functionName)
+    return true;
+  else if (composite)
+    return containsFunction(composite, functionName);
+  return false;
+}
+
+template <typename T, typename F, typename... Ts>
+std::vector<T, Ts...> transformVector(const std::vector<T, Ts...> &vec,
+                                      F const &functor) {
+  auto target = std::vector<T, Ts...>();
+  target.reserve(vec.size());
+  std::transform(vec.begin(), vec.end(), std::back_inserter(target), functor);
+  return target;
+}
+
+template <typename T, typename F, typename... Ts>
+std::vector<T, Ts...> combineVectors(const std::vector<T, Ts...> &vec,
+                                     const std::vector<T, Ts...> &vec2,
+                                     F const &combinator) {
+  auto combined = std::vector<T, Ts...>();
+  combined.reserve(vec.size());
+  std::transform(vec.begin(), vec.end(), vec2.begin(),
+                 std::back_inserter(combined), combinator);
+  return combined;
 }
 
+template <typename T, typename... Ts>
+std::vector<T, Ts...> divideVectors(const std::vector<T, Ts...> &dividend,
+                                    const std::vector<T, Ts...> &divisor) {
+  return combineVectors(dividend, divisor, std::divides<T>());
+}
+
+template <typename T, typename... Ts>
+std::vector<double> addVectors(const std::vector<T, Ts...> &vec,
+                               const std::vector<T, Ts...> &vec2) {
+  return combineVectors(vec, vec2, std::plus<T>());
+}
+
+template <typename T, typename... Ts>
+std::vector<double> multiplyVectors(const std::vector<T, Ts...> &vec,
+                                    const std::vector<T, Ts...> &vec2) {
+  return combineVectors(vec, vec2, std::multiplies<T>());
+}
+
+template <typename T, typename... Ts>
+std::vector<T, Ts...> squareVector(const std::vector<T, Ts...> &vec) {
+  return transformVector(vec, VectorHelper::Squares<T>());
+}
+
+template <typename T, typename... Ts>
+std::vector<T, Ts...> squareRootVector(const std::vector<T, Ts...> &vec) {
+  return transformVector(vec, static_cast<T (*)(T)>(sqrt));
+}
+
+IFunction_sptr extractFirstBackground(IFunction_sptr function);
+
+IFunction_sptr extractFirstBackground(CompositeFunction_sptr composite) {
+  for (auto i = 0u; i < composite->nFunctions(); ++i) {
+    auto background = extractFirstBackground(composite->getFunction(i));
+    if (background)
+      return background;
+  }
+  return nullptr;
+}
+
+IFunction_sptr extractFirstBackground(IFunction_sptr function) {
+  auto composite = boost::dynamic_pointer_cast<CompositeFunction>(function);
+
+  if (composite)
+    return extractFirstBackground(composite);
+  else if (function->category() == "Background")
+    return function;
+  return nullptr;
+}
+
+std::string extractBackgroundType(IFunction_sptr function) {
+  auto background = extractFirstBackground(function);
+  if (!background)
+    return "None";
+
+  auto backgroundType = background->name();
+  auto position = backgroundType.rfind("Background");
+
+  if (position != std::string::npos)
+    backgroundType = backgroundType.substr(0, position);
+
+  if (background->isFixed(0))
+    backgroundType = "Fixed " + backgroundType;
+  else
+    backgroundType = "Fit " + backgroundType;
+  return backgroundType;
+}
+
+std::vector<std::size_t>
+searchForFitParameters(const std::string &suffix,
+                       ITableWorkspace_sptr tableWorkspace) {
+  auto indices = std::vector<std::size_t>();
+
+  for (auto i = 0u; i < tableWorkspace->columnCount(); ++i) {
+    auto name = tableWorkspace->getColumn(i)->name();
+    auto position = name.rfind(suffix);
+    if (position != std::string::npos &&
+        position + suffix.size() == name.size())
+      indices.emplace_back(i);
+  }
+  return indices;
+}
+
+std::pair<MantidVec, MantidVec>
+calculateEISFAndError(const MantidVec &height, const MantidVec &heightError,
+                      const MantidVec &amplitude,
+                      const MantidVec &amplitudeError) {
+  auto total = addVectors(height, amplitude);
+  auto eisfY = divideVectors(height, total);
+
+  auto heightESq = squareVector(heightError);
+
+  auto ampErrSq = squareVector(amplitudeError);
+  auto totalErr = addVectors(heightESq, ampErrSq);
+
+  auto heightYSq = squareVector(height);
+  auto totalSq = squareVector(total);
+
+  auto errOverTotalSq = divideVectors(totalErr, totalSq);
+  auto heightESqOverYSq = divideVectors(heightESq, heightYSq);
+
+  auto sqrtESqOverYSq = squareRootVector(heightESqOverYSq);
+  auto eisfYSumRoot = multiplyVectors(eisfY, sqrtESqOverYSq);
+
+  return {eisfY, addVectors(eisfYSumRoot, errOverTotalSq)};
+}
+} // namespace
+
 namespace Mantid {
 namespace Algorithms {
 
@@ -54,552 +226,89 @@ const std::string ConvolutionFitSequential::summary() const {
   return "Performs a sequential fit for a convolution workspace";
 }
 
-//----------------------------------------------------------------------------------------------
-/** Initialize the algorithm's properties.
- */
-void ConvolutionFitSequential::init() {
-  declareProperty(
-      make_unique<WorkspaceProperty<>>("InputWorkspace", "", Direction::Input),
-      "The input workspace for the fit.");
-
-  auto scv = boost::make_shared<StringContainsValidator>();
-  auto requires = std::vector<std::string>{"Convolution", "Resolution"};
-  scv->setRequiredStrings(requires);
-
-  declareProperty("Function", "", scv,
-                  "The function that describes the parameters of the fit.",
-                  Direction::Input);
-
-  declareProperty("PassWSIndexToFunction", false,
-                  "For each spectrum in Input pass its workspace index to all "
-                  "functions that have attribute WorkspaceIndex.");
-
-  std::vector<std::string> backType{"Fixed Flat", "Fit Flat", "Fit Linear"};
-
-  declareProperty("BackgroundType", "Fixed Flat",
-                  boost::make_shared<StringListValidator>(backType),
-                  "The Type of background used in the fitting",
-                  Direction::Input);
-
-  declareProperty(
-      "StartX", EMPTY_DBL(), boost::make_shared<MandatoryValidator<double>>(),
-      "The start of the range for the fit function.", Direction::Input);
-
-  declareProperty(
-      "EndX", EMPTY_DBL(), boost::make_shared<MandatoryValidator<double>>(),
-      "The end of the range for the fit function.", Direction::Input);
-
-  auto boundedV = boost::make_shared<BoundedValidator<int>>();
-  boundedV->setLower(0);
-
-  declareProperty("SpecMin", 0, boundedV, "The first spectrum to be used in "
-                                          "the fit. Spectra values can not be "
-                                          "negative",
-                  Direction::Input);
-
-  declareProperty("SpecMax", 0, boundedV, "The final spectrum to be used in "
-                                          "the fit. Spectra values can not be "
-                                          "negative",
-                  Direction::Input);
-
-  declareProperty(
-      "Convolve", true,
-      "If true, output fitted model components will be convolved with "
-      "the resolution.",
-      Direction::Input);
-
-  declareProperty(
-      "ExtractMembers", false,
-      "If true, then each member of the convolution fit will be extracted"
-      ", into their own workspace. These workspaces will have a histogram"
-      " for each spectrum (Q-value) and will be grouped.",
-      Direction::Input);
-
-  declareProperty("Minimizer", "Levenberg-Marquardt",
-                  boost::make_shared<MandatoryValidator<std::string>>(),
-                  "Minimizer to use for fitting. Minimizers available are "
-                  "'Levenberg-Marquardt', 'Simplex', 'FABADA',\n"
-                  "'Conjugate gradient (Fletcher-Reeves imp.)', 'Conjugate "
-                  "gradient (Polak-Ribiere imp.)' and 'BFGS'");
-
-  declareProperty("MaxIterations", 500, boundedV,
-                  "The maximum number of iterations permitted",
-                  Direction::Input);
-  declareProperty("PeakRadius", 0,
-                  "A value of the peak radius the peak functions should use. A "
-                  "peak radius defines an interval on the x axis around the "
-                  "centre of the peak where its values are calculated. Values "
-                  "outside the interval are not calculated and assumed zeros."
-                  "Numerically the radius is a whole number of peak widths "
-                  "(FWHM) that fit into the interval on each side from the "
-                  "centre. The default value of 0 means the whole x axis.");
-
-  declareProperty(make_unique<WorkspaceProperty<>>("OutputWorkspace", "",
-                                                   Direction::Output),
-                  "The OutputWorkspace containing the results of the fit.");
-}
-
-//----------------------------------------------------------------------------------------------
-/** Execute the algorithm.
- */
-void ConvolutionFitSequential::exec() {
-  // Initialise variables with properties
-  MatrixWorkspace_sptr inputWs = getProperty("InputWorkspace");
-  const std::string function = getProperty("Function");
-  const bool passIndex = getProperty("PassWSIndexToFunction");
-  const std::string backType =
-      convertBackToShort(getProperty("backgroundType"));
-  const double startX = getProperty("StartX");
-  const double endX = getProperty("EndX");
-  const int specMin = getProperty("SpecMin");
-  const int specMax = getProperty("Specmax");
-  const bool convolve = getProperty("Convolve");
-  const bool doExtractMembers = getProperty("ExtractMembers");
-  const int maxIter = getProperty("MaxIterations");
-  const std::string minimizer = getProperty("Minimizer");
-  const int peakRadius = getProperty("PeakRadius");
-
-  // Inspect function to obtain fit Type and background
-  const auto functionValues = findValuesFromFunction(function);
-  const auto LorentzNum = functionValues[0];
-  const auto funcName = functionValues[1];
-  std::vector<std::string> convolvedMembers;
-
-  // Check if a delta function is being used
-  auto delta = false;
-  std::string usingDelta = "false";
-  auto pos = function.find("Delta");
-  if (pos != std::string::npos) {
-    delta = true;
-    usingDelta = "true";
-    convolvedMembers.emplace_back("DeltaFunction");
-  }
-
-  if (LorentzNum == "0")
-    convolvedMembers.emplace_back(funcName);
-  else if (LorentzNum == "1")
-    convolvedMembers.emplace_back("Lorentzian");
-  else {
-    convolvedMembers.emplace_back("Lorentzian1");
-    convolvedMembers.emplace_back("Lorentzian2");
-  }
-
-  // Log information to result log
-  m_log.information("Input files: " + inputWs->getName());
-  m_log.information("Fit type: Delta=" + usingDelta + "; Lorentzians=" +
-                    LorentzNum);
-  m_log.information("Background type: " + backType);
-
-  // Output workspace name
-  auto outputWsName = inputWs->getName();
-  pos = outputWsName.rfind('_');
-  if (pos != std::string::npos) {
-    outputWsName = outputWsName.substr(0, pos + 1);
-  }
-  outputWsName += "conv_";
-  if (delta) {
-    outputWsName += "Delta";
-  }
-  if (LorentzNum != "0") {
-    outputWsName += LorentzNum + "L";
-  } else {
-    outputWsName += convertFuncToShort(funcName);
-  }
-  outputWsName += backType + "_s";
-  outputWsName += std::to_string(specMin);
-
-  if (specMin != specMax) {
-    outputWsName += "_to_";
-    outputWsName += std::to_string(specMax);
-  }
-
-  // Convert input workspace to get Q axis
-  const std::string tempFitWsName = "__convfit_fit_ws";
-  convertInputToElasticQ(inputWs, tempFitWsName);
-
-  Progress plotPeakStringProg(this, 0.0, 0.05, specMax - specMin);
-  // Construct plotpeak string
-  std::string plotPeakInput;
-  for (int i = specMin; i < specMax + 1; i++) {
-    auto nextWs = tempFitWsName + ",i";
-    nextWs += std::to_string(i);
-    plotPeakInput += nextWs + ";";
-    plotPeakStringProg.report("Constructing PlotPeak name");
-  }
-
-  // Run PlotPeaksByLogValue
-  auto plotPeaks = createChildAlgorithm("PlotPeakByLogValue", 0.05, 0.90, true);
-  plotPeaks->setProperty("Input", plotPeakInput);
-  plotPeaks->setProperty("OutputWorkspace", outputWsName);
-  plotPeaks->setProperty("Function", function);
-  plotPeaks->setProperty("StartX", startX);
-  plotPeaks->setProperty("EndX", endX);
-  plotPeaks->setProperty("FitType", "Sequential");
-  plotPeaks->setProperty("CreateOutput", true);
-  plotPeaks->setProperty("OutputCompositeMembers", true);
-  plotPeaks->setProperty("ConvolveMembers", convolve);
-  plotPeaks->setProperty("MaxIterations", maxIter);
-  plotPeaks->setProperty("Minimizer", minimizer);
-  plotPeaks->setProperty("PassWSIndexToFunction", passIndex);
-  plotPeaks->setProperty("PeakRadius", peakRadius);
-  plotPeaks->executeAsChildAlg();
-  ITableWorkspace_sptr outputWs = plotPeaks->getProperty("OutputWorkspace");
-
-  // Delete workspaces
-  Progress deleteProgress(this, 0.90, 0.91, 2);
-  auto deleter = createChildAlgorithm("DeleteWorkspace", -1.0, -1.0, false);
-  deleter->setProperty("WorkSpace",
-                       outputWsName + "_NormalisedCovarianceMatrices");
-  deleter->executeAsChildAlg();
-  deleteProgress.report("Deleting PlotPeak Output");
-
-  deleter->setProperty("Workspace", tempFitWsName);
-  deleter->executeAsChildAlg();
-
-  deleter->setProperty("WorkSpace", outputWsName + "_Parameters");
-  deleter->executeAsChildAlg();
-  deleteProgress.report("Deleting PlotPeak Output");
-
-  const auto paramTableName = outputWsName + "_Parameters";
-  AnalysisDataService::Instance().addOrReplace(paramTableName, outputWs);
-
-  // Construct output workspace
-  const auto resultWsName = outputWsName + "_Result";
-
-  Progress workflowProg(this, 0.91, 0.94, 4);
-  auto paramNames = std::vector<std::string>();
-  if (funcName == "DeltaFunction") {
-    paramNames.emplace_back("Height");
-  } else {
-    auto func = FunctionFactory::Instance().createFunction(funcName);
-    if (delta) {
-      paramNames.emplace_back("Height");
-    }
-    for (size_t i = 0; i < func->nParams(); i++) {
-      paramNames.push_back(func->parameterName(i));
-      workflowProg.report("Finding parameters to process");
-    }
-    if (funcName == "Lorentzian") {
-      // remove peak centre
-      size_t pos = find(paramNames.begin(), paramNames.end(), "PeakCentre") -
-                   paramNames.begin();
-      paramNames.erase(paramNames.begin() + pos);
-      paramNames.emplace_back("EISF");
-    }
-  }
-
-  // Run calcEISF if Delta
-  if (delta) {
-    calculateEISF(outputWs);
-  }
-
-  // Construct comma separated list for ProcessIndirectFitParameters
-  std::string paramNamesList;
-  const size_t maxNames = paramNames.size();
-  for (size_t i = 0; i < maxNames; i++) {
-    paramNamesList += paramNames.at(i);
-    if (i != (maxNames - 1)) {
-      paramNamesList += ",";
-    }
-    workflowProg.report("Constructing indirectFitParams input");
-  }
-
-  // Run ProcessIndirectFitParameters
-  auto pifp =
-      createChildAlgorithm("ProcessIndirectFitParameters", 0.94, 0.96, true);
-  pifp->setProperty("InputWorkspace", outputWs);
-  pifp->setProperty("ColumnX", "axis-1");
-  pifp->setProperty("XAxisUnit", "MomentumTransfer");
-  pifp->setProperty("ParameterNames", paramNamesList);
-  pifp->setProperty("OutputWorkspace", resultWsName);
-  pifp->executeAsChildAlg();
-
-  MatrixWorkspace_sptr resultWs = pifp->getProperty("OutputWorkspace");
-  AnalysisDataService::Instance().addOrReplace(resultWsName, resultWs);
-
-  // Handle sample logs
-  auto logCopier = createChildAlgorithm("CopyLogs", -1.0, -1.0, false);
-  logCopier->setProperty("InputWorkspace", inputWs);
-  logCopier->setProperty("OutputWorkspace", resultWs);
-  logCopier->executeAsChildAlg();
-
-  resultWs = logCopier->getProperty("OutputWorkspace");
-
-  // Create Sample Log
-  auto sampleLogStrings = std::map<std::string, std::string>();
-  sampleLogStrings["sample_filename"] = inputWs->getName();
-  sampleLogStrings["convolve_members"] = (convolve == 1) ? "true" : "false";
-  sampleLogStrings["fit_program"] = "ConvFit";
-  sampleLogStrings["background"] = backType;
-  sampleLogStrings["delta_function"] = usingDelta;
-  auto sampleLogNumeric = std::map<std::string, std::string>();
-  sampleLogNumeric["lorentzians"] =
-      boost::lexical_cast<std::string>(LorentzNum);
-
-  Progress logAdderProg(this, 0.96, 0.97, 6);
-  // Add String Logs
-  auto logAdder = createChildAlgorithm("AddSampleLog", -1.0, -1.0, false);
-  for (auto &sampleLogString : sampleLogStrings) {
-    logAdder->setProperty("Workspace", resultWs);
-    logAdder->setProperty("LogName", sampleLogString.first);
-    logAdder->setProperty("LogText", sampleLogString.second);
-    logAdder->setProperty("LogType", "String");
-    logAdder->executeAsChildAlg();
-    logAdderProg.report("Add text logs");
-  }
-
-  // Add Numeric Logs
-  for (auto &logItem : sampleLogNumeric) {
-    logAdder->setProperty("Workspace", resultWs);
-    logAdder->setProperty("LogName", logItem.first);
-    logAdder->setProperty("LogText", logItem.second);
-    logAdder->setProperty("LogType", "Number");
-    logAdder->executeAsChildAlg();
-    logAdderProg.report("Adding Numerical logs");
-  }
-
-  // Copy Logs to GroupWorkspace
-  logCopier = createChildAlgorithm("CopyLogs", 0.97, 0.98, false);
-  logCopier->setProperty("InputWorkspace", resultWs);
-  std::string groupName = outputWsName + "_Workspaces";
-  logCopier->setProperty("OutputWorkspace", groupName);
-  logCopier->executeAsChildAlg();
-
-  // Rename Workspaces in group
-  WorkspaceGroup_sptr groupWs =
-      AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>(outputWsName +
-                                                                 "_Workspaces");
-  const auto groupWsNames = groupWs->getNames();
-  auto renamer = createChildAlgorithm("RenameWorkspace", -1.0, -1.0, false);
-  Progress renamerProg(this, 0.98, 1.0, specMax + 1);
-  for (int i = specMin; i < specMax + 1; i++) {
-    renamer->setProperty("InputWorkspace", groupWsNames.at(i - specMin));
-    auto outName = outputWsName;
-
-    // Check if multiple spectrum were fit.
-    if (specMin != specMax) {
-      outName += "_" + std::to_string(i);
-    }
-    outName += "_Workspace";
-    renamer->setProperty("OutputWorkspace", outName);
-    renamer->executeAsChildAlg();
-    renamerProg.report("Renaming group workspaces");
-  }
-
-  // Check whether to extract members into their own workspaces.
-  if (doExtractMembers)
-    extractMembers(inputWs, groupWs, convolvedMembers,
-                   outputWsName + "_Members");
-
-  setProperty("OutputWorkspace", resultWs);
+/// Algorithm's see also for related algorithms. @see Algorithm::seeAlso
+const std::vector<std::string> ConvolutionFitSequential::seeAlso() const {
+  return {"QENSFitSequential"};
 }
 
-/**
- * Check function to establish if it is for one lorentzian or Two
- * @param subFunction The unchecked substring of the function
- * @return true if the function is two lorentzian false if one lorentzian
- */
-bool ConvolutionFitSequential::checkForTwoLorentz(
-    const std::string &subFunction) {
-  auto pos = subFunction.rfind("Lorentzian");
-  return pos != std::string::npos;
+std::map<std::string, std::string> ConvolutionFitSequential::validateInputs() {
+  auto errors = QENSFitSequential::validateInputs();
+  IFunction_sptr function = getProperty("Function");
+  if (!containsFunction(function, "Convolution") ||
+      !containsFunction(function, "Resolution"))
+    errors["Function"] = "Function provided does not contain convolution with "
+                         "a resolution function.";
+  return errors;
 }
 
-/**
- * Finds specific values embedded in the function supplied to the algorithm
- * @param function The full function string
- * @return all values of interest from the function (0 - fitType,  1 -
- * functionName)
- */
-std::vector<std::string>
-ConvolutionFitSequential::findValuesFromFunction(const std::string &function) {
-  std::vector<std::string> result;
-  std::string fitType;
-  std::string functionName;
-  auto startPos = function.rfind("name=");
-  if (startPos != std::string::npos) {
-    fitType = function.substr(startPos, function.size());
-    auto nextPos = fitType.find_first_of(',');
-    fitType = fitType.substr(5, nextPos - 5);
-    functionName = fitType;
-    if (fitType == "Lorentzian") {
-      std::string newSub = function.substr(0, startPos);
-      bool isTwoL = checkForTwoLorentz(newSub);
-      if (isTwoL) {
-        fitType = "2";
-      } else {
-        fitType = "1";
-      }
-    } else {
-      fitType = "0";
-    }
-    result.push_back(fitType);
-  }
-
-  result.push_back(functionName);
-  return result;
+bool ConvolutionFitSequential::throwIfElasticQConversionFails() const {
+  return true;
 }
 
-/**
- * Searchs for a given fit parameter within the a vector of columnNames
- * @param suffix - The string to search for within the columnName
- * @param columns - A vector of column names to be searched through
- * @return A vector of all the column names that contained the given suffix
- * string
- */
-std::vector<std::string> ConvolutionFitSequential::searchForFitParams(
-    const std::string &suffix, const std::vector<std::string> &columns) {
-  auto fitParams = std::vector<std::string>();
-  const size_t totalColumns = columns.size();
-  for (size_t i = 0; i < totalColumns; i++) {
-    auto pos = columns.at(i).rfind(suffix);
-    if (pos != std::string::npos) {
-      auto endCheck = pos + suffix.size();
-      if (endCheck == columns.at(i).size()) {
-        fitParams.push_back(columns.at(i));
-      }
-    }
-  }
-  return fitParams;
+bool ConvolutionFitSequential::isFitParameter(const std::string &name) const {
+  bool isBackgroundParameter = name.rfind("A0") != std::string::npos ||
+                               name.rfind("A1") != std::string::npos;
+  return name.rfind("Centre") == std::string::npos && !isBackgroundParameter;
 }
 
-/**
- * Squares all the values inside a vector of type double
- * @param target - The vector to be squared
- * @return The vector after being squared
- */
-std::vector<double>
-ConvolutionFitSequential::squareVector(std::vector<double> target) {
-  std::transform(target.begin(), target.end(), target.begin(),
-                 VectorHelper::Squares<double>());
-  return target;
+ITableWorkspace_sptr
+ConvolutionFitSequential::performFit(const std::string &input,
+                                     const std::string &output) {
+  auto parameterWorkspace = QENSFitSequential::performFit(input, output);
+  IFunction_sptr function = getProperty("Function");
+  m_deltaUsed = containsFunction(function, "DeltaFunction");
+  if (m_deltaUsed)
+    calculateEISF(parameterWorkspace);
+  return parameterWorkspace;
 }
 
-/**
- * Creates a vector of type double using the values of another vector
- * @param original - The original vector to be cloned
- * @return A clone of the original vector
- */
-std::vector<double>
-ConvolutionFitSequential::cloneVector(const std::vector<double> &original) {
-  return std::vector<double>(original.begin(), original.end());
+std::map<std::string, std::string>
+ConvolutionFitSequential::getAdditionalLogStrings() const {
+  IFunction_sptr function = getProperty("Function");
+  auto logs = QENSFitSequential::getAdditionalLogStrings();
+  logs["delta_function"] = m_deltaUsed ? "true" : "false";
+  logs["background"] = extractBackgroundType(function);
+  return logs;
 }
 
-/**
- * Converts the input workspaces to spectrum axis to ElasticQ and adds it to the
- * ADS to be used by PlotPeakBylogValue
- * @param inputWs - The MatrixWorkspace to be converted
- * @param wsName - The desired name of the output workspace
- */
-void ConvolutionFitSequential::convertInputToElasticQ(
-    API::MatrixWorkspace_sptr &inputWs, const std::string &wsName) {
-  auto axis = inputWs->getAxis(1);
-  if (axis->isSpectra()) {
-    auto convSpec = createChildAlgorithm("ConvertSpectrumAxis");
-    // Store in ADS to allow use by PlotPeakByLogValue
-    convSpec->setAlwaysStoreInADS(true);
-    convSpec->setProperty("InputWorkSpace", inputWs);
-    convSpec->setProperty("OutputWorkSpace", wsName);
-    convSpec->setProperty("Target", "ElasticQ");
-    convSpec->setProperty("EMode", "Indirect");
-    convSpec->executeAsChildAlg();
-  } else if (axis->isNumeric()) {
-    // Check that units are Momentum Transfer
-    if (axis->unit()->unitID() != "MomentumTransfer") {
-      throw std::runtime_error("Input must have axis values of Q");
-    }
-    auto cloneWs = createChildAlgorithm("CloneWorkspace");
-    // Store in ADS to allow use by PlotPeakByLogValue
-    cloneWs->setAlwaysStoreInADS(true);
-    cloneWs->setProperty("InputWorkspace", inputWs);
-    cloneWs->setProperty("OutputWorkspace", wsName);
-    cloneWs->executeAsChildAlg();
-  } else {
-    throw std::runtime_error(
-        "Input workspace must have either spectra or numeric axis.");
-  }
+std::map<std::string, std::string>
+ConvolutionFitSequential::getAdditionalLogNumbers() const {
+  auto logs = QENSFitSequential::getAdditionalLogNumbers();
+  IFunction_sptr function = getProperty("Function");
+  logs["lorentzians"] = boost::lexical_cast<std::string>(
+      numberOfFunctions(function, "Lorentzian"));
+  return logs;
 }
 
 /**
  * Calculates the EISF if the fit includes a Delta function
  * @param tableWs - The TableWorkspace to append the EISF calculation to
  */
-void ConvolutionFitSequential::calculateEISF(
-    API::ITableWorkspace_sptr &tableWs) {
+void ConvolutionFitSequential::calculateEISF(ITableWorkspace_sptr &tableWs) {
   // Get height data from parameter table
-  const auto columns = tableWs->getColumnNames();
-  const auto height = searchForFitParams("Height", columns).at(0);
-  const auto heightErr = searchForFitParams("Height_Err", columns).at(0);
+  const auto height = searchForFitParameters("Height", tableWs).at(0);
+  const auto heightErr = searchForFitParameters("Height_Err", tableWs).at(0);
   auto heightY = tableWs->getColumn(height)->numeric_fill<>();
   auto heightE = tableWs->getColumn(heightErr)->numeric_fill<>();
 
   // Get amplitude column names
-  const auto ampNames = searchForFitParams("Amplitude", columns);
-  const auto ampErrorNames = searchForFitParams("Amplitude_Err", columns);
+  const auto ampIndices = searchForFitParameters("Amplitude", tableWs);
+  const auto ampErrorIndices = searchForFitParameters("Amplitude_Err", tableWs);
 
   // For each lorentzian, calculate EISF
-  size_t maxSize = ampNames.size();
-  if (ampErrorNames.size() > maxSize) {
-    maxSize = ampErrorNames.size();
-  }
-  for (size_t i = 0; i < maxSize; i++) {
+  auto maxSize = ampIndices.size();
+  if (ampErrorIndices.size() > maxSize)
+    maxSize = ampErrorIndices.size();
+
+  for (auto i = 0u; i < maxSize; ++i) {
     // Get amplitude from column in table workspace
-    const auto ampName = ampNames.at(i);
-    auto ampY = tableWs->getColumn(ampName)->numeric_fill<>();
-    const auto ampErrorName = ampErrorNames.at(i);
-    auto ampErr = tableWs->getColumn(ampErrorName)->numeric_fill<>();
-
-    // Calculate EISF and EISF error
-    // total = heightY + ampY
-    auto total = cloneVector(heightY);
-    std::transform(total.begin(), total.end(), ampY.begin(), total.begin(),
-                   std::plus<double>());
-    // eisfY = heightY / total
-    auto eisfY = cloneVector(heightY);
-    std::transform(eisfY.begin(), eisfY.end(), total.begin(), eisfY.begin(),
-                   std::divides<double>());
-    // heightE squared
-    auto heightESq = cloneVector(heightE);
-    heightESq = squareVector(heightESq);
-    // ampErr squared
-    auto ampErrSq = cloneVector(ampErr);
-    ampErrSq = squareVector(ampErrSq);
-    // totalErr = heightE squared + ampErr squared
-    auto totalErr = cloneVector(heightESq);
-    std::transform(totalErr.begin(), totalErr.end(), ampErrSq.begin(),
-                   totalErr.begin(), std::plus<double>());
-    // heightY squared
-    auto heightYSq = cloneVector(heightY);
-    heightYSq = squareVector(heightYSq);
-    // total Squared
-    auto totalSq = cloneVector(total);
-    totalSq = squareVector(totalSq);
-    // errOverTotalSq = totalErr / total squared
-    auto errOverTotalSq = cloneVector(totalErr);
-    std::transform(errOverTotalSq.begin(), errOverTotalSq.end(),
-                   totalSq.begin(), errOverTotalSq.begin(),
-                   std::divides<double>());
-    // heightESqOverYSq = heightESq / heightYSq
-    auto heightESqOverYSq = cloneVector(heightESq);
-    std::transform(heightESqOverYSq.begin(), heightESqOverYSq.end(),
-                   heightYSq.begin(), heightESqOverYSq.begin(),
-                   std::divides<double>());
-    // sqrtESqOverYSq = squareRoot( heightESqOverYSq )
-    auto sqrtESqOverYSq = cloneVector(heightESqOverYSq);
-    std::transform(sqrtESqOverYSq.begin(), sqrtESqOverYSq.end(),
-                   sqrtESqOverYSq.begin(),
-                   static_cast<double (*)(double)>(sqrt));
-    // eisfYSumRoot = eisfY * sqrtESqOverYSq
-    auto eisfYSumRoot = cloneVector(eisfY);
-    std::transform(eisfYSumRoot.begin(), eisfYSumRoot.end(),
-                   sqrtESqOverYSq.begin(), eisfYSumRoot.begin(),
-                   std::multiplies<double>());
-    // eisfErr = eisfYSumRoot + errOverTotalSq
-    auto eisfErr = cloneVector(eisfYSumRoot);
-    std::transform(eisfErr.begin(), eisfErr.end(), errOverTotalSq.begin(),
-                   eisfErr.begin(), std::plus<double>());
+    auto ampY = tableWs->getColumn(ampIndices[i])->numeric_fill<>();
+    auto ampErr = tableWs->getColumn(ampErrorIndices[i])->numeric_fill<>();
+    auto eisfAndError = calculateEISFAndError(heightY, heightE, ampY, ampErr);
 
     // Append the calculated values to the table workspace
+    auto ampName = tableWs->getColumn(ampIndices[i])->name();
+    auto ampErrorName = tableWs->getColumn(ampErrorIndices[i])->name();
     auto columnName =
         ampName.substr(0, (ampName.size() - std::string("Amplitude").size()));
     columnName += "EISF";
@@ -609,87 +318,18 @@ void ConvolutionFitSequential::calculateEISF(
 
     tableWs->addColumn("double", columnName);
     tableWs->addColumn("double", errorColumnName);
-    size_t maxEisf = eisfY.size();
-    if (eisfErr.size() > maxEisf) {
-      maxEisf = eisfErr.size();
+    auto maxEisf = eisfAndError.first.size();
+    if (eisfAndError.second.size() > maxEisf) {
+      maxEisf = eisfAndError.second.size();
     }
 
-    Column_sptr col = tableWs->getColumn(columnName);
-    Column_sptr errCol = tableWs->getColumn(errorColumnName);
-    for (size_t j = 0; j < maxEisf; j++) {
-      col->cell<double>(j) = eisfY.at(j);
-      errCol->cell<double>(j) = eisfErr.at(j);
-    }
-  }
-}
-
-/*
- * Extracts the convolution fit members from the specified result group
- * workspace, given the specified input workspace used for the fit, each into a
- * workspace, stored inside a group workspace of the specified name.
- *
- * @param inputWs       The input workspace used in the convolution fit.
- * @param resultGroupWs The result group workspace produced by the convolution
- *                      fit; from which to extract the members.
- * @param outputWsName  The name of the output group workspace to store the
- *                      member workspaces.
- */
-void ConvolutionFitSequential::extractMembers(
-    MatrixWorkspace_sptr inputWs, WorkspaceGroup_sptr resultGroupWs,
-    const std::vector<std::string> &convolvedMembers,
-    const std::string &outputWsName) {
-  bool convolved = getProperty("Convolve");
-  auto extractMembersAlg =
-      AlgorithmManager::Instance().create("ExtractQENSMembers");
-  extractMembersAlg->setProperty("InputWorkspace", inputWs);
-  extractMembersAlg->setProperty("ResultWorkspace", resultGroupWs);
-  extractMembersAlg->setProperty("OutputWorkspace", outputWsName);
-  extractMembersAlg->setProperty("RenameConvolvedMembers", convolved);
-  extractMembersAlg->setProperty("ConvolvedMembers", convolvedMembers);
-  extractMembersAlg->execute();
-}
-
-/**
- * Converts the user input for background into short hand for use in the
- * workspace naming
- * @param original - The original user input to the function
- * @return The short hand of the users input
- */
-std::string
-ConvolutionFitSequential::convertBackToShort(const std::string &original) {
-  auto result = original.substr(0, 3);
-  const auto pos = original.find(' ');
-  if (pos != std::string::npos) {
-    result += original.at(pos + 1);
-  }
-  return result;
-}
-
-/**
- * Converts the user input for function into short hand for use in the workspace
- * naming
- * @param original - The original user input to the function
- * @return The short hand of the users input
- */
-std::string
-ConvolutionFitSequential::convertFuncToShort(const std::string &original) {
-  std::string result;
-  if (original != "DeltaFunction") {
-    if (original.at(0) == 'E') {
-      result += "E";
-    } else if (original.at(0) == 'I') {
-      result += "I";
-    } else {
-      return "SFT";
-    }
-    const auto pos = original.find("Circle");
-    if (pos != std::string::npos) {
-      result += "DC";
-    } else {
-      result += "DS";
+    auto col = tableWs->getColumn(columnName);
+    auto errCol = tableWs->getColumn(errorColumnName);
+    for (auto j = 0u; j < maxEisf; j++) {
+      col->cell<double>(j) = eisfAndError.first.at(j);
+      errCol->cell<double>(j) = eisfAndError.second.at(j);
     }
   }
-  return result;
 }
 
 } // namespace Algorithms
diff --git a/Framework/WorkflowAlgorithms/src/ExtractQENSMembers.cpp b/Framework/WorkflowAlgorithms/src/ExtractQENSMembers.cpp
index 8bf115c60875d9a038c73a547187454d82fd4e8e..6c792e113c6ec46fed8c2ab2ac5dd5867ac5b1fb 100644
--- a/Framework/WorkflowAlgorithms/src/ExtractQENSMembers.cpp
+++ b/Framework/WorkflowAlgorithms/src/ExtractQENSMembers.cpp
@@ -1,5 +1,6 @@
 #include "MantidWorkflowAlgorithms/ExtractQENSMembers.h"
 
+#include "MantidAPI/ADSValidator.h"
 #include "MantidAPI/Axis.h"
 #include "MantidAPI/NumericAxis.h"
 
@@ -45,8 +46,13 @@ const std::string ExtractQENSMembers::summary() const {
  */
 void ExtractQENSMembers::init() {
   declareProperty(
-      make_unique<WorkspaceProperty<>>("InputWorkspace", "", Direction::Input),
-      "The input workspace used in the fit.");
+      make_unique<WorkspaceProperty<>>("InputWorkspace", "", Direction::Input,
+                                       PropertyMode::Optional),
+      "The input workspace used in the fit. Ignored if 'InputWorkspaces' "
+      "property is provided.");
+  declareProperty(
+      make_unique<ArrayProperty<std::string>>("InputWorkspaces", ""),
+      "List of the workspaces used in the fit.");
   declareProperty(make_unique<WorkspaceProperty<WorkspaceGroup>>(
                       "ResultWorkspace", "", Direction::Input),
                   "The result group workspace produced in a QENS fit.");
@@ -63,12 +69,23 @@ void ExtractQENSMembers::init() {
                   "The output workspace group, containing the fit members.");
 }
 
+std::map<std::string, std::string> ExtractQENSMembers::validateInputs() {
+  std::map<std::string, std::string> errors;
+  std::vector<std::string> workspaceNames = getProperty("InputWorkspaces");
+  MatrixWorkspace_sptr inputWorkspace = getProperty("InputWorkspace");
+
+  if (workspaceNames.empty() && !inputWorkspace)
+    errors["InputWorkspace"] = "Neither the InputWorkspace or InputWorkspaces "
+                               "property have been defined.";
+  return errors;
+}
+
 void ExtractQENSMembers::exec() {
-  MatrixWorkspace_sptr inputWS = getProperty("InputWorkspace");
+  auto inputWorkspaces = getInputWorkspaces();
   WorkspaceGroup_sptr resultWS = getProperty("ResultWorkspace");
   MatrixWorkspace_sptr initialWS =
       boost::dynamic_pointer_cast<MatrixWorkspace>(resultWS->getItem(0));
-  auto qValues = getQValues(inputWS);
+  const auto qValues = getQValues(inputWorkspaces);
   auto members = getAxisLabels(initialWS, 1);
 
   bool renameConvolved = getProperty("RenameConvolvedMembers");
@@ -89,18 +106,43 @@ void ExtractQENSMembers::exec() {
   setProperty("OutputWorkspace", groupWorkspaces(workspaceNames));
 }
 
+std::vector<MatrixWorkspace_sptr>
+ExtractQENSMembers::getInputWorkspaces() const {
+  std::vector<MatrixWorkspace_sptr> workspaces;
+  std::vector<std::string> workspaceNames = getProperty("InputWorkspaces");
+
+  for (const auto &name : workspaceNames)
+    workspaces.emplace_back(
+        AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(name));
+
+  if (!workspaces.empty()) {
+    workspaces.reserve(workspaceNames.size());
+    for (const auto &name : workspaceNames)
+      workspaces.emplace_back(
+          AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(name));
+  } else
+    workspaces.push_back(getProperty("InputWorkspace"));
+  return workspaces;
+}
+
 /**
  * Extracts the Q-Values from the specified workspace.
  *
- * @param workspace The workspace whose Q-Values to extract.
- * @return          The extracted Q-Values.
+ * @param workspaces The workspaces whose Q-Values to extract.
+ * @return            The extracted Q-Values.
  */
-std::vector<double>
-ExtractQENSMembers::getQValues(MatrixWorkspace_sptr workspace) {
-  auto getQs = createChildAlgorithm("GetQsInQENSData", -1.0, -1.0, false);
-  getQs->setProperty("InputWorkspace", workspace);
-  getQs->executeAsChildAlg();
-  return getQs->getProperty("Qvalues");
+std::vector<double> ExtractQENSMembers::getQValues(
+    const std::vector<MatrixWorkspace_sptr> &workspaces) {
+  std::vector<double> qValues;
+
+  for (const auto &workspace : workspaces) {
+    auto getQs = createChildAlgorithm("GetQsInQENSData", -1.0, -1.0, false);
+    getQs->setProperty("InputWorkspace", workspace);
+    getQs->executeAsChildAlg();
+    const std::vector<double> values = getQs->getProperty("Qvalues");
+    qValues.insert(std::end(qValues), std::begin(values), std::end(values));
+  }
+  return qValues;
 }
 
 /**
diff --git a/Framework/WorkflowAlgorithms/src/IqtFitSequential.cpp b/Framework/WorkflowAlgorithms/src/IqtFitSequential.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..82c0ae4a30d3c831ff2f42034016109b03d56932
--- /dev/null
+++ b/Framework/WorkflowAlgorithms/src/IqtFitSequential.cpp
@@ -0,0 +1,90 @@
+#include "MantidWorkflowAlgorithms/IqtFitSequential.h"
+
+using namespace Mantid::API;
+
+namespace {
+Mantid::Kernel::Logger g_log("IqtFitSequential");
+
+MatrixWorkspace_sptr cropWorkspace(MatrixWorkspace_sptr workspace,
+                                   double startX, double endX) {
+  auto cropper = AlgorithmManager::Instance().create("CropWorkspace");
+  cropper->setLogging(false);
+  cropper->setAlwaysStoreInADS(false);
+  cropper->setProperty("InputWorkspace", workspace);
+  cropper->setProperty("OutputWorkspace", "__cropped");
+  cropper->setProperty("XMin", startX);
+  cropper->setProperty("XMax", endX);
+  cropper->execute();
+  return cropper->getProperty("OutputWorkspace");
+}
+
+MatrixWorkspace_sptr convertToHistogram(MatrixWorkspace_sptr workspace) {
+  auto converter = AlgorithmManager::Instance().create("ConvertToHistogram");
+  converter->setLogging(false);
+  converter->setAlwaysStoreInADS(false);
+  converter->setProperty("InputWorkspace", workspace);
+  converter->setProperty("OutputWorkspace", "__converted");
+  converter->execute();
+  return converter->getProperty("OutputWorkspace");
+}
+} // namespace
+
+namespace Mantid {
+namespace Algorithms {
+
+using namespace API;
+
+// Register the algorithm into the AlgorithmFactory
+DECLARE_ALGORITHM(IqtFitSequential)
+
+/// Algorithms name for identification. @see Algorithm::name
+const std::string IqtFitSequential::name() const { return "IqtFitSequential"; }
+
+/// Algorithm's version for identification. @see Algorithm::version
+int IqtFitSequential::version() const { return 1; }
+
+/// Algorithm's category for identification. @see Algorithm::category
+const std::string IqtFitSequential::category() const {
+  return "Workflow\\MIDAS";
+}
+
+/// Algorithm's summary for use in the GUI and help. @see Algorithm::summary
+const std::string IqtFitSequential::summary() const {
+  return "Fits an \\*\\_iqt file generated by I(Q, t) sequentially.";
+}
+
+/// Algorithm's see also for related algorithms. @see Algorithm::seeAlso
+const std::vector<std::string> IqtFitSequential::seeAlso() const {
+  return {"QENSFitSequential"};
+}
+
+std::map<std::string, std::string> IqtFitSequential::validateInputs() {
+  auto errors = QENSFitSequential::validateInputs();
+  double startX = getProperty("StartX");
+  if (startX < 0)
+    errors["StartX"] = "StartX must be greater than or equal to 0.";
+  return errors;
+}
+
+bool IqtFitSequential::isFitParameter(const std::string &name) const {
+  return name.rfind("A0") != std::string::npos ||
+         name.rfind("Height") != std::string::npos ||
+         name.rfind("Stretching") != std::string::npos ||
+         name.rfind("Lifetime") != std::string::npos;
+}
+
+bool IqtFitSequential::throwIfElasticQConversionFails() const { return true; }
+
+std::vector<API::MatrixWorkspace_sptr> IqtFitSequential::getWorkspaces() const {
+  auto workspaces = QENSFitSequential::getWorkspaces();
+  const double startX = getProperty("StartX");
+  const double endX = getProperty("EndX");
+
+  for (auto i = 0u; i < workspaces.size(); ++i)
+    workspaces[i] =
+        convertToHistogram(cropWorkspace(workspaces[i], startX, endX));
+  return workspaces;
+}
+
+} // namespace Algorithms
+} // namespace Mantid
diff --git a/Framework/WorkflowAlgorithms/src/ProcessIndirectFitParameters.cpp b/Framework/WorkflowAlgorithms/src/ProcessIndirectFitParameters.cpp
index d1aa464a75cdfdf73d0ac5981e1f4ef5a37f5c16..ec98c9067d7fdfdb14a9bbac37d50c1b46be519e 100644
--- a/Framework/WorkflowAlgorithms/src/ProcessIndirectFitParameters.cpp
+++ b/Framework/WorkflowAlgorithms/src/ProcessIndirectFitParameters.cpp
@@ -4,10 +4,10 @@
 #include "MantidAPI/MatrixWorkspace.h"
 #include "MantidAPI/TextAxis.h"
 
-#include "MantidKernel/MandatoryValidator.h"
+#include "MantidKernel/ArrayProperty.h"
 #include "MantidKernel/ListValidator.h"
+#include "MantidKernel/MandatoryValidator.h"
 #include "MantidKernel/UnitFactory.h"
-#include "MantidKernel/VectorHelper.h"
 
 namespace Mantid {
 namespace Algorithms {
@@ -55,10 +55,11 @@ void ProcessIndirectFitParameters::init() {
       "ColumnX", "", boost::make_shared<MandatoryValidator<std::string>>(),
       "The column in the table to use for the x values.", Direction::Input);
 
-  declareProperty("ParameterNames", "",
-                  boost::make_shared<MandatoryValidator<std::string>>(),
-                  "List of the parameter names to add to the workspace.",
-                  Direction::Input);
+  declareProperty(
+      make_unique<ArrayProperty<std::string>>(
+          "ParameterNames",
+          boost::make_shared<MandatoryValidator<std::vector<std::string>>>()),
+      "List of the parameter names to add to the workspace.");
 
   declareProperty("XAxisUnit", "",
                   boost::make_shared<StringListValidator>(unitOptions),
@@ -76,10 +77,7 @@ void ProcessIndirectFitParameters::exec() {
   // Get Properties
   ITableWorkspace_sptr inputWs = getProperty("InputWorkspace");
   std::string xColumn = getProperty("ColumnX");
-  std::string parameterNamesProp = getProperty("ParameterNames");
-  auto parameterNames =
-      Kernel::VectorHelper::splitStringIntoVector<std::string>(
-          parameterNamesProp);
+  std::vector<std::string> parameterNames = getProperty("ParameterNames");
   std::string xUnit = getProperty("XAxisUnit");
   MatrixWorkspace_sptr outputWs = getProperty("OutputWorkspace");
 
diff --git a/Framework/WorkflowAlgorithms/src/QENSFitSequential.cpp b/Framework/WorkflowAlgorithms/src/QENSFitSequential.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0adcb72039b1bdb61758c766eaa86e75b180f8eb
--- /dev/null
+++ b/Framework/WorkflowAlgorithms/src/QENSFitSequential.cpp
@@ -0,0 +1,695 @@
+#include "MantidWorkflowAlgorithms/QENSFitSequential.h"
+
+#include "MantidAPI/AlgorithmManager.h"
+#include "MantidAPI/Axis.h"
+#include "MantidAPI/CompositeFunction.h"
+#include "MantidAPI/CostFunctionFactory.h"
+#include "MantidAPI/FunctionProperty.h"
+#include "MantidAPI/IFunction.h"
+#include "MantidKernel/BoundedValidator.h"
+#include "MantidKernel/ListValidator.h"
+#include "MantidKernel/MandatoryValidator.h"
+
+#include <boost/cast.hpp>
+#include <boost/regex.hpp>
+
+#include <sstream>
+#include <stdexcept>
+#include <unordered_map>
+
+namespace {
+using namespace Mantid::API;
+using namespace Mantid::Kernel;
+
+MatrixWorkspace_sptr convertSpectrumAxis(MatrixWorkspace_sptr inputWorkspace,
+                                         const std::string &outputName) {
+  auto convSpec = AlgorithmManager::Instance().create("ConvertSpectrumAxis");
+  convSpec->setLogging(false);
+  convSpec->setProperty("InputWorkspace", inputWorkspace);
+  convSpec->setProperty("OutputWorkspace", outputName);
+  convSpec->setProperty("Target", "ElasticQ");
+  convSpec->setProperty("EMode", "Indirect");
+  convSpec->execute();
+  // Attempting to use getProperty("OutputWorkspace") on algorithm results in a
+  // nullptr being returned
+  return AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(
+      outputName);
+}
+
+MatrixWorkspace_sptr cloneWorkspace(MatrixWorkspace_sptr inputWorkspace,
+                                    const std::string &outputName) {
+  Workspace_sptr workspace = inputWorkspace->clone();
+  AnalysisDataService::Instance().addOrReplace(outputName, workspace);
+  return boost::dynamic_pointer_cast<MatrixWorkspace>(workspace);
+}
+
+MatrixWorkspace_sptr convertToElasticQ(MatrixWorkspace_sptr inputWorkspace,
+                                       const std::string &outputName,
+                                       bool doThrow) {
+  auto axis = inputWorkspace->getAxis(1);
+  if (axis->isSpectra())
+    return convertSpectrumAxis(inputWorkspace, outputName);
+  else if (axis->isNumeric()) {
+    if (axis->unit()->unitID() != "MomentumTransfer" && doThrow)
+      throw std::runtime_error("Input must have axis values of Q");
+    return cloneWorkspace(inputWorkspace, outputName);
+  } else if (doThrow)
+    throw std::runtime_error(
+        "Input workspace must have either spectra or numeric axis.");
+  return cloneWorkspace(inputWorkspace, outputName);
+}
+
+struct ElasticQAppender {
+  explicit ElasticQAppender(std::vector<MatrixWorkspace_sptr> &elasticInput)
+      : m_elasticInput(elasticInput), m_converted() {}
+
+  void operator()(MatrixWorkspace_sptr workspace, const std::string &outputBase,
+                  bool doThrow) {
+    auto it = m_converted.find(workspace.get());
+    if (it != m_converted.end())
+      m_elasticInput.emplace_back(it->second);
+    else {
+      auto elasticQ = convertToElasticQ(
+          workspace, outputBase + std::to_string(m_converted.size() + 1),
+          doThrow);
+      m_elasticInput.emplace_back(elasticQ);
+      m_converted[workspace.get()] = elasticQ;
+    }
+  }
+
+private:
+  std::vector<MatrixWorkspace_sptr> &m_elasticInput;
+  std::unordered_map<MatrixWorkspace *, MatrixWorkspace_sptr> m_converted;
+};
+
+std::vector<MatrixWorkspace_sptr>
+convertToElasticQ(const std::vector<MatrixWorkspace_sptr> &workspaces,
+                  const std::string &outputBaseName, bool doThrow) {
+  std::vector<MatrixWorkspace_sptr> elasticInput;
+  auto appendElasticQWorkspace = ElasticQAppender(elasticInput);
+  appendElasticQWorkspace(workspaces[0], outputBaseName, doThrow);
+
+  for (auto i = 1u; i < workspaces.size(); ++i)
+    appendElasticQWorkspace(workspaces[i], outputBaseName, doThrow);
+  return elasticInput;
+}
+
+void extractFunctionNames(CompositeFunction_sptr composite,
+                          std::vector<std::string> &names) {
+  for (auto i = 0u; i < composite->nFunctions(); ++i)
+    names.emplace_back(composite->getFunction(i)->name());
+}
+
+void extractFunctionNames(IFunction_sptr function,
+                          std::vector<std::string> &names) {
+  auto composite = boost::dynamic_pointer_cast<CompositeFunction>(function);
+  if (composite)
+    extractFunctionNames(composite, names);
+  else
+    names.emplace_back(function->name());
+}
+
+void extractConvolvedNames(IFunction_sptr function,
+                           std::vector<std::string> &names);
+
+void extractConvolvedNames(CompositeFunction_sptr composite,
+                           std::vector<std::string> &names) {
+  for (auto i = 0u; i < composite->nFunctions(); ++i)
+    extractConvolvedNames(composite->getFunction(i), names);
+}
+
+void extractConvolvedNames(IFunction_sptr function,
+                           std::vector<std::string> &names) {
+  auto composite = boost::dynamic_pointer_cast<CompositeFunction>(function);
+  if (composite) {
+    if (composite->name() == "Convolution" && composite->nFunctions() > 1 &&
+        composite->getFunction(0)->name() == "Resolution")
+      extractFunctionNames(composite->getFunction(1), names);
+    else
+      extractConvolvedNames(composite, names);
+  }
+}
+
+std::string constructInputString(MatrixWorkspace_sptr workspace, int specMin,
+                                 int specMax) {
+  std::ostringstream input;
+  for (auto i = specMin; i < specMax + 1; ++i)
+    input << workspace->getName() << ",i" << std::to_string(i) << ";";
+  return input.str();
+}
+
+std::vector<MatrixWorkspace_sptr> extractWorkspaces(const std::string &input) {
+  std::vector<MatrixWorkspace_sptr> workspaces;
+
+  auto extractWorkspace = [&](const std::string &name) {
+    workspaces.emplace_back(
+        AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(name));
+  };
+
+  boost::regex reg("([^,;]+),");
+  std::for_each(
+      boost::sregex_token_iterator(input.begin(), input.end(), reg, 1),
+      boost::sregex_token_iterator(), extractWorkspace);
+  return workspaces;
+}
+
+std::vector<std::string> getSpectra(const std::string &input) {
+  std::vector<std::string> spectra;
+  boost::regex reg(",[i|sp](0|[1-9][0-9]*);?");
+  std::copy(boost::sregex_token_iterator(input.begin(), input.end(), reg, 1),
+            boost::sregex_token_iterator(), std::back_inserter(spectra));
+  return spectra;
+}
+
+std::vector<std::string> getSuffices(const std::string &input) {
+  std::vector<std::string> suffices;
+  boost::regex reg(",[i|sp](0|[1-9][0-9]*);?");
+  std::copy(boost::sregex_token_iterator(input.begin(), input.end(), reg, 0),
+            boost::sregex_token_iterator(), std::back_inserter(suffices));
+  return suffices;
+}
+
+std::string
+replaceWorkspaces(const std::string &input,
+                  const std::vector<MatrixWorkspace_sptr> &workspaces) {
+  const auto suffices = getSuffices(input);
+  std::stringstream newInput;
+  for (auto i = 0u; i < workspaces.size(); ++i)
+    newInput << workspaces[i]->getName() << suffices[i];
+  return newInput.str();
+}
+
+void renameWorkspace(IAlgorithm_sptr renamer, Workspace_sptr workspace,
+                     const std::string &newName) {
+  renamer->setProperty("InputWorkspace", workspace);
+  renamer->setProperty("OutputWorkspace", newName);
+  renamer->executeAsChildAlg();
+}
+
+void deleteTemporaries(IAlgorithm_sptr deleter, const std::string &base) {
+  auto name = base + std::to_string(1);
+  std::size_t i = 2;
+
+  while (AnalysisDataService::Instance().doesExist(name)) {
+    deleter->setProperty("Workspace", name);
+    deleter->executeAsChildAlg();
+    name = base + std::to_string(i++);
+  }
+}
+
+std::string shortParameterName(const std::string &longName) {
+  return longName.substr(longName.rfind('.') + 1, longName.size());
+}
+
+bool containsMultipleData(const std::vector<MatrixWorkspace_sptr> &workspaces) {
+  const auto &first = workspaces.front();
+  for (const auto &workspace : workspaces) {
+    if (workspace != first)
+      return true;
+  }
+  return false;
+}
+
+template <typename F, typename Renamer>
+void renameWorkspacesWith(WorkspaceGroup_sptr groupWorkspace, F const &getName,
+                          Renamer const &renamer) {
+  std::unordered_map<std::string, std::size_t> nameCount;
+  for (auto i = 0u; i < groupWorkspace->size(); ++i) {
+    const auto name = getName(i);
+    auto count = nameCount.find(name);
+
+    if (count == nameCount.end()) {
+      renamer(groupWorkspace->getItem(i), name);
+      nameCount[name] = 1;
+    } else
+      renamer(groupWorkspace->getItem(i),
+              name + "(" + std::to_string(++count->second) + ")");
+  }
+}
+
+template <typename F>
+void renameWorkspacesInQENSFit(Algorithm *qensFit,
+                               IAlgorithm_sptr renameAlgorithm,
+                               WorkspaceGroup_sptr outputGroup,
+                               const F &getNameSuffix) {
+  const auto groupName = qensFit->getPropertyValue("OutputWorkspaceGroup");
+  auto outputBase = groupName.substr(0, groupName.rfind("_Workspaces"));
+
+  Progress renamerProg(qensFit, 0.98, 1.0, outputGroup->size() + 1);
+  renamerProg.report("Renaming group workspaces...");
+
+  auto getName =
+      [&](std::size_t i) { return outputBase + "_" + getNameSuffix(i); };
+
+  auto renamer = [&](Workspace_sptr workspace, const std::string &name) {
+    renameWorkspace(renameAlgorithm, workspace, name);
+    renamerProg.report("Renamed workspace in group.");
+  };
+  renameWorkspacesWith(outputGroup, getName, renamer);
+
+  if (outputGroup->getName() != groupName)
+    renameWorkspace(renameAlgorithm, outputGroup, groupName);
+}
+} // namespace
+
+namespace Mantid {
+namespace Algorithms {
+
+using namespace API;
+using namespace Kernel;
+
+// Register the algorithm into the AlgorithmFactory
+DECLARE_ALGORITHM(QENSFitSequential)
+
+/// Algorithms name for identification. @see Algorithm::name
+const std::string QENSFitSequential::name() const {
+  return "QENSFitSequential";
+}
+
+/// Algorithm's version for identification. @see Algorithm::version
+int QENSFitSequential::version() const { return 1; }
+
+/// Algorithm's category for identification. @see Algorithm::category
+const std::string QENSFitSequential::category() const {
+  return "Workflow\\MIDAS";
+}
+
+/// Algorithm's summary for use in the GUI and help. @see Algorithm::summary
+const std::string QENSFitSequential::summary() const {
+  return "Performs a sequential fit for QENS data";
+}
+
+/// Algorithm's see also for related algorithms. @see Algorithm::seeAlso
+const std::vector<std::string> QENSFitSequential::seeAlso() const {
+  return {"ConvolutionFitSequential", "IqtFitSequential", "PlotPeakByLogValue"};
+}
+
+void QENSFitSequential::init() {
+  declareProperty(
+      make_unique<WorkspaceProperty<>>("InputWorkspace", "", Direction::Input,
+                                       PropertyMode::Optional),
+      "The input workspace for the fit. This property will be ignored if "
+      "'Input' is provided.");
+
+  auto boundedV = boost::make_shared<BoundedValidator<int>>();
+  boundedV->setLower(0);
+
+  declareProperty(
+      "SpecMin", 0, boundedV,
+      "The first spectrum to be used in "
+      "the fit. Spectra values can not be "
+      "negative. This property will be ignored if 'Input' is provided.",
+      Direction::Input);
+
+  declareProperty(
+      "SpecMax", 0, boundedV,
+      "The final spectrum to be used in "
+      "the fit. Spectra values can not be "
+      "negative. This property will be ignored if 'Input' is provided.",
+      Direction::Input);
+
+  declareProperty(
+      "Input", "",
+      "A list of sources of data to fit. \n"
+      "Sources can be either workspace names or file names followed optionally "
+      "by a list of spectra/workspace-indices \n"
+      "or values using the notation described in the description section of "
+      "the help page.");
+
+  declareProperty(make_unique<WorkspaceProperty<MatrixWorkspace>>(
+                      "OutputWorkspace", "", Direction::Output),
+                  "The output result workspace");
+  declareProperty(make_unique<WorkspaceProperty<ITableWorkspace>>(
+                      "OutputParameterWorkspace", "", Direction::Output,
+                      PropertyMode::Optional),
+                  "The output parameter workspace");
+  declareProperty(make_unique<WorkspaceProperty<WorkspaceGroup>>(
+                      "OutputWorkspaceGroup", "", Direction::Output,
+                      PropertyMode::Optional),
+                  "The output group workspace");
+
+  declareProperty(
+      make_unique<FunctionProperty>("Function"),
+      "The fitting function, common for all workspaces in the input.");
+  declareProperty("LogValue", "", "Name of the log value to plot the "
+                                  "parameters against. Default: use spectra "
+                                  "numbers.");
+  declareProperty("StartX", EMPTY_DBL(), "A value of x in, or on the low x "
+                                         "boundary of, the first bin to "
+                                         "include in\n"
+                                         "the fit (default lowest value of x)");
+  declareProperty("EndX", EMPTY_DBL(), "A value in, or on the high x boundary "
+                                       "of, the last bin the fitting range\n"
+                                       "(default the highest value of x)");
+
+  declareProperty("PassWSIndexToFunction", false,
+                  "For each spectrum in Input pass its workspace index to all "
+                  "functions that"
+                  "have attribute WorkspaceIndex.");
+
+  declareProperty("Minimizer", "Levenberg-Marquardt",
+                  "Minimizer to use for fitting. Minimizers available are "
+                  "'Levenberg-Marquardt', 'Simplex', 'FABADA',\n"
+                  "'Conjugate gradient (Fletcher-Reeves imp.)', 'Conjugate "
+                  "gradient (Polak-Ribiere imp.)' and 'BFGS'");
+
+  const std::vector<std::string> costFuncOptions =
+      CostFunctionFactory::Instance().getKeys();
+  declareProperty("CostFunction", "Least squares",
+                  boost::make_shared<StringListValidator>(costFuncOptions),
+                  "Cost functions to use for fitting. Cost functions available "
+                  "are 'Least squares' and 'Ignore positive peaks'",
+                  Direction::InOut);
+
+  declareProperty("MaxIterations", 500, boundedV,
+                  "Stop after this number of iterations if a good fit is not "
+                  "found");
+  declareProperty("PeakRadius", 0,
+                  "A value of the peak radius the peak functions should use. A "
+                  "peak radius defines an interval on the x axis around the "
+                  "centre of the peak where its values are calculated. Values "
+                  "outside the interval are not calculated and assumed zeros."
+                  "Numerically the radius is a whole number of peak widths "
+                  "(FWHM) that fit into the interval on each side from the "
+                  "centre. The default value of 0 means the whole x axis.");
+
+  declareProperty(
+      "ExtractMembers", false,
+      "If true, then each member of the convolution fit will be extracted"
+      ", into their own workspace. These workspaces will have a histogram"
+      " for each spectrum (Q-value) and will be grouped.",
+      Direction::Input);
+
+  declareProperty(
+      make_unique<Kernel::PropertyWithValue<bool>>("ConvolveMembers", false),
+      "If true and ExtractMembers is true members of any "
+      "Convolution are output convolved\n"
+      "with corresponding resolution");
+
+  const std::array<std::string, 2> evaluationTypes = {
+      {"CentrePoint", "Histogram"}};
+  declareProperty(
+      "EvaluationType", "CentrePoint",
+      Kernel::IValidator_sptr(
+          new Kernel::ListValidator<std::string>(evaluationTypes)),
+      "The way the function is evaluated: CentrePoint or Histogram.",
+      Kernel::Direction::Input);
+}
+
+std::map<std::string, std::string> QENSFitSequential::validateInputs() {
+  std::map<std::string, std::string> errors;
+
+  if (getPropertyValue("Input").empty()) {
+    MatrixWorkspace_sptr workspace = getProperty("InputWorkspace");
+    if (!workspace)
+      errors["InputWorkspace"] =
+          "No input string or input workspace was provided.";
+
+    const int specMin = getProperty("SpecMin");
+    const int specMax = getProperty("SpecMax");
+    if (specMin > specMax)
+      errors["SpecMin"] = "SpecMin must be less than or equal to SpecMax.";
+  }
+
+  const double startX = getProperty("StartX");
+  const double endX = getProperty("EndX");
+  if (startX >= endX)
+    errors["StartX"] = "StartX must be less than EndX";
+
+  return errors;
+}
+
+void QENSFitSequential::exec() {
+  const auto outputBaseName = getOutputBaseName();
+
+  if (getPropertyValue("OutputParameterWorkspace").empty())
+    setProperty("OutputParameterWorkspace", outputBaseName + "_Parameters");
+
+  if (getPropertyValue("OutputWorkspaceGroup").empty())
+    setProperty("OutputWorkspaceGroup", outputBaseName + "_Workspaces");
+
+  const auto inputWorkspaces = getWorkspaces();
+  const auto workspaces = convertInputToElasticQ(inputWorkspaces);
+  const auto inputString = getInputString(workspaces);
+  const auto spectra = getSpectra(inputString);
+
+  if (workspaces.empty() || spectra.empty() ||
+      (workspaces.size() > 1 && workspaces.size() != spectra.size()))
+    throw std::invalid_argument("A malformed input string was provided.");
+
+  const auto outputWs = performFit(inputString, outputBaseName);
+  const auto resultWs = processIndirectFitParameters(outputWs);
+  const auto groupWs =
+      AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>(
+          outputBaseName + "_Workspaces");
+  AnalysisDataService::Instance().addOrReplace(
+      getPropertyValue("OutputWorkspace"), resultWs);
+
+  if (containsMultipleData(workspaces))
+    renameWorkspaces(groupWs, spectra, inputWorkspaces);
+  else
+    renameWorkspaces(groupWs, spectra);
+  copyLogs(resultWs, workspaces);
+
+  const bool doExtractMembers = getProperty("ExtractMembers");
+  if (doExtractMembers)
+    extractMembers(groupWs, workspaces, outputBaseName + "_Members");
+
+  deleteTemporaryWorkspaces(outputBaseName);
+
+  addAdditionalLogs(resultWs);
+  copyLogs(resultWs, groupWs);
+
+  setProperty("OutputWorkspace", resultWs);
+  setProperty("OutputParameterWorkspace", outputWs);
+  setProperty("OutputWorkspaceGroup", groupWs);
+}
+
+std::map<std::string, std::string>
+QENSFitSequential::getAdditionalLogStrings() const {
+  const bool convolve = getProperty("ConvolveMembers");
+  auto fitProgram = name();
+  fitProgram = fitProgram.substr(0, fitProgram.rfind("Sequential"));
+
+  auto logs = std::map<std::string, std::string>();
+  logs["sample_filename"] = getPropertyValue("InputWorkspace");
+  logs["convolve_members"] = convolve ? "true" : "false";
+  logs["fit_program"] = fitProgram;
+  logs["fit_mode"] = "Sequential";
+  return logs;
+}
+
+std::map<std::string, std::string>
+QENSFitSequential::getAdditionalLogNumbers() const {
+  std::map<std::string, std::string> logs;
+  logs["start_x"] = getPropertyValue("StartX");
+  logs["end_x"] = getPropertyValue("EndX");
+  return logs;
+}
+
+void QENSFitSequential::addAdditionalLogs(
+    MatrixWorkspace_sptr resultWorkspace) {
+  auto logAdder = createChildAlgorithm("AddSampleLog", -1.0, -1.0, false);
+  logAdder->setProperty("Workspace", resultWorkspace);
+
+  Progress logAdderProg(this, 0.99, 1.00, 6);
+  logAdder->setProperty("LogType", "String");
+  for (const auto &log : getAdditionalLogStrings()) {
+    logAdder->setProperty("LogName", log.first);
+    logAdder->setProperty("LogText", log.second);
+    logAdder->executeAsChildAlg();
+    logAdderProg.report("Add text logs");
+  }
+
+  logAdder->setProperty("LogType", "Number");
+  for (const auto &log : getAdditionalLogNumbers()) {
+    logAdder->setProperty("LogName", log.first);
+    logAdder->setProperty("LogText", log.second);
+    logAdder->executeAsChildAlg();
+    logAdderProg.report("Add number logs");
+  }
+}
+
+std::string QENSFitSequential::getOutputBaseName() const {
+  const auto base = getPropertyValue("OutputWorkspace");
+  const auto position = base.rfind("_Result");
+  if (position != std::string::npos)
+    return base.substr(0, position);
+  return base;
+}
+
+bool QENSFitSequential::throwIfElasticQConversionFails() const { return false; }
+
+bool QENSFitSequential::isFitParameter(const std::string &) const {
+  return true;
+}
+
+std::vector<std::string> QENSFitSequential::getFitParameterNames() const {
+  const auto uniqueParameters = getUniqueParameterNames();
+  std::vector<std::string> parameters;
+  parameters.reserve(uniqueParameters.size());
+  std::copy_if(
+      uniqueParameters.begin(), uniqueParameters.end(),
+      std::back_inserter(parameters),
+      [&](const std::string &parameter) { return isFitParameter(parameter); });
+  return parameters;
+}
+
+std::set<std::string> QENSFitSequential::getUniqueParameterNames() const {
+  IFunction_sptr function = getProperty("Function");
+  std::set<std::string> nameSet;
+  for (auto i = 0u; i < function->nParams(); ++i)
+    nameSet.insert(shortParameterName(function->parameterName(i)));
+  return nameSet;
+}
+
+void QENSFitSequential::deleteTemporaryWorkspaces(
+    const std::string &outputBaseName) {
+  auto deleter = createChildAlgorithm("DeleteWorkspace", -1.0, -1.0, false);
+  deleter->setProperty("Workspace",
+                       outputBaseName + "_NormalisedCovarianceMatrices");
+  deleter->executeAsChildAlg();
+
+  deleter->setProperty("Workspace", outputBaseName + "_Parameters");
+  deleter->executeAsChildAlg();
+
+  deleteTemporaries(deleter, getTemporaryName());
+}
+
+MatrixWorkspace_sptr QENSFitSequential::processIndirectFitParameters(
+    ITableWorkspace_sptr parameterWorkspace) {
+  auto pifp =
+      createChildAlgorithm("ProcessIndirectFitParameters", 0.91, 0.95, true);
+  pifp->setProperty("InputWorkspace", parameterWorkspace);
+  pifp->setProperty("ColumnX", "axis-1");
+  pifp->setProperty("XAxisUnit", "MomentumTransfer");
+  pifp->setProperty("ParameterNames", getFitParameterNames());
+  pifp->setProperty("OutputWorkspace", "__Result");
+  pifp->executeAsChildAlg();
+  return pifp->getProperty("OutputWorkspace");
+}
+
+void QENSFitSequential::renameWorkspaces(
+    WorkspaceGroup_sptr outputGroup, const std::vector<std::string> &spectra,
+    const std::vector<MatrixWorkspace_sptr> &inputWorkspaces) {
+  auto rename = createChildAlgorithm("RenameWorkspace", -1.0, -1.0, false);
+  const auto getNameSuffix = [&](std::size_t i) {
+    return inputWorkspaces[i]->getName() + "_" + spectra[i] + "_Workspace";
+  };
+  return renameWorkspacesInQENSFit(this, rename, outputGroup, getNameSuffix);
+}
+
+void QENSFitSequential::renameWorkspaces(
+    WorkspaceGroup_sptr outputGroup, const std::vector<std::string> &spectra) {
+  auto rename = createChildAlgorithm("RenameWorkspace", -1.0, -1.0, false);
+  auto getNameSuffix = [&](std::size_t i) { return spectra[i] + "_Workspace"; };
+  return renameWorkspacesInQENSFit(this, rename, outputGroup, getNameSuffix);
+}
+
+ITableWorkspace_sptr QENSFitSequential::performFit(const std::string &input,
+                                                   const std::string &output) {
+  const bool convolveMembers = getProperty("ConvolveMembers");
+  const bool passWsIndex = getProperty("PassWSIndexToFunction");
+
+  // Run PlotPeaksByLogValue
+  auto plotPeaks = createChildAlgorithm("PlotPeakByLogValue", 0.05, 0.90, true);
+  plotPeaks->setProperty("Input", input);
+  plotPeaks->setProperty("OutputWorkspace", output);
+  plotPeaks->setPropertyValue("Function", getPropertyValue("Function"));
+  plotPeaks->setProperty("StartX", getPropertyValue("StartX"));
+  plotPeaks->setProperty("EndX", getPropertyValue("EndX"));
+  plotPeaks->setProperty("FitType", "Sequential");
+  plotPeaks->setProperty("CreateOutput", true);
+  plotPeaks->setProperty("OutputCompositeMembers", true);
+  plotPeaks->setProperty("ConvolveMembers", convolveMembers);
+  plotPeaks->setProperty("MaxIterations", getPropertyValue("MaxIterations"));
+  plotPeaks->setProperty("Minimizer", getPropertyValue("Minimizer"));
+  plotPeaks->setProperty("PassWSIndexToFunction", passWsIndex);
+  plotPeaks->setProperty("PeakRadius", getPropertyValue("PeakRadius"));
+  plotPeaks->setProperty("LogValue", getPropertyValue("LogValue"));
+  plotPeaks->setProperty("EvaluationType", getPropertyValue("EvaluationType"));
+  plotPeaks->setProperty("CostFunction", getPropertyValue("CostFunction"));
+  plotPeaks->executeAsChildAlg();
+  return plotPeaks->getProperty("OutputWorkspace");
+}
+
+std::string QENSFitSequential::getInputString(
+    const std::vector<MatrixWorkspace_sptr> &workspaces) const {
+  const auto inputString = getPropertyValue("Input");
+  if (!inputString.empty())
+    return replaceWorkspaces(inputString, workspaces);
+  return constructInputString(workspaces[0], getProperty("SpecMin"),
+                              getProperty("SpecMax"));
+}
+
+std::vector<MatrixWorkspace_sptr> QENSFitSequential::getWorkspaces() const {
+  const auto inputString = getPropertyValue("Input");
+  if (!inputString.empty())
+    return extractWorkspaces(inputString);
+  return {getProperty("InputWorkspace")};
+}
+
+std::vector<MatrixWorkspace_sptr> QENSFitSequential::convertInputToElasticQ(
+    const std::vector<MatrixWorkspace_sptr> &workspaces) const {
+  return convertToElasticQ(workspaces, getTemporaryName(),
+                           throwIfElasticQConversionFails());
+}
+
+void QENSFitSequential::extractMembers(
+    WorkspaceGroup_sptr resultGroupWs,
+    const std::vector<API::MatrixWorkspace_sptr> &workspaces,
+    const std::string &outputWsName) {
+  std::vector<std::string> workspaceNames;
+  std::transform(
+      workspaces.begin(), workspaces.end(), std::back_inserter(workspaceNames),
+      [](API::MatrixWorkspace_sptr workspace) { return workspace->getName(); });
+
+  auto extractAlgorithm = extractMembersAlgorithm(resultGroupWs, outputWsName);
+  extractAlgorithm->setProperty("InputWorkspaces", workspaceNames);
+  extractAlgorithm->execute();
+}
+
+void QENSFitSequential::copyLogs(
+    MatrixWorkspace_sptr resultWorkspace,
+    const std::vector<MatrixWorkspace_sptr> &workspaces) {
+  auto logCopier = createChildAlgorithm("CopyLogs", -1.0, -1.0, false);
+  logCopier->setProperty("OutputWorkspace", resultWorkspace->getName());
+
+  for (const auto &workspace : workspaces) {
+    logCopier->setProperty("InputWorkspace", workspace);
+    logCopier->executeAsChildAlg();
+  }
+}
+
+void QENSFitSequential::copyLogs(MatrixWorkspace_sptr resultWorkspace,
+                                 WorkspaceGroup_sptr resultGroup) {
+  auto logCopier = createChildAlgorithm("CopyLogs", -1.0, -1.0, false);
+  logCopier->setProperty("InputWorkspace", resultWorkspace);
+  logCopier->setProperty("OutputWorkspace", resultGroup->getName());
+  logCopier->executeAsChildAlg();
+}
+
+IAlgorithm_sptr QENSFitSequential::extractMembersAlgorithm(
+    WorkspaceGroup_sptr resultGroupWs, const std::string &outputWsName) const {
+  const bool convolved = getProperty("ConvolveMembers");
+  std::vector<std::string> convolvedMembers;
+  IFunction_sptr function = getProperty("Function");
+
+  if (convolved)
+    extractConvolvedNames(function, convolvedMembers);
+
+  auto extractMembersAlg =
+      AlgorithmManager::Instance().create("ExtractQENSMembers");
+  extractMembersAlg->setProperty("ResultWorkspace", resultGroupWs);
+  extractMembersAlg->setProperty("OutputWorkspace", outputWsName);
+  extractMembersAlg->setProperty("RenameConvolvedMembers", convolved);
+  extractMembersAlg->setProperty("ConvolvedMembers", convolvedMembers);
+  return extractMembersAlg;
+}
+
+std::string QENSFitSequential::getTemporaryName() const {
+  return "__" + name() + "_ws";
+}
+
+} // namespace Algorithms
+} // namespace Mantid
diff --git a/Framework/WorkflowAlgorithms/test/ConvolutionFitSequentialTest.h b/Framework/WorkflowAlgorithms/test/ConvolutionFitSequentialTest.h
index 298a5a43e9a8be7c12bc27d80985de7e9731e8a0..137749cde25ac70b80c94a03a318b0b89fa0933a 100644
--- a/Framework/WorkflowAlgorithms/test/ConvolutionFitSequentialTest.h
+++ b/Framework/WorkflowAlgorithms/test/ConvolutionFitSequentialTest.h
@@ -42,8 +42,11 @@ public:
   void test_fit_function_is_valid_for_convolution_fitting() {
     Mantid::Algorithms::ConvolutionFitSequential alg;
     TS_ASSERT_THROWS_NOTHING(alg.initialize());
+    createConvFitResWorkspace(1, 1);
     TS_ASSERT_THROWS_NOTHING(alg.setProperty(
-        "Function", "function=test,name=Convolution,name=Resolution"));
+        "Function", "name=Convolution;name=Resolution,Workspace=__ConvFit_"
+                    "Resolution,WorkspaceIndex=0;"));
+    AnalysisDataService::Instance().clear();
   }
 
   //-------------------------- Failure cases ----------------------------
@@ -143,15 +146,15 @@ public:
                     "WorkspaceIndex=0;((composite=ProductFunction,NumDeriv="
                     "false;name=Lorentzian,Amplitude=1,PeakCentre=0,FWHM=0."
                     "0175)))");
-    alg.setProperty("BackgroundType", "Fixed Flat");
     alg.setProperty("StartX", 0.0);
     alg.setProperty("EndX", 3.0);
     alg.setProperty("SpecMin", 0);
     alg.setProperty("SpecMax", 5);
-    alg.setProperty("Convolve", true);
+    alg.setProperty("ConvolveMembers", true);
     alg.setProperty("Minimizer", "Levenberg-Marquardt");
     alg.setProperty("MaxIterations", 500);
-    alg.setProperty("OutputWorkspace", "Result");
+    alg.setProperty("OutputWorkspace",
+                    "ReductionWs_conv_1LFixF_s0_to_5_Result");
     TS_ASSERT_THROWS_NOTHING(alg.execute());
     TS_ASSERT(alg.isExecuted());
 
@@ -197,12 +200,15 @@ public:
     // Check new Log data is present
     auto memberLogs = memberRun.getLogData();
 
-    TS_ASSERT_EQUALS(memberLogs.at(2)->value(), "FixF");
-    TS_ASSERT_EQUALS(memberLogs.at(3)->value(), "true");
-    TS_ASSERT_EQUALS(memberLogs.at(4)->value(), "false");
-    TS_ASSERT_EQUALS(memberLogs.at(5)->value(), "ConvFit");
-    TS_ASSERT_EQUALS(memberLogs.at(6)->value(), "ReductionWs_");
-    TS_ASSERT_EQUALS(memberLogs.at(7)->value(), "1");
+    TS_ASSERT_EQUALS(memberRun.getLogData("background")->value(),
+                     "Fixed Linear");
+    TS_ASSERT_EQUALS(memberRun.getLogData("convolve_members")->value(), "true");
+    TS_ASSERT_EQUALS(memberRun.getLogData("delta_function")->value(), "false");
+    TS_ASSERT_EQUALS(memberRun.getLogData("fit_program")->value(),
+                     "ConvolutionFit");
+    TS_ASSERT_EQUALS(memberRun.getLogData("sample_filename")->value(),
+                     "ReductionWs_");
+    TS_ASSERT_EQUALS(memberRun.getLogData("lorentzians")->value(), "1");
 
     AnalysisDataService::Instance().clear();
   }
@@ -221,15 +227,14 @@ public:
                     "WorkspaceIndex=0;((composite=ProductFunction,NumDeriv="
                     "false;name=Lorentzian,Amplitude=1,PeakCentre=0,FWHM=0."
                     "0175)))");
-    alg.setProperty("BackgroundType", "Fixed Flat");
     alg.setProperty("StartX", 0.0);
     alg.setProperty("EndX", 5.0);
     alg.setProperty("SpecMin", 0);
     alg.setProperty("SpecMax", 0);
-    alg.setProperty("Convolve", true);
+    alg.setProperty("ConvolveMembers", true);
     alg.setProperty("Minimizer", "Levenberg-Marquardt");
     alg.setProperty("MaxIterations", 500);
-    alg.setProperty("OutputWorkspace", "Result");
+    alg.setProperty("OutputWorkspace", "SqwWs_conv_1LFixF_s0_Result");
     TS_ASSERT_THROWS_NOTHING(alg.execute());
     TS_ASSERT(alg.isExecuted());
 
@@ -263,6 +268,9 @@ public:
     size_t specMin = 0;
     size_t specMax = 5;
 
+    auto outputName = runName + "_conv_1LFixF_s" + std::to_string(specMin) +
+                      "_to_" + std::to_string(specMax) + "_Result";
+
     Mantid::Algorithms::ConvolutionFitSequential alg;
     TS_ASSERT_THROWS_NOTHING(alg.initialize());
     alg.setProperty("InputWorkspace", redWs);
@@ -273,16 +281,15 @@ public:
                     "WorkspaceIndex=0;((composite=ProductFunction,NumDeriv="
                     "false;name=Lorentzian,Amplitude=1,PeakCentre=0,FWHM=0."
                     "0175)))");
-    alg.setProperty("BackgroundType", "Fixed Flat");
     alg.setProperty("StartX", 0.0);
     alg.setProperty("EndX", 3.0);
     alg.setProperty("SpecMin", boost::numeric_cast<int>(specMin));
     alg.setProperty("SpecMax", boost::numeric_cast<int>(specMax));
-    alg.setProperty("Convolve", true);
+    alg.setProperty("ConvolveMembers", true);
     alg.setProperty("ExtractMembers", true);
     alg.setProperty("Minimizer", "Levenberg-Marquardt");
     alg.setProperty("MaxIterations", 500);
-    alg.setProperty("OutputWorkspace", "Result");
+    alg.setProperty("OutputWorkspace", outputName);
     TS_ASSERT_THROWS_NOTHING(alg.execute());
     TS_ASSERT(alg.isExecuted());
 
diff --git a/Framework/WorkflowAlgorithms/test/QENSFitSequentialTest.h b/Framework/WorkflowAlgorithms/test/QENSFitSequentialTest.h
new file mode 100644
index 0000000000000000000000000000000000000000..199d0bdc0692c8e7a0cca5cb332e75d4359f59ca
--- /dev/null
+++ b/Framework/WorkflowAlgorithms/test/QENSFitSequentialTest.h
@@ -0,0 +1,225 @@
+#ifndef MANTID_ALGORITHMS_QENSFITSEQUENTIALTEST_H_
+#define MANTID_ALGORITHMS_QENSFITSEQUENTIALTEST_H_
+
+#include <cxxtest/TestSuite.h>
+
+#include "MantidAPI/Axis.h"
+#include "MantidAPI/FrameworkManager.h"
+#include "MantidAPI/WorkspaceFactory.h"
+#include "MantidAPI/WorkspaceGroup.h"
+
+#include "MantidDataHandling/Load.h"
+
+#include "MantidWorkflowAlgorithms/QENSFitSequential.h"
+
+#include "MantidDataObjects/Workspace2D.h"
+#include "MantidKernel/TimeSeriesProperty.h"
+#include "MantidTestHelpers/WorkspaceCreationHelper.h"
+
+using Mantid::Algorithms::QENSFitSequential;
+using namespace Mantid::API;
+using namespace Mantid::DataObjects;
+using Mantid::Kernel::make_cow;
+using Mantid::HistogramData::BinEdges;
+using Mantid::HistogramData::Counts;
+using Mantid::HistogramData::CountStandardDeviations;
+
+class QENSFitSequentialTest : 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 QENSFitSequentialTest *createSuite() {
+    return new QENSFitSequentialTest();
+  }
+  static void destroySuite(QENSFitSequentialTest *suite) { delete suite; }
+
+  QENSFitSequentialTest() { FrameworkManager::Instance(); }
+
+  void test_set_valid_fit_function() {
+    Mantid::Algorithms::QENSFitSequential alg;
+    TS_ASSERT_THROWS_NOTHING(alg.initialize());
+    TS_ASSERT_THROWS_NOTHING(alg.setProperty(
+        "Function", "name=DeltaFunction,Height=1,Centre=0;name="
+                    "Lorentzian,Amplitude=1,PeakCentre=0,FWHM=0;"));
+  }
+
+  void test_empty_function_is_not_allowed() {
+    Mantid::Algorithms::QENSFitSequential alg;
+    TS_ASSERT_THROWS_NOTHING(alg.initialize());
+
+    TS_ASSERT_THROWS(alg.setPropertyValue("Function", ""),
+                     std::invalid_argument);
+  }
+
+  void test_convolution_fit() {
+    const int totalBins = 6;
+    const int totalHist = 5;
+    auto inputWorkspace = createReducedWorkspace(totalBins, totalHist);
+    auto resolution =
+        createResolutionWorkspace(totalBins, totalHist, "__QENS_Resolution");
+
+    auto outputBaseName = runConvolutionFit(inputWorkspace, resolution);
+    testFitOutput(outputBaseName, inputWorkspace->getNumberHistograms());
+    AnalysisDataService::Instance().clear();
+  }
+
+  void test_multiple_fit() {
+    const int totalBins = 15;
+    const int totalHist = 10;
+
+    std::vector<std::string> names = {"first_red", "second_red"};
+    auto outputBaseName = runMultipleFit(
+        createReducedWorkspaces(names, totalBins, totalHist), peakFunction());
+    testFitOutput(outputBaseName, names.size() * 3);
+    AnalysisDataService::Instance().clear();
+  }
+
+private:
+  std::string runConvolutionFit(MatrixWorkspace_sptr inputWorkspace,
+                                MatrixWorkspace_sptr resolution) {
+    Mantid::Algorithms::QENSFitSequential alg;
+    TS_ASSERT_THROWS_NOTHING(alg.initialize());
+
+    alg.setProperty("InputWorkspace", inputWorkspace);
+    alg.setProperty("Function", convolutionFunction(resolution->getName()));
+    alg.setProperty("StartX", 0.0);
+    alg.setProperty("EndX", 3.0);
+    alg.setProperty("SpecMin", 0);
+    alg.setProperty(
+        "SpecMax", static_cast<int>(inputWorkspace->getNumberHistograms() - 1));
+    alg.setProperty("ConvolveMembers", true);
+    alg.setProperty("Minimizer", "Levenberg-Marquardt");
+    alg.setProperty("MaxIterations", 500);
+    alg.setProperty("OutputWorkspace",
+                    "ReductionWs_conv_1LFixF_s0_to_5_Result");
+    TS_ASSERT_THROWS_NOTHING(alg.execute());
+    TS_ASSERT(alg.isExecuted());
+
+    return "ReductionWs_conv_1LFixF_s0_to_5";
+  }
+
+  std::string runMultipleFit(
+      const std::vector<Mantid::API::MatrixWorkspace_sptr> &workspaces,
+      const std::string &function) {
+    Mantid::Algorithms::QENSFitSequential alg;
+    TS_ASSERT_THROWS_NOTHING(alg.initialize());
+
+    alg.setProperty("Input", createMultipleFitInput(workspaces));
+    alg.setProperty("Function", function);
+    alg.setProperty("StartX", 0.0);
+    alg.setProperty("EndX", 10.0);
+    alg.setProperty("ConvolveMembers", true);
+    alg.setProperty("Minimizer", "Levenberg-Marquardt");
+    alg.setProperty("MaxIterations", 500);
+    alg.setProperty("OutputWorkspace", "MultiQENSFitSequential_Result");
+    TS_ASSERT_THROWS_NOTHING(alg.execute());
+    TS_ASSERT(alg.isExecuted());
+
+    return "MultiQENSFitSequential";
+  }
+
+  void testFitOutput(const std::string &outputBaseName,
+                     std::size_t expectedGroupSize) {
+    WorkspaceGroup_sptr groupWorkspace;
+
+    TS_ASSERT_THROWS_NOTHING(
+        AnalysisDataService::Instance().retrieveWS<ITableWorkspace>(
+            outputBaseName + "_Parameters"));
+    TS_ASSERT_THROWS_NOTHING(
+        groupWorkspace =
+            AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>(
+                outputBaseName + "_Workspaces"));
+    TS_ASSERT_THROWS_NOTHING(
+        AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(
+            outputBaseName + "_Result"));
+
+    TS_ASSERT_EQUALS(groupWorkspace->size(), expectedGroupSize);
+  }
+
+  std::vector<Mantid::API::MatrixWorkspace_sptr>
+  createReducedWorkspaces(const std::vector<std::string> &names, int totalBins,
+                          int totalHist) {
+    std::vector<Mantid::API::MatrixWorkspace_sptr> workspaces;
+
+    for (const auto &name : names) {
+      workspaces.emplace_back(createReducedWorkspace(totalBins, totalHist));
+      AnalysisDataService::Instance().addOrReplace(name, workspaces.back());
+    }
+    return workspaces;
+  }
+
+  std::string createMultipleFitInput(
+      const std::vector<Mantid::API::MatrixWorkspace_sptr> &workspaces) const {
+    std::ostringstream input;
+
+    for (const auto &workspace : workspaces)
+      input << workspace->getName() << ",i0;" << workspace->getName() << ",i"
+            << std::to_string(workspace->getNumberHistograms() / 2) << ";"
+            << workspace->getName() << ",i"
+            << std::to_string(workspace->getNumberHistograms() - 1) << ";";
+    return input.str();
+  }
+
+  std::string peakFunction() const {
+    return "name=LinearBackground,A0=0,A1=0;name=Lorentzian,Amplitude=1,"
+           "PeakCentre=0,FWHM=0.0175";
+  }
+
+  std::string convolutionFunction(const std::string &resolutionName) const {
+    return "name=LinearBackground,A0=0,A1=0,ties=(A0=0.000000,A1=0.0);("
+           "composite=Convolution,FixResolution=true,NumDeriv=true;name="
+           "Resolution,Workspace=" +
+           resolutionName +
+           ",WorkspaceIndex=0;((composite=ProductFunction,NumDeriv=false;name="
+           "Lorentzian,Amplitude=1,PeakCentre=0,FWHM=0.0175)))";
+  }
+
+  MatrixWorkspace_sptr createReducedWorkspace(int xlen, int ylen) const {
+    auto ws = WorkspaceCreationHelper::create2DWorkspaceWithFullInstrument(
+        xlen, xlen - 1, false, false, true, "testInst");
+    ws->initialize(ylen, xlen, xlen - 1);
+    addBinsAndCountsToWorkspace(ws, xlen, xlen - 1, 1.0, 3.0);
+
+    ws->getAxis(0)->setUnit("DeltaE");
+
+    for (int i = 0; i < xlen; ++i)
+      ws->setEFixed((i + 1), 0.50);
+
+    auto &run = ws->mutableRun();
+    auto timeSeries =
+        new Mantid::Kernel::TimeSeriesProperty<std::string>("TestTimeSeries");
+    timeSeries->addValue("2010-09-14T04:20:12", "0.02");
+    run.addProperty(timeSeries);
+    return ws;
+  }
+
+  MatrixWorkspace_sptr
+  createResolutionWorkspace(std::size_t totalBins, std::size_t totalHist,
+                            const std::string &name) const {
+    auto resolution =
+        createWorkspace<Workspace2D>(totalHist + 1, totalBins + 1, totalBins);
+    addBinsAndCountsToWorkspace(resolution, totalBins + 1, totalBins, 0.0, 3.0);
+    AnalysisDataService::Instance().addOrReplace(name, resolution);
+    return resolution;
+  }
+
+  void addBinsAndCountsToWorkspace(Workspace2D_sptr workspace,
+                                   std::size_t totalBinEdges,
+                                   std::size_t totalCounts, double binValue,
+                                   double countValue) const {
+    BinEdges x1(totalBinEdges, binValue);
+    Counts y1(totalCounts, countValue);
+    CountStandardDeviations e1(totalCounts, sqrt(countValue));
+
+    int j = 0;
+    std::generate(begin(x1), end(x1), [&j] { return 0.5 + 0.75 * j++; });
+
+    for (auto i = 0u; i < workspace->getNumberHistograms(); ++i) {
+      workspace->setBinEdges(i, x1);
+      workspace->setCounts(i, y1);
+      workspace->setCountStandardDeviations(i, e1);
+    }
+  }
+};
+
+#endif /* MANTID_ALGORITHMS_CONVOLUTIONFITSEQUENTIALTEST_H_ */
diff --git a/Testing/SystemTests/tests/analysis/ISISIndirectInelastic.py b/Testing/SystemTests/tests/analysis/ISISIndirectInelastic.py
index 2a68c74d136de8814fe166837ca75ba69ad5a336..c41ea24915483f42f1ac79f506047774faf1d579 100644
--- a/Testing/SystemTests/tests/analysis/ISISIndirectInelastic.py
+++ b/Testing/SystemTests/tests/analysis/ISISIndirectInelastic.py
@@ -750,7 +750,7 @@ class ISISIndirectInelasticIqtAndIqtFit(with_metaclass(ABCMeta, ISISIndirectInel
         # Load files into Mantid
         for sample in self.samples:
             LoadNexus(sample, OutputWorkspace=sample)
-        LoadNexus(self.resolution, OutputWorkspace=self.resolution)
+        LoadNexus(FileFinder.getFullPath(self.resolution), OutputWorkspace=self.resolution)
 
         _, iqt_ws = TransformToIqt(SampleWorkspace=self.samples[0],
                                    ResolutionWorkspace=self.resolution,
@@ -760,12 +760,9 @@ class ISISIndirectInelasticIqtAndIqtFit(with_metaclass(ABCMeta, ISISIndirectInel
                                    DryRun=False)
 
         # Test IqtFit Sequential
-        iqtfitSeq_ws, params, fit_group = IqtFitSequential(iqt_ws,
-                                                           self.func,
-                                                           self.ftype,
-                                                           self.startx,
-                                                           self.endx, 0,
-                                                           self.spec_max)
+        iqtfitSeq_ws, params, fit_group = IqtFitSequential(InputWorkspace=iqt_ws, Function=self.func,
+                                                           StartX=self.startx, EndX=self.endx,
+                                                           SpecMin=0, SpecMax=self.spec_max)
 
         self.result_names = [iqt_ws.name(),
                              iqtfitSeq_ws.name()]
@@ -792,8 +789,6 @@ class ISISIndirectInelasticIqtAndIqtFit(with_metaclass(ABCMeta, ISISIndirectInel
             raise RuntimeError("num_bins should be an int")
         if not isinstance(self.func, str):
             raise RuntimeError("Function should be a string.")
-        if not isinstance(self.ftype, str):
-            raise RuntimeError("Function type should be a string.")
         if not isinstance(self.startx, float):
             raise RuntimeError("startx should be a float")
         if not isinstance(self.endx, float):
@@ -818,7 +813,6 @@ class OSIRISIqtAndIqtFit(ISISIndirectInelasticIqtAndIqtFit):
         self.func = r'composite=CompositeFunction,NumDeriv=1;name=LinearBackground,A0=0,A1=0,ties=(A1=0);'\
                     'name=UserFunction,Formula=Intensity*exp(-(x/Tau)),'\
                     'Intensity=0.304185,Tau=100;ties=(f1.Intensity=1-f0.A0)'
-        self.ftype = '1E_s'
         self.spec_max = 41
         self.startx = 0.0
         self.endx = 0.118877
@@ -847,7 +841,6 @@ class IRISIqtAndIqtFit(ISISIndirectInelasticIqtAndIqtFit):
         self.func = r'composite=CompositeFunction,NumDeriv=1;name=LinearBackground,A0=0,A1=0,ties=(A1=0);'\
                     'name=UserFunction,Formula=Intensity*exp(-(x/Tau)),'\
                     'Intensity=0.355286,Tau=100;ties=(f1.Intensity=1-f0.A0)'
-        self.ftype = '1E_s'
         self.spec_max = 50
         self.startx = 0.0
         self.endx = 0.169171
@@ -875,7 +868,7 @@ class ISISIndirectInelasticIqtAndIqtFitMulti(with_metaclass(ABCMeta, ISISIndirec
         #load files into mantid
         for sample in self.samples:
             LoadNexus(sample, OutputWorkspace=sample)
-        LoadNexus(self.resolution, OutputWorkspace=self.resolution)
+        LoadNexus(FileFinder.getFullPath(self.resolution), OutputWorkspace=self.resolution)
 
         _, iqt_ws = TransformToIqt(SampleWorkspace=self.samples[0],
                                    ResolutionWorkspace=self.resolution,
@@ -999,7 +992,7 @@ class ISISIndirectInelasticConvFit(with_metaclass(ABCMeta, ISISIndirectInelastic
         '''Defines the workflow for the test'''
         self.tolerance = 1e-4
         LoadNexus(self.sample, OutputWorkspace=self.sample)
-        LoadNexus(self.resolution, OutputWorkspace=self.resolution)
+        LoadNexus(FileFinder.getFullPath(self.resolution), OutputWorkspace=self.resolution)
 
         ConvolutionFitSequential(
             InputWorkspace=self.sample,
@@ -1007,11 +1000,10 @@ class ISISIndirectInelasticConvFit(with_metaclass(ABCMeta, ISISIndirectInelastic
             PassWSIndexToFunction=self.passWSIndexToFunction,
             StartX=self.startx,
             EndX=self.endx,
-            BackgroundType=self.bg,
             SpecMin=self.spectra_min,
             SpecMax=self.spectra_max,
             PeakRadius=5,
-            OutputWorkspace='result')
+            OutputWorkspace=self.result_names[0])
 
     def _validate_properties(self):
         '''Check the object properties are in an expected state to continue'''
@@ -1022,8 +1014,6 @@ class ISISIndirectInelasticConvFit(with_metaclass(ABCMeta, ISISIndirectInelastic
             raise RuntimeError("Resolution should be a string.")
         if not isinstance(self.func, str):
             raise RuntimeError("Function should be a string.")
-        if not isinstance(self.bg, str):
-            raise RuntimeError("Background type should be a string.")
         if not isinstance(self.startx, float):
             raise RuntimeError("startx should be a float")
         if not isinstance(self.endx, float):
@@ -1043,7 +1033,7 @@ class OSIRISConvFit(ISISIndirectInelasticConvFit):
     def __init__(self):
         ISISIndirectInelasticConvFit.__init__(self)
         self.sample = 'osi97935_graphite002_red.nxs'
-        self.resolution = FileFinder.getFullPath('osi97935_graphite002_res.nxs')
+        self.resolution = 'osi97935_graphite002_res.nxs'
         #ConvFit fit function
         self.func = 'name=LinearBackground,A0=0,A1=0;(composite=Convolution,FixResolution=true,NumDeriv=true;'\
                     'name=Resolution,Workspace=\"%s\";name=Lorentzian,Amplitude=2,FWHM=0.002,ties=(PeakCentre=0)'\
@@ -1051,7 +1041,6 @@ class OSIRISConvFit(ISISIndirectInelasticConvFit):
         self.passWSIndexToFunction = False  # osi97935_graphite002_res is single histogram
         self.startx = -0.2
         self.endx = 0.2
-        self.bg = 'Fit Linear'
         self.spectra_min = 0
         self.spectra_max = 41
         self.ties = False
@@ -1070,7 +1059,7 @@ class IRISConvFit(ISISIndirectInelasticConvFit):
     def __init__(self):
         ISISIndirectInelasticConvFit.__init__(self)
         self.sample = 'irs53664_graphite002_red.nxs'
-        self.resolution = FileFinder.getFullPath('irs53664_graphite002_res.nxs')
+        self.resolution = 'irs53664_graphite002_res.nxs'
         #ConvFit fit function
         self.func = 'name=LinearBackground,A0=0.060623,A1=0.001343;' \
                     '(composite=Convolution,FixResolution=true,NumDeriv=true;' \
@@ -1079,7 +1068,6 @@ class IRISConvFit(ISISIndirectInelasticConvFit):
         self.passWSIndexToFunction = False  # irs53664_graphite002_res is single histogram
         self.startx = -0.2
         self.endx = 0.2
-        self.bg = 'Fit Linear'
         self.spectra_min = 0
         self.spectra_max = 50
         self.ties = False
diff --git a/docs/source/algorithms/ConvolutionFitSequential-v1.rst b/docs/source/algorithms/ConvolutionFitSequential-v1.rst
index 92b2d66d6e97f37b7479596211d8c4c2410edfc0..66d6565736f300a5bd2ec511abdd9bd630449a59 100644
--- a/docs/source/algorithms/ConvolutionFitSequential-v1.rst
+++ b/docs/source/algorithms/ConvolutionFitSequential-v1.rst
@@ -10,14 +10,14 @@
 Description
 -----------
 
-An algorithm designed mainly as a sequential call to PlotPeakByLogValue 
-but used within the ConvFit tab within the Indirect Analysis interface 
-to fit Convolution Functions.
+Performs a sequential fit involving a convolution with a defined resolution. This algorithm is a special-case of
+:ref:`QENSFitSequential <algm-QENSFitSequential>`, which calculates the elastic incoherent scattering factor when
+a delta function is provided in the fitting model.
 
 Workflow
 --------
 
-.. diagram:: ConvolutionFitSequential-v1_wkflw.dot
+.. diagram:: QENSFitSequential-v1_wkflw.dot
 
 
 Usage
@@ -38,7 +38,6 @@ Usage
   (composite=Convolution,FixResolution=true,NumDeriv=true;
   name=Resolution,Workspace=resolution,WorkspaceIndex=0;
   name=Lorentzian,Amplitude=1,PeakCentre=0,FWHM=0.0175)"""
-  bgType = "Fixed Flat"
   startX = -0.547608
   endX = 0.543217
   specMin = 0
@@ -48,10 +47,13 @@ Usage
   maxIt = 500
 
   # Run algorithm
-  result_ws = ConvolutionFitSequential(InputWorkspace=sample,
-      Function=function, PassWSIndexToFunction=True, BackgroundType=bgType,
-      StartX=startX, EndX=endX, SpecMin=specMin, SpecMax=specMax,
-      Convolve=convolve, Minimizer=minimizer, MaxIterations=maxIt)
+  result_ws, _, _ = ConvolutionFitSequential(InputWorkspace=sample,
+                                             Function=function,
+                                             PassWSIndexToFunction=True,
+                                             StartX=startX, EndX=endX,
+                                             SpecMin=specMin, SpecMax=specMax,
+                                             ConvolveMembers=convolve,
+                                             Minimizer=minimizer, MaxIterations=maxIt)
   
   print("Result has %i Spectra" %result_ws.getNumberHistograms())
   
diff --git a/docs/source/algorithms/ExtractQENSMembers-v1.rst b/docs/source/algorithms/ExtractQENSMembers-v1.rst
index 8ea5b640408287fb97c886c03ab21660a17d0bd2..e72c473e4b1e5ccdc2cc5dfc4071e95806f07d2c 100644
--- a/docs/source/algorithms/ExtractQENSMembers-v1.rst
+++ b/docs/source/algorithms/ExtractQENSMembers-v1.rst
@@ -29,7 +29,6 @@ Usage
   (composite=Convolution,FixResolution=true,NumDeriv=true;
   name=Resolution,Workspace=resolution,WorkspaceIndex=0;
   name=Lorentzian,Amplitude=1,PeakCentre=0,FWHM=0.0175)"""
-  bgType = "Fixed Flat"
   startX = -0.547608
   endX = 0.543217
   specMin = 0
@@ -40,11 +39,11 @@ Usage
   output_ws_name = "irs26176_graphite002_conv_1LFixF_s0_to_9"
 
   # Run ConvolutionFitSequential algorithm
-  result_ws = ConvolutionFitSequential(InputWorkspace=sample, Function=function,
-                                       PassWSIndexToFunction=True, BackgroundType=bgType,
-                                       StartX=startX, EndX=endX, SpecMin=specMin, SpecMax=specMax,
-                                       Convolve=convolve, Minimizer=minimizer, MaxIterations=maxIt,
-                                       OutputWorkspace=output_ws_name)
+  ConvolutionFitSequential(InputWorkspace=sample, Function=function,
+                           PassWSIndexToFunction=True, StartX=startX, EndX=endX,
+                           SpecMin=specMin, SpecMax=specMax, ConvolveMembers=convolve,
+                           Minimizer=minimizer, MaxIterations=maxIt,
+                           OutputWorkspace=output_ws_name)
 
   # Extract members from the output of the ConvolutionFitSequential algorithm
   members_ws = ExtractQENSMembers(InputWorkspace=sample, ResultWorkspace=output_ws_name+"_Workspaces",
@@ -59,7 +58,7 @@ Usage
   DeleteWorkspace(output_ws_name + "_Workspaces")
   DeleteWorkspace(output_ws_name + "_Parameters")
   DeleteWorkspace(output_ws_name + "_Members")
-  DeleteWorkspace(result_ws)
+  DeleteWorkspace(output_ws_name)
   DeleteWorkspace(sample)
   DeleteWorkspace(resolution)
 
@@ -86,7 +85,6 @@ Output:
   (composite=Convolution,FixResolution=true,NumDeriv=true;
   name=Resolution,Workspace=resolution,WorkspaceIndex=0;
   name=Lorentzian,Amplitude=1,PeakCentre=0,FWHM=0.0175)"""
-  bgType = "Fixed Flat"
   startX = -0.547608
   endX = 0.543217
   specMin = 0
@@ -97,11 +95,10 @@ Output:
   output_ws_name = "irs26176_graphite002_conv_1LFixF_s0_to_9"
 
   # Run ConvolutionFitSequential algorithm with ExtractMembers property
-  result_ws = ConvolutionFitSequential(InputWorkspace=sample, Function=function,
-                                       PassWSIndexToFunction=True, BackgroundType=bgType,
-                                       StartX=startX, EndX=endX, SpecMin=specMin, SpecMax=specMax,
-                                       Convolve=convolve, Minimizer=minimizer, MaxIterations=maxIt,
-                                       ExtractMembers=True, OutputWorkspace=output_ws_name)
+  ConvolutionFitSequential(InputWorkspace=sample, Function=function, PassWSIndexToFunction=True,
+                           StartX=startX, EndX=endX, SpecMin=specMin, SpecMax=specMax,
+                           ConvolveMembers=convolve, Minimizer=minimizer, MaxIterations=maxIt,
+                           ExtractMembers=True, OutputWorkspace=output_ws_name)
 
   members_ws = mtd[output_ws_name + "_Members"]
 
@@ -113,7 +110,7 @@ Output:
   DeleteWorkspace(output_ws_name + "_Workspaces")
   DeleteWorkspace(output_ws_name + "_Parameters")
   DeleteWorkspace(output_ws_name + "_Members")
-  DeleteWorkspace(result_ws)
+  DeleteWorkspace(output_ws_name)
   DeleteWorkspace(sample)
   DeleteWorkspace(resolution)
 
diff --git a/docs/source/algorithms/IqtFitSequential-v1.rst b/docs/source/algorithms/IqtFitSequential-v1.rst
index 6c62230170af024a48ab16f8f35e80b56d6f88c6..89853eb7c76ac8d3cda0bdd35e0cec8b9b8dd123 100644
--- a/docs/source/algorithms/IqtFitSequential-v1.rst
+++ b/docs/source/algorithms/IqtFitSequential-v1.rst
@@ -9,13 +9,13 @@
 Description
 -----------
 
-Fits an \*\_iqt file generated by I(Q,t) sequentially.
+Fits an \*\_iqt file generated by I(Q,t) sequentially. This algorithm is a special-case of
+:ref:`QENSFitSequential <algm-QENSFitSequential>` in which all input is converted into a histogram format.
 
 Workflow
 --------
 
-.. diagram:: IqtFitSequential-v1_wkflw.dot
-
+.. diagram:: QENSFitSequential-v1_wkflw.dot
 
 Usage
 -----
@@ -29,7 +29,7 @@ Usage
     function = r'name=LinearBackground,A0=0.027668,A1=0,ties=(A1=0);name=UserFunction,Formula=Intensity*exp(-(x/Tau)^Beta),Intensity=0.972332,Tau=0.0247558,Beta=1;ties=(f1.Intensity=1-f0.A0)'
 
     #run IqtFitSequential
-    result, params, fit_group = IqtFitSequential(InputWorkspace=input_ws, Function=function, FitType='1S_s', StartX=0, EndX=0.2, SpecMin=0, SpecMax=16)
+    result, params, fit_group = IqtFitSequential(InputWorkspace=input_ws, Function=function, StartX=0, EndX=0.2, SpecMin=0, SpecMax=16)
 
 
 .. categories::
diff --git a/docs/source/algorithms/QENSFitSequential-v1.rst b/docs/source/algorithms/QENSFitSequential-v1.rst
new file mode 100644
index 0000000000000000000000000000000000000000..eb943a3b86b3e436fbbe1376d5547a0a523a827e
--- /dev/null
+++ b/docs/source/algorithms/QENSFitSequential-v1.rst
@@ -0,0 +1,48 @@
+
+.. algorithm::
+
+.. summary::
+
+.. relatedalgorithms::
+
+.. properties::
+
+Description
+-----------
+An algorithm used for fitting QENS-data sequentially and formatting the output. Uses the
+:ref:`PlotPeakByLogValue <algm-PlotPeakByLogValue>` algorithm to perform the sequential fit.
+
+The string format expected by the "Input" property of this algorithm is outlined in
+:ref:`PlotPeakByLogValue <algm-PlotPeakByLogValue>`.
+
+Workflow
+--------
+
+.. diagram:: QENSFitSequential-v1_wkflw.dot
+
+
+Usage
+-----
+
+**Example - QENSFitSequential**
+
+.. testcode:: QENSFitSequentialExample
+
+  from __future__ import print_function
+
+  # Load sample and resolution files
+  sample = Load('irs26176_graphite002_red.nxs')
+  resolution = Load('irs26173_graphite002_red.nxs')
+
+  # Set up algorithm parameters
+  function = """name=LinearBackground,A0=0,A1=0,ties=(A0=0.000000,A1=0.0);
+  (composite=Convolution,FixResolution=true,NumDeriv=true;
+  name=Resolution,Workspace=resolution,WorkspaceIndex=0;
+  name=Lorentzian,Amplitude=1,PeakCentre=0,FWHM=0.0175)"""
+  startX = -0.547608
+  endX = 0.543217
+  specMin = 0
+  specMax = sample.getNumberHistograms() - 1
+  convolve = True  # Convolve the fitted model components with the resolution
+  minimizer = "Levenberg-Marquardt"
+  maxIt = 500
\ No newline at end of file
diff --git a/docs/source/diagrams/IqtFitSequential-v1_wkflw.dot b/docs/source/diagrams/IqtFitSequential-v1_wkflw.dot
deleted file mode 100644
index bc7a0e846d308cf5ea5344ca2fbece7355df7414..0000000000000000000000000000000000000000
--- a/docs/source/diagrams/IqtFitSequential-v1_wkflw.dot
+++ /dev/null
@@ -1,72 +0,0 @@
-digraph IqtFitSequential {
-  label="IqtFitSequential Flowchart"
-  $global_style
-  
-  subgraph params {
-    $param_style
-    InputWorkspace
-    InputWorkspace2         [label="InputWorkspace"]
-    Function
-    StartX
-    EndX
-    Minimizer
-    MaxIterations
-    ExtractMembers
-    ParameterTable
-    ParameterNames
-    FitGroup
-    FitGroup2               [label="FitGroup"]
-    SampleLogs
-    ResultWorkspace
-    ResultWorkspace2        [label="ResultWorkspace"]
-  }
-
-  subgraph algorithms {
-    $algorithm_style
-    CropWorkspace
-    PlotPeakByLogValue
-    ConvertToHistogram
-    AddSampleLogMultiple
-    AddSampleLogMultiple2   [label="AddSampleLogMultiple"]
-    CopyLogs
-    CopyLogs2               [label="Copy logs"]
-    ExtractQENSMembers
-  }
-  
-  subgraph decisions {
-    $decision_style
-  }
-  
-  subgraph process  {
-    $process_style
-    ConvertToElasticQ
-  }
-  
-  InputWorkspace                -> CropWorkspace
-  StartX                        -> CropWorkspace
-  EndX                          -> CropWorkspace
-  CropWorkspace                 -> ConvertToHistogram
-  ConvertToHistogram            -> ConvertToElasticQ            [label="Convert spectrum axis to ElaticQ"]
-  ConvertToElasticQ             -> PlotPeakByLogValue
-  Function                      -> PlotPeakByLogValue
-  Minimizer                     -> PlotPeakByLogValue
-  MaxIterations                 -> PlotPeakByLogValue
-  PlotPeakByLogValue            -> ParameterTable
-  PlotPeakByLogValue            -> FitGroup                     [label="GroupWorkspace containing fitting results from each spectra"]
-  ParameterTable                -> ProcessIndirectFitParameters
-  ParameterNames                -> ProcessIndirectFitParameters [label="Convert parameter of interest into MatrixWorkspace containing paramNames"]
-  ProcessIndirectFitParameters  -> ResultWorkspace              [label="Outputs Matrixworkspace containing Tau, Intensity, A0 and Beta"]
-  InputWorkspace2               -> CopyLogs                     [label="Copy SampleLogs from InputWorkspace to OutputWorkspaces"]
-  ResultWorkspace               -> CopyLogs
-  InputWorkspace2               -> CopyLogs2
-  FitGroup                      -> CopyLogs2
-  CopyLogs                      -> AddSampleLogMultiple         
-  CopyLogs2                     -> AddSampleLogMultiple2
-  SampleLogs                    -> AddSampleLogMultiple2
-  SampleLogs                    -> AddSampleLogMultiple         [label="Additional SampleLogs based on Fit data"]
-  AddSampleLogMultiple          -> ResultWorkspace2             [label="Outputworkspace"]
-  AddSampleLogMultiple2         -> FitGroup2                    [label="OutputWorkspace"]
-  ExtractMembers                -> ExtractQENSMembers           [label="Extract members?"]
-  InputWorkspace                -> ExtractQENSMembers
-  FitGroup                      -> ExtractQENSMembers
-}
diff --git a/docs/source/diagrams/ConvolutionFitSequential-v1_wkflw.dot b/docs/source/diagrams/QENSFitSequential-v1_wkflw.dot
similarity index 76%
rename from docs/source/diagrams/ConvolutionFitSequential-v1_wkflw.dot
rename to docs/source/diagrams/QENSFitSequential-v1_wkflw.dot
index 90e2132cdbe988da0b4c3822d26c111f0f8e5ea9..c4329aa9257dd85788141be23875fe2dd428a4c5 100644
--- a/docs/source/diagrams/ConvolutionFitSequential-v1_wkflw.dot
+++ b/docs/source/diagrams/QENSFitSequential-v1_wkflw.dot
@@ -1,28 +1,33 @@
-digraph ConvolutionFitSequential {
-  label="ConvolutionFitSequential Flowchart"
+digraph QENSFitSequential {
+  label="QENSFitSequential Flowchart"
   $global_style
 
   subgraph params  {
     $param_style
     InputWorkspace
-    FitType
+    Input
     FitParameters
-    BackgroundType
     Function
-    XStart
-    XEnd
+    StartX
+    EndX
     SpecMin
     SpecMax
-    Convolve
+    ConvolveMembers
     ExtractMembers
+    PassWSIndexToFunction
     Minimizer
+    CostFunction
     MaxIterations
+    PeakRadius
+    EvaluationType
+    LogValue
     ParameterTable
     FitWorkspaces
     NormalisedCovarianceWorkspace
     ParameterNames
-    ResultWorkspace
 	OutputWorkspace
+	OutputParameterWorkspace
+	OutputWorkspaceGroup
     SampleLog
   }
 
@@ -48,20 +53,25 @@ digraph ConvolutionFitSequential {
   }
 
   InputWorkspace                 -> FitFunction
-  BackgroundType                 -> FitFunction
-  FitType                        -> FitFunction
+  Input                          -> FitFunction
   Function                       -> FitFunction
   FitParameters                  -> FitFunction
   SpecMin                        -> FitFunction
   SpecMax                        -> FitFunction
-  InputWorkspace                 -> ConvertSpectrumAxis            [label="Convert to Elastic Q"]
+  InputWorkspace                 -> ConvertSpectrumAxis            [label="Attempt conversion to Elastic Q"]
   ConvertSpectrumAxis            -> PlotPeakByLogValue             [label="InputWorkspace"]
+  Input                          -> PlotPeakByLogValue
   FitFunction                    -> PlotPeakByLogValue
-  XStart                         -> PlotPeakByLogValue
-  XEnd                           -> PlotPeakByLogValue
-  Convolve                       -> PlotPeakByLogValue
+  StartX                         -> PlotPeakByLogValue
+  EndX                           -> PlotPeakByLogValue
+  ConvolveMembers                -> PlotPeakByLogValue
   MaxIterations                  -> PlotPeakByLogValue
   Minimizer                      -> PlotPeakByLogValue
+  CostFunction                   -> PlotPeakByLogValue
+  PeakRadius                     -> PlotPeakByLogValue
+  LogValue                       -> PlotPeakByLogValue
+  PassWSIndexToFunction          -> PlotPeakByLogValue
+  EvaluationType                 -> PlotPeakByLogValue
   PlotPeakByLogValue             -> NormalisedCovarianceWorkspace
   PlotPeakByLogValue             -> ParameterTable
   PlotPeakByLogValue             -> FitWorkspaces
@@ -81,4 +91,4 @@ digraph ConvolutionFitSequential {
   FitWorkspaces                  -> do_extract_members
   InputWorkspace                 -> do_extract_members
   do_extract_members             -> ExtractQENSMembers             [label="Yes"]
-}
+}
\ No newline at end of file
diff --git a/docs/source/release/v3.13.0/indirect_inelastic.rst b/docs/source/release/v3.13.0/indirect_inelastic.rst
index 7eb0483dc76efdaf716da17508a35be37886f4c4..d6f5483badf14fe17106d96141894bb772ae8b87 100644
--- a/docs/source/release/v3.13.0/indirect_inelastic.rst
+++ b/docs/source/release/v3.13.0/indirect_inelastic.rst
@@ -10,3 +10,19 @@ Indirect Inelastic Changes
     improvements, followed by bug fixes.
 
 :ref:`Release 3.13.0 <v3.13.0>`
+
+Algorithms
+----------
+
+New
+###
+
+- :ref:`algm-QENSFitSequential` can be used to perform a general QENS sequential fit, in addition providing the
+  functionality to fit across multiple datasets.
+
+
+Improved
+########
+
+- :ref:`algm-ConvolutionFitSequential` and :ref:`algm-IqtFitSequential` can now accept multiple datasets as input, in
+  the same format as that of :ref:`algm-PlotPeakByLogValue`.
diff --git a/qt/scientific_interfaces/Indirect/ConvFit.cpp b/qt/scientific_interfaces/Indirect/ConvFit.cpp
index f84281861a466f00634e869fda41da46a04dbf12..980bdfa4dc84591451ce100b0cd4cfd99662760b 100644
--- a/qt/scientific_interfaces/Indirect/ConvFit.cpp
+++ b/qt/scientific_interfaces/Indirect/ConvFit.cpp
@@ -354,7 +354,6 @@ IAlgorithm_sptr ConvFit::sequentialFit(const int &specMin,
   auto cfs = AlgorithmManager::Instance().create("ConvolutionFitSequential");
   cfs->initialize();
   cfs->setProperty("PassWSIndexToFunction", true);
-  cfs->setProperty("BackgroundType", backgroundType().toStdString());
   cfs->setProperty("SpecMin", specMin);
   cfs->setProperty("SpecMax", specMax);
   cfs->setProperty("ExtractMembers", boolSettingValue("ExtractMembers"));
diff --git a/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.cpp b/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.cpp
index cc59cf21a2aa6d6a3d79f706e3d6b4ce4b2b8fcb..9524ca5bf94606c7f3012d89d3dcc177e46f8cba 100644
--- a/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.cpp
+++ b/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.cpp
@@ -1051,18 +1051,6 @@ MatrixWorkspace_sptr IndirectFitAnalysisTab::fitWorkspace() const {
   return inputWorkspace();
 }
 
-/**
- * Sets the MaxIterations property of the specified algorithm, to the specified
- * integer value.
- *
- * @param fitAlgorithm  The fit algorithm whose MaxIterations property to set.
- * @param maxIterations The value to set.
- */
-void IndirectFitAnalysisTab::setMaxIterations(IAlgorithm_sptr fitAlgorithm,
-                                              int maxIterations) const {
-  setAlgorithmProperty(fitAlgorithm, "MaxIterations", maxIterations);
-}
-
 /**
  * Called when the 'Run' button is called in the IndirectTab.
  */
@@ -1090,11 +1078,17 @@ void IndirectFitAnalysisTab::runFitAlgorithm(IAlgorithm_sptr fitAlgorithm) {
   setAlgorithmProperty(fitAlgorithm, "EndX", m_fitPropertyBrowser->endX());
   setAlgorithmProperty(fitAlgorithm, "Minimizer",
                        m_fitPropertyBrowser->minimizer(true));
-  setMaxIterations(fitAlgorithm, m_fitPropertyBrowser->maxIterations());
-  setAlgorithmProperty(fitAlgorithm, "Convolve",
+  setAlgorithmProperty(fitAlgorithm, "MaxIterations",
+                       m_fitPropertyBrowser->maxIterations());
+  setAlgorithmProperty(fitAlgorithm, "ConvolveMembers",
                        m_fitPropertyBrowser->convolveMembers());
   setAlgorithmProperty(fitAlgorithm, "PeakRadius",
                        m_fitPropertyBrowser->getPeakRadius());
+  setAlgorithmProperty(fitAlgorithm, "CostFunction",
+                       m_fitPropertyBrowser->costFunction());
+
+  if (m_fitPropertyBrowser->isHistogramFit())
+    setAlgorithmProperty(fitAlgorithm, "EvaluationType", "Histogram");
 
   auto fittingFunction = m_fitPropertyBrowser->getFittingFunction();
   m_appendResults = false;
diff --git a/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.h b/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.h
index 653be7a5d30757a1a69c2338d26e4fe1dbe03571..9d2dd8b75bc4a23320f27886fcf1933271a0c1fc 100644
--- a/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.h
+++ b/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.h
@@ -261,9 +261,6 @@ protected:
   void setPlotOptions(QComboBox *cbPlotType,
                       const QSet<QString> &options) const;
 
-  virtual void setMaxIterations(Mantid::API::IAlgorithm_sptr fitAlgorithm,
-                                int maxIterations) const;
-
   virtual std::string createSequentialFitOutputName() const;
 
   virtual std::string createSingleFitOutputName() const = 0;
diff --git a/qt/scientific_interfaces/Indirect/IqtFit.cpp b/qt/scientific_interfaces/Indirect/IqtFit.cpp
index dc168ad34453bb8ac57a8c960a96d5d181fc4098..a5ec3d4b9d4685fd0a9a65b4d7cac1f33b132589 100644
--- a/qt/scientific_interfaces/Indirect/IqtFit.cpp
+++ b/qt/scientific_interfaces/Indirect/IqtFit.cpp
@@ -227,7 +227,6 @@ IAlgorithm_sptr IqtFit::iqtFitAlgorithm(const size_t &specMin,
                                         const size_t &specMax) const {
   const auto outputName = outputWorkspaceName(specMin);
   const bool constrainBeta = boolSettingValue("ConstrainBeta");
-  const bool constrainIntens = boolSettingValue("ConstrainIntensities");
   const bool extractMembers = boolSettingValue("ExtractMembers");
 
   IAlgorithm_sptr iqtFitAlg;
@@ -238,15 +237,10 @@ IAlgorithm_sptr IqtFit::iqtFitAlgorithm(const size_t &specMin,
     iqtFitAlg = AlgorithmManager::Instance().create("IqtFitSequential");
 
   iqtFitAlg->initialize();
-  iqtFitAlg->setProperty("FitType", fitTypeString() + "_s");
-  iqtFitAlg->setProperty("SpecMin", boost::numeric_cast<long>(specMin));
-  iqtFitAlg->setProperty("SpecMax", boost::numeric_cast<long>(specMax));
-  iqtFitAlg->setProperty("ConstrainIntensities", constrainIntens);
+  iqtFitAlg->setProperty("SpecMin", static_cast<int>(specMin));
+  iqtFitAlg->setProperty("SpecMax", static_cast<int>(specMax));
   iqtFitAlg->setProperty("ExtractMembers", extractMembers);
-  iqtFitAlg->setProperty("OutputResultWorkspace", outputName + "_Result");
-  iqtFitAlg->setProperty("OutputParameterWorkspace",
-                         outputName + "_Parameters");
-  iqtFitAlg->setProperty("OutputWorkspaceGroup", outputName + "_Workspaces");
+  iqtFitAlg->setProperty("OutputWorkspace", outputName + "_Result");
   return iqtFitAlg;
 }
 
@@ -277,11 +271,6 @@ IqtFit::getParameters(IFunction_sptr function,
   return parameters;
 }
 
-void IqtFit::setMaxIterations(IAlgorithm_sptr fitAlgorithm,
-                              int maxIterations) const {
-  fitAlgorithm->setProperty("MaxIterations", static_cast<long>(maxIterations));
-}
-
 void IqtFit::updatePlotOptions() {
   IndirectFitAnalysisTab::updatePlotOptions(m_uiForm->cbPlotType);
 }
diff --git a/qt/scientific_interfaces/Indirect/IqtFit.h b/qt/scientific_interfaces/Indirect/IqtFit.h
index 391db76fe4b5c7f029176e15b0ecc982d4b06c72..d642678d83753db010cc6d708032bd991472571d 100644
--- a/qt/scientific_interfaces/Indirect/IqtFit.h
+++ b/qt/scientific_interfaces/Indirect/IqtFit.h
@@ -43,8 +43,6 @@ protected:
   std::string createSequentialFitOutputName() const override;
   Mantid::API::IAlgorithm_sptr singleFitAlgorithm() const override;
   Mantid::API::IAlgorithm_sptr sequentialFitAlgorithm() const override;
-  void setMaxIterations(Mantid::API::IAlgorithm_sptr fitAlgorithm,
-                        int maxIterations) const override;
   void enablePlotResult() override;
   void disablePlotResult() override;
   void enableSaveResult() override;
diff --git a/qt/scientific_interfaces/Indirect/JumpFit.cpp b/qt/scientific_interfaces/Indirect/JumpFit.cpp
index 6aa7bcd3a276fdefd27a5febe314e448c720567c..e939e726e4a249bedff0f917bfc23b913d7a6708 100644
--- a/qt/scientific_interfaces/Indirect/JumpFit.cpp
+++ b/qt/scientific_interfaces/Indirect/JumpFit.cpp
@@ -117,10 +117,6 @@ void JumpFit::algorithmComplete(bool error) {
 
   // Process the parameters table
   const auto paramWsName = outputWorkspaceName() + "_Parameters";
-  const auto resultWsName = outputWorkspaceName() + "_Result";
-  deleteWorkspaceAlgorithm(paramWsName)->execute();
-  renameWorkspaceAlgorithm(outputWorkspaceName(), paramWsName)->execute();
-  processParametersAlgorithm(paramWsName, resultWsName)->execute();
   IndirectFitAnalysisTab::fitAlgorithmComplete(paramWsName);
 }
 
@@ -254,49 +250,27 @@ void JumpFit::updatePlotRange() {
 
 std::string JumpFit::createSingleFitOutputName() const {
   auto outputName = inputWorkspace()->getName();
+  auto position = outputName.rfind("_Result");
 
-  // Remove _red
-  const auto cutIndex = outputName.find_last_of('_');
-  if (cutIndex != std::string::npos)
-    outputName = outputName.substr(0, cutIndex);
+  if (position != std::string::npos)
+    outputName = outputName.substr(0, position) +
+                 outputName.substr(position + 7, outputName.size());
   return outputName + "_" + selectedFitType().toStdString() + "_JumpFit";
 }
 
 IAlgorithm_sptr JumpFit::singleFitAlgorithm() const {
-  const auto sample = inputWorkspace()->getName();
   const auto widthText = m_uiForm->cbWidth->currentText().toStdString();
   const auto width = m_spectraList.at(widthText);
 
-  auto fitAlg = AlgorithmManager::Instance().create("PlotPeakByLogValue");
+  auto fitAlg = AlgorithmManager::Instance().create("QENSFitSequential");
   fitAlg->initialize();
-  fitAlg->setProperty("Input", sample + ",i" + std::to_string(width));
-  fitAlg->setProperty("OutputWorkspace", outputWorkspaceName());
-  fitAlg->setProperty("CreateOutput", true);
+  fitAlg->setProperty("SpecMin", static_cast<int>(width));
+  fitAlg->setProperty("SpecMax", static_cast<int>(width));
+  fitAlg->setProperty("OutputWorkspace",
+                      createSingleFitOutputName() + "_Result");
   return fitAlg;
 }
 
-/*
- * Creates an algorithm for processing an output parameters workspace.
- *
- * @param parameterWSName The name of the parameters workspace.
- * @return                A processing algorithm.
- */
-IAlgorithm_sptr
-JumpFit::processParametersAlgorithm(const std::string &parameterWSName,
-                                    const std::string &resultWSName) {
-  const auto parameterNames =
-      boost::algorithm::join(fitFunction()->getParameterNames(), ",");
-
-  auto processAlg =
-      AlgorithmManager::Instance().create("ProcessIndirectFitParameters");
-  processAlg->setProperty("InputWorkspace", parameterWSName);
-  processAlg->setProperty("ColumnX", "axis-1");
-  processAlg->setProperty("XAxisUnit", "MomentumTransfer");
-  processAlg->setProperty("ParameterNames", parameterNames);
-  processAlg->setProperty("OutputWorkspace", resultWSName);
-  return processAlg;
-}
-
 IAlgorithm_sptr
 JumpFit::deleteWorkspaceAlgorithm(const std::string &workspaceName) {
   auto deleteAlg = AlgorithmManager::Instance().create("DeleteWorkspace");
@@ -304,15 +278,6 @@ JumpFit::deleteWorkspaceAlgorithm(const std::string &workspaceName) {
   return deleteAlg;
 }
 
-IAlgorithm_sptr
-JumpFit::renameWorkspaceAlgorithm(const std::string &workspaceToRename,
-                                  const std::string &newName) {
-  auto renameAlg = AlgorithmManager::Instance().create("RenameWorkspace");
-  renameAlg->setProperty("InputWorkspace", workspaceToRename);
-  renameAlg->setProperty("OutputWorkspace", newName);
-  return renameAlg;
-}
-
 IAlgorithm_sptr JumpFit::scaleAlgorithm(const std::string &workspaceToScale,
                                         const std::string &outputName,
                                         double scaleFactor) {
diff --git a/qt/scientific_interfaces/Indirect/JumpFit.h b/qt/scientific_interfaces/Indirect/JumpFit.h
index 0afdd82c388ef9b923f5fddb137647f18f15dd44..a49188be0d929049c869824bed02baa0eb342e37 100644
--- a/qt/scientific_interfaces/Indirect/JumpFit.h
+++ b/qt/scientific_interfaces/Indirect/JumpFit.h
@@ -51,17 +51,9 @@ protected:
   std::string createSingleFitOutputName() const override;
   Mantid::API::IAlgorithm_sptr singleFitAlgorithm() const override;
 
-  Mantid::API::IAlgorithm_sptr
-  processParametersAlgorithm(const std::string &parameterWSName,
-                             const std::string &resultWSName);
-
   Mantid::API::IAlgorithm_sptr
   deleteWorkspaceAlgorithm(const std::string &workspaceName);
 
-  Mantid::API::IAlgorithm_sptr
-  renameWorkspaceAlgorithm(const std::string &workspaceToRename,
-                           const std::string &newName);
-
   Mantid::API::IAlgorithm_sptr
   scaleAlgorithm(const std::string &workspaceToScale,
                  const std::string &outputName, double scaleFactor);
diff --git a/qt/scientific_interfaces/Indirect/MSDFit.cpp b/qt/scientific_interfaces/Indirect/MSDFit.cpp
index 63090cc4fb0a82a52b6d5a9a362782a02412ae01..e3bdde91186e938937fb6da6650133eeec74a21d 100644
--- a/qt/scientific_interfaces/Indirect/MSDFit.cpp
+++ b/qt/scientific_interfaces/Indirect/MSDFit.cpp
@@ -101,16 +101,14 @@ std::string MSDFit::constructBaseName() const {
 }
 
 IAlgorithm_sptr MSDFit::singleFitAlgorithm() const {
-  const auto model = selectedFitType();
   const auto fitSpec = m_uiForm->spPlotSpectrum->value();
-  return msdFitAlgorithm(modelToAlgorithmProperty(model), fitSpec, fitSpec);
+  return msdFitAlgorithm(fitSpec, fitSpec);
 }
 
 IAlgorithm_sptr MSDFit::sequentialFitAlgorithm() const {
-  const auto model = selectedFitType();
   const auto specMin = m_uiForm->spSpectraMin->value();
   const auto specMax = m_uiForm->spSpectraMax->value();
-  return msdFitAlgorithm(modelToAlgorithmProperty(model), specMin, specMax);
+  return msdFitAlgorithm(specMin, specMax);
 }
 
 /*
@@ -118,25 +116,21 @@ IAlgorithm_sptr MSDFit::sequentialFitAlgorithm() const {
  * specified name, to be run from the specified minimum spectrum to
  * the specified maximum spectrum.
  *
- * @param model   The name of the model to be used by the algorithm.
  * @param specMin The minimum spectrum to fit.
  * @param specMax The maximum spectrum to fit.
  * @return        An MSDFit Algorithm using the specified model, which
  *                will run across all spectrum between the specified
  *                minimum and maximum.
  */
-IAlgorithm_sptr MSDFit::msdFitAlgorithm(const std::string &model, int specMin,
-                                        int specMax) const {
-  IAlgorithm_sptr msdAlg = AlgorithmManager::Instance().create("MSDFit");
+IAlgorithm_sptr MSDFit::msdFitAlgorithm(int specMin, int specMax) const {
+  IAlgorithm_sptr msdAlg =
+      AlgorithmManager::Instance().create("QENSFitSequential");
   msdAlg->initialize();
-  msdAlg->setProperty("Model", model);
-  msdAlg->setProperty("SpecMin", boost::numeric_cast<long>(specMin));
-  msdAlg->setProperty("SpecMax", boost::numeric_cast<long>(specMax));
-  msdAlg->setProperty("XStart", startX());
-  msdAlg->setProperty("XEnd", endX());
+  msdAlg->setProperty("SpecMin", specMin);
+  msdAlg->setProperty("SpecMax", specMax);
   msdAlg->setProperty(
       "OutputWorkspace",
-      outputWorkspaceName(boost::numeric_cast<size_t>(specMin)));
+      outputWorkspaceName(boost::numeric_cast<size_t>(specMin)) + "_Result");
   return msdAlg;
 }
 
@@ -287,25 +281,6 @@ void MSDFit::endXChanged(double endX) {
   rangeSelector->setMaximum(endX);
 }
 
-/*
- * Given the selected model in the interface, returns the name of
- * the associated model to pass to the MSDFit algorithm.
- *
- * @param model The name of the model as displayed in the interface.
- * @return      The name of the model as defined in the MSDFit algorithm.
- */
-std::string MSDFit::modelToAlgorithmProperty(const QString &model) const {
-
-  if (model == "Gaussian")
-    return "Gauss";
-  else if (model == "Peters")
-    return "Peters";
-  else if (model == "Yi")
-    return "Yi";
-  else
-    return model.toStdString();
-}
-
 /**
  * Handles saving of workspace
  */
diff --git a/qt/scientific_interfaces/Indirect/MSDFit.h b/qt/scientific_interfaces/Indirect/MSDFit.h
index 805ee9e1fdaafb88c0fe302680eceba478c2ba5c..d3fc5a24faaddfdfeb19baf725d4dbd3d8d8e04f 100644
--- a/qt/scientific_interfaces/Indirect/MSDFit.h
+++ b/qt/scientific_interfaces/Indirect/MSDFit.h
@@ -57,9 +57,7 @@ protected:
 private:
   void disablePlotGuess() override;
   void enablePlotGuess() override;
-  Mantid::API::IAlgorithm_sptr msdFitAlgorithm(const std::string &model,
-                                               int specMin, int specMax) const;
-  std::string modelToAlgorithmProperty(const QString &model) const;
+  Mantid::API::IAlgorithm_sptr msdFitAlgorithm(int specMin, int specMax) const;
 
   std::unique_ptr<Ui::MSDFit> m_uiForm;
 };