diff --git a/Framework/PythonInterface/plugins/algorithms/LRDirectBeamSort.py b/Framework/PythonInterface/plugins/algorithms/LRDirectBeamSort.py
new file mode 100644
index 0000000000000000000000000000000000000000..8060917f7338aecd3f137b4ce4282fb94e25513e
--- /dev/null
+++ b/Framework/PythonInterface/plugins/algorithms/LRDirectBeamSort.py
@@ -0,0 +1,247 @@
+#pylint: disable=no-init,invalid-name
+import math
+import time
+import numpy as np
+import mantid
+from mantid.api import *
+from mantid.simpleapi import *
+from mantid.kernel import *
+import logging
+
+class CompareTwoNXSDataForSFcalculator(object):
+    """
+        will return -1, 0 or 1 according to the position of the nexusToPosition in relation to the 
+        nexusToCompareWith based on the following criteria
+        #1: number of attenuators (ascending order)
+        #2: lambda requested (descending order)
+        #3: S2W (ascending order)
+        #4: S2H (descending order)
+        #5 if everything up to this point is identical, return 0
+    """
+    nexusToCompareWithRun = None
+    nexusToPositionRun = None
+    resultComparison = 0
+    
+    def __init__(self, nxsdataToCompareWith, nxsdataToPosition):
+        self.nexusToCompareWithRun = nxsdataToCompareWith.getRun()
+        self.nexusToPositionRun = nxsdataToPosition.getRun()
+                
+        compare1 = self.compareParameter('LambdaRequest', 'descending')
+        if compare1 != 0:
+            self.resultComparison = compare1
+            return
+        
+        compare2 = self.compareParameter('vATT', 'ascending')
+        if compare2 != 0:
+            self.resultComparison = compare2
+            return
+        
+        pcharge1 = self.nexusToCompareWithRun.getProperty('gd_prtn_chrg').value/nxsdataToCompareWith.getNEvents()
+        pcharge2 = self.nexusToPositionRun.getProperty('gd_prtn_chrg').value/nxsdataToPosition.getNEvents()
+
+        self.resultComparison = -1 if pcharge1 < pcharge2 else 1
+        
+    def compareParameter(self, param, order):
+        """
+            Compare parameters for the two runs
+        """
+        _nexusToCompareWithRun = self.nexusToCompareWithRun
+        _nexusToPositionRun = self.nexusToPositionRun
+        
+        _paramNexusToCompareWith = float(_nexusToCompareWithRun.getProperty(param).value[0])
+        _paramNexusToPosition = float(_nexusToPositionRun.getProperty(param).value[0])
+            
+        if order == 'ascending':
+            resultLessThan = -1
+            resultMoreThan = 1
+        else:
+            resultLessThan = 1
+            resultMoreThan = -1
+        
+        if (_paramNexusToPosition < _paramNexusToCompareWith):
+            return resultLessThan
+        elif (_paramNexusToPosition > _paramNexusToCompareWith):
+            return resultMoreThan
+        else:
+            return 0
+        
+    def result(self):
+        return self.resultComparison
+        
+def sorter_function(r1, r2):
+    """
+        Sorter function used by with the 'sorted' call to sort the direct beams.
+    """
+    return CompareTwoNXSDataForSFcalculator(r2, r1).result()
+
+     
+class LRDirectBeamSort(PythonAlgorithm):
+
+    def category(self):
+        return "Reflectometry\\SNS"
+
+    def name(self):
+        return "LRDirectBeamSort"
+
+    def version(self):
+        return 1
+
+    def summary(self):
+        return "Sort a set of direct beams for the purpose of calculating scaling factors."
+
+    def PyInit(self):
+        self.declareProperty(IntArrayProperty("RunList", [], direction=Direction.Input),
+                             "List of run numbers (integers) to be sorted - takes precedence over WorkspaceList")
+        self.declareProperty(StringArrayProperty("WorkspaceList", [], direction=Direction.Input),
+                             "List of workspace names to be sorted")
+        self.declareProperty("UseLowResCut", False, direction=Direction.Input,
+                             doc="If True, an x-direction cut will be determined and used")
+        self.declareProperty("ComputeScalingFactors", True, direction=Direction.Input,
+                             doc="If True, the scaling factors will be computed")
+        self.declareProperty("TOFSteps", 200.0, doc="TOF bin width")
+        self.declareProperty(FileProperty("ScalingFactorFile","",
+                                          action=FileAction.OptionalSave,
+                                          extensions=['cfg']),
+                             "Scaling factor file to be created")
+        
+        self.declareProperty(IntArrayProperty("OrderedRunList", [], direction=Direction.Output),
+                             "Ordered list of run numbers")
+        self.declareProperty(StringArrayProperty("OrderedNameList", [], direction=Direction.Output),
+                             "Ordered list of workspace names corresponding to the run list")
+
+    def PyExec(self):
+        compute = self.getProperty("ComputeScalingFactors").value
+        lr_data = []
+        run_list = self.getProperty("RunList").value
+        if len(run_list) > 0:
+            for run in run_list:
+                workspace = LoadEventNexus(Filename="REF_L_%s" % run, OutputWorkspace="__data_file_%s" % run, MetaDataOnly=not compute)
+                lr_data.append(workspace)
+        else:
+            ws_list = self.getProperty("WorkspaceList").value
+            for ws in ws_list:
+                lr_data.append(mtd[ws])
+            
+        lr_data_sorted = sorted(lr_data, cmp=sorter_function)
+        
+        # Set the output properties
+        run_numbers = [r.getRunNumber() for r in lr_data_sorted]
+        ws_names = [str(r) for r in lr_data_sorted]
+        self.setProperty("OrderedRunList", run_numbers)
+        self.setProperty("OrderedNameList", ws_names)
+
+        # Compute the scaling factors if requested
+        if compute:
+            sf_file = self.getProperty("ScalingFactorFile").value
+            if len(sf_file)==0:
+                logger.error("Scaling factors were requested but no output file was set")
+            else:
+                self._compute_scaling_factors(lr_data_sorted)
+            
+    def _compute_scaling_factors(self, lr_data_sorted):
+        """
+            If we need to compute the scaling factors, group the runs by their wavelength request 
+            @param lr_data_sorted: ordered list of workspaces
+        """
+        #TODO: change LRScalingFactor so that it reads the number of attenuators from the file.
+        group_list = []
+        current_group = []
+        group_wl = None
+        for r in lr_data_sorted:
+            wl = r.getRun().getProperty('LambdaRequest').value[0]
+            
+            if not group_wl == wl:
+                # New group
+                group_wl = wl
+                if len(current_group)>0:
+                    group_list.append(current_group)
+                current_group = []
+                                
+            current_group.append(r)
+            
+        # Add in the last group
+        group_list.append(current_group)
+                
+        tof_steps = self.getProperty("TOFSteps").value
+        scaling_file = self.getProperty("ScalingFactorFile").value
+        use_low_res_cut = self.getProperty("UseLowResCut").value
+        summary = ""
+        for g in group_list:
+            if len(g) == 0:
+                continue
+            runs = [r.getRunNumber() for r in g]
+            logger.notice(str(runs))
+            
+            direct_beam_runs = []
+            attenuators = []
+            peak_ranges = []
+            x_ranges = []
+            bck_ranges = []
+            
+            for run in g:
+                # Create peak workspace
+                number_of_pixels_x = int(run.getInstrument().getNumberParameter("number-of-x-pixels")[0])
+                number_of_pixels_y = int(run.getInstrument().getNumberParameter("number-of-y-pixels")[0])
+                
+                # Direct beam signal
+                workspace = RefRoi(InputWorkspace=run, ConvertToQ=False,
+                                   NXPixel=number_of_pixels_x,
+                                   NYPixel=number_of_pixels_y,
+                                   IntegrateY=False,
+                                   OutputWorkspace="__ref_peak")
+                workspace = Transpose(InputWorkspace=workspace)
+                peak, _, _ = LRPeakSelection(InputWorkspace=workspace)
+
+                # Low resolution cut
+                if use_low_res_cut:
+                    workspace = RefRoi(InputWorkspace=run, ConvertToQ=False,
+                                       NXPixel=number_of_pixels_x,
+                                       NYPixel=number_of_pixels_y,
+                                       IntegrateY=True,
+                                       OutputWorkspace="__ref_peak")
+                    workspace = Transpose(InputWorkspace=workspace)
+                    _, low_res, _ = LRPeakSelection(InputWorkspace=workspace)
+                else:
+                    low_res = [0, number_of_pixels_x]
+                
+                # TOF range         
+                att = run.getRun().getProperty('vATT').value[0]-1
+                attenuators.append(int(att))
+                
+                direct_beam_runs.append(run.getRunNumber())
+                peak_ranges.append(int(peak[0]))
+                peak_ranges.append(int(peak[1]))
+                x_ranges.append(int(low_res[0]))
+                x_ranges.append(int(low_res[1]))
+                bck_ranges.append(int(peak[0])-3)
+                bck_ranges.append(int(peak[1])+3)
+                
+                summary += "%10s %s %5s,%5s %5s,%5s\n" % (run.getRunNumber(), att, peak[0], peak[1], low_res[0], low_res[1])
+                
+            # Determine TOF range from first file
+            sample = g[0].getInstrument().getSample()
+            source = g[0].getInstrument().getSource()
+            source_sample_distance = sample.getDistance(source)
+            detector = g[0].getDetector(0)
+            sample_detector_distance = detector.getPos().getZ()
+            source_detector_distance = source_sample_distance + sample_detector_distance
+            h = 6.626e-34  # m^2 kg s^-1
+            m = 1.675e-27  # kg
+            wl = g[0].getRun().getProperty('LambdaRequest').value[0]
+            tof_min = source_detector_distance / h * m * (wl + 0.5 - 1.7) * 1e-4
+            tof_max = source_detector_distance / h * m * (wl + 0.5 + 1.7) * 1e-4
+            tof_range = [tof_min, tof_max]
+            
+            summary += "TOF: %s\n\n" % tof_range
+            
+            # Compute the scaling factors
+            LRScalingFactors(DirectBeamRuns=direct_beam_runs,
+                             Attenuators = attenuators,
+                             TOFRange=tof_range, TOFSteps=tof_steps,
+                             SignalPeakPixelRange=peak_ranges,
+                             SignalBackgroundPixelRange=bck_ranges,
+                             LowResolutionPixelRange=x_ranges,
+                             ScalingFactorFile=scaling_file)
+        logger.notice(summary)
+
+AlgorithmFactory.subscribe(LRDirectBeamSort)
diff --git a/Framework/PythonInterface/plugins/algorithms/LRPeakSelection.py b/Framework/PythonInterface/plugins/algorithms/LRPeakSelection.py
new file mode 100644
index 0000000000000000000000000000000000000000..21cfd23cdd6d100e54dfc1ac3fc20e7f74bd4582
--- /dev/null
+++ b/Framework/PythonInterface/plugins/algorithms/LRPeakSelection.py
@@ -0,0 +1,252 @@
+#pylint: disable=no-init,invalid-name
+import math
+import time
+import numpy as np
+import mantid
+from mantid.api import *
+from mantid.simpleapi import *
+from mantid.kernel import *
+
+class PeakFinderDerivation(object):
+    """
+        Determine various types of peak for reflectivity.
+        Those include specular peaks and low-resolution direction signal range.
+    """
+    xdata_firstderi = []
+    ydata_firstderi = []
+    five_highest_ydata = []
+    five_highest_xdata = []
+    sum_peak_counts = -1
+    sum_peak_counts_time_pixel = -1
+    peak_pixel = -1
+    deri_min = 1
+    deri_max = -1
+    deri_min_pixel_value = -1
+    deri_max_pixel_value = -1
+    mean_counts_firstderi = -1
+    std_deviation_counts_firstderi = -1
+    peak_max_final_value = -1
+    peak_min_final_value = -1
+
+    def __init__(self, workspace, back_offset=4):
+        self.back_offset = back_offset
+        self.ydata = workspace.dataY(0)
+        self.xdata = np.arange(len(self.ydata))
+        
+        self.compute()
+
+    def compute(self):
+        """
+            Perform the computation
+        """
+        self.initArrays()
+        self.calculate_five_highest_points()
+        self.calculate_peak_pixel()
+        self.calculate_first_derivative()
+        self.calculate_min_max_derivative_pixels()
+        self.calculate_avg_and_derivative()
+        self.calculate_min_max_signal_pixels()
+        self.low_resolution_range()
+        
+    def initArrays(self):
+        """
+            Initialize internal data members
+        """
+        self.xdata_firstderi = []
+        self.ydata_firstderi = []
+        self.peak = [-1, -1]
+        self.low_res = [-1, -1]
+        self.five_highest_ydata = []
+        self.five_highest_xdata = []
+        self.sum_five_highest_ydata = -1
+        self.peak_pixel = -1
+        self.deri_min = 1
+        self.deri_max = -1
+        self.deri_min_pixel_value = -1
+        self.deri_max_pixel_value = -1
+        self.mean_counts_firstderi = -1
+        self.std_deviation_counts_firstderi = -1
+        self.peak_max_final_value = -1
+        self.peak_min_final_value = -1
+
+    def calculate_five_highest_points(self):
+        _xdata = self.xdata
+        _ydata = self.ydata
+        
+        _sort_ydata = np.sort(_ydata)
+        _decreasing_sort_ydata = _sort_ydata[::-1]
+        self.five_highest_ydata = _decreasing_sort_ydata[0:5]
+        
+        _sort_index = np.argsort(_ydata)
+        _decreasing_sort_index = _sort_index[::-1]
+        _5decreasing_sort_index = _decreasing_sort_index[0:5]
+        self.five_highest_xdata = _xdata[_5decreasing_sort_index]
+        
+    def calculate_peak_pixel(self):    
+        self.sum_peak_counts = sum(self.five_highest_ydata)
+        _sum_peak_counts_time_pixel = -1
+        for index,yvalue in enumerate(self.five_highest_ydata):
+            _sum_peak_counts_time_pixel += yvalue * self.five_highest_xdata[index]
+        self.sum_peak_counts_time_pixel = _sum_peak_counts_time_pixel
+        self.peak_pixel = round(self.sum_peak_counts_time_pixel / self.sum_peak_counts)
+
+    def calculate_first_derivative(self):
+        xdata = self.xdata
+        ydata = self.ydata
+        
+        _xdata_firstderi = []
+        _ydata_firstderi = []
+        for i in range(len(xdata)-1):
+            _left_x = xdata[i]
+            _right_x = xdata[i+1]
+            _xdata_firstderi.append(np.mean([_left_x, _right_x]))
+            
+            _left_y = ydata[i]
+            _right_y = ydata[i+1]
+            _ydata_firstderi.append((_right_y - _left_y)/(_right_x - _left_x))
+        
+        self.xdata_firstderi = _xdata_firstderi
+        self.ydata_firstderi = _ydata_firstderi
+        
+    def calculate_min_max_derivative_pixels(self):
+        _pixel = self.xdata_firstderi
+        _counts_firstderi = self.ydata_firstderi
+        
+        _sort_counts_firstderi = np.sort(_counts_firstderi)
+        self.deri_min = _sort_counts_firstderi[0]
+        self.deri_max = _sort_counts_firstderi[-1]
+        
+        _sort_index = np.argsort(_counts_firstderi)
+        self.deri_min_pixel_value = int(min([_pixel[_sort_index[0]], _pixel[_sort_index[-1]]]))
+        self.deri_max_pixel_value = int(max([_pixel[_sort_index[0]], _pixel[_sort_index[-1]]]))
+        
+    def calculate_avg_and_derivative(self):
+        _counts_firstderi = np.array(self.ydata_firstderi)
+        self.mean_counts_firstderi = np.mean(_counts_firstderi)
+        _mean_counts_firstderi = np.mean(_counts_firstderi * _counts_firstderi)
+        self.std_deviation_counts_firstderi = math.sqrt(_mean_counts_firstderi)
+
+    def calculate_min_max_signal_pixels(self):
+        """
+            Determine specular peak region
+        """
+        _counts = self.ydata_firstderi
+        _pixel = self.xdata_firstderi
+
+        _deri_min_pixel_value = self.deri_min_pixel_value
+        _deri_max_pixel_value = self.deri_max_pixel_value
+
+        _std_deviation_counts_firstderi = self.std_deviation_counts_firstderi
+
+        px_offset = 0
+        while abs(_counts[int(_deri_min_pixel_value - px_offset)]) > _std_deviation_counts_firstderi:
+            px_offset += 1
+        _peak_min_final_value = _pixel[int(_deri_min_pixel_value - px_offset)]
+            
+        px_offset = 0
+        while abs(_counts[int(round(_deri_max_pixel_value + px_offset))]) > _std_deviation_counts_firstderi:
+            px_offset += 1
+        _peak_max_final_value = _pixel[int(round(_deri_max_pixel_value + px_offset))]
+        
+        self.peak = [int(_peak_min_final_value), int(np.ceil(_peak_max_final_value))]
+
+    def low_resolution_range(self):
+        """
+            Determine the x range of the signal
+        """
+        y_integrated = []
+        total = 0.0
+        for y_value in self.ydata:
+            total += y_value
+            y_integrated.append(total)
+            
+        for i in range(len(y_integrated)):
+            y_integrated[i] /= total
+            
+        # Derivative of the flipped integrated distribution
+        deriv = []
+        offset = 1
+        for i in range(offset, len(y_integrated)):
+            value = (self.xdata[i]-self.xdata[i-offset]) / (y_integrated[i]-y_integrated[i-offset]) 
+            deriv.append(value)
+            
+        # Find lower edge of the main peak
+        center = int(len(deriv)/2.0)
+        middle_value = deriv[center]
+        i_min = 0
+        for i in range(center, 0, -1):
+            if deriv[i]/middle_value>3:
+                i_min = i
+                break
+        # Find upper edge of the main peak
+        i_max = len(deriv)
+        for i in range(center, i_max):
+            if deriv[i]/middle_value>3:
+                i_max = i
+                break
+                
+        self.low_res = [int(self.xdata[i_min])-self.back_offset, int(self.xdata[i_max])+self.back_offset]
+        return self.low_res
+
+class LRPeakSelection(PythonAlgorithm):
+
+    def category(self):
+        return "Reflectometry\\SNS"
+
+    def name(self):
+        return "LRPeakSelection"
+
+    def version(self):
+        return 1
+
+    def summary(self):
+        return "Find reflectivity peak and return its pixel range."
+
+    def PyInit(self):
+        self.declareProperty(WorkspaceProperty("InputWorkspace", "",Direction.Input), "Workspace to select peak from")
+        self.declareProperty(IntArrayProperty("PeakRange", [0,0], direction=Direction.Output))
+        self.declareProperty(IntArrayProperty("LowResRange", [0,0], direction=Direction.Output))
+        self.declareProperty(IntArrayProperty("PrimaryRange", [0,0], direction=Direction.Output))
+        self.declareProperty("ComputePrimaryRange", False, doc="If True, the primary fraction range will be determined")
+
+    def PyExec(self):
+        workspace = self.getProperty("InputWorkspace").value
+
+        # Main peak finding algorithm
+        pf = PeakFinderDerivation(workspace)
+        self.setProperty("PeakRange", pf.peak)
+        logger.information("Peak: [%s, %s]" % (pf.peak[0], pf.peak[1]))
+        self.setProperty("LowResRange", pf.low_res)
+        logger.information("Low Res: [%s, %s]" % (pf.low_res[0], pf.low_res[1]))
+
+        # Primary fraction range
+        compute_primary = self.getProperty("ComputePrimaryRange").value
+        if compute_primary:
+            primary_range = self.clocking_range(workspace)
+            self.setProperty("PrimaryRange", primary_range)
+            logger.information("Primary: [%s, %s]" % (primary_range[0], primary_range[1]))
+
+    def clocking_range(self, workspace):
+        """
+            Determine the primary fraction range
+            @param workspace: workspace to determine the range from
+        """
+        # Get the full range
+        pf = PeakFinderDerivation(workspace, back_offset=0)
+        [left_max, right_min] = pf.low_res
+
+        # Process left-end data
+        pf.ydata = workspace.dataY(0)[0: left_max]
+        pf.xdata = np.arange(len(pf.ydata))
+        pf.compute()
+        left_clocking = pf.calculate_low_res_range()[0]
+
+        pf.ydata = workspace.dataY(0)[right_min: -1]
+        pf.xdata = np.arange(len(pf.ydata))
+        pf.compute()
+        right_clocking = pf.calculate_low_res_range()[1]
+
+        return [left_clocking, right_clocking]
+
+
+AlgorithmFactory.subscribe(LRPeakSelection)
diff --git a/docs/source/algorithms/LRDirectBeamSort-v1.rst b/docs/source/algorithms/LRDirectBeamSort-v1.rst
new file mode 100644
index 0000000000000000000000000000000000000000..1fa9d3863959e77d94a321add929fd29f35dbb81
--- /dev/null
+++ b/docs/source/algorithms/LRDirectBeamSort-v1.rst
@@ -0,0 +1,17 @@
+.. algorithm::
+
+.. summary::
+
+.. alias::
+
+.. properties::
+
+Description
+-----------
+
+Sort a set of direct beams for the purpose of calculating scaling factors.
+By setting ComputeScalingFactors to True, the scaling factors will be computed.
+
+.. categories::
+
+.. sourcelink::
diff --git a/docs/source/algorithms/LRPeakSelection-v1.rst b/docs/source/algorithms/LRPeakSelection-v1.rst
new file mode 100644
index 0000000000000000000000000000000000000000..c295738b6f502dd6fa58f24de7417a44b428dbdf
--- /dev/null
+++ b/docs/source/algorithms/LRPeakSelection-v1.rst
@@ -0,0 +1,17 @@
+.. algorithm::
+
+.. summary::
+
+.. alias::
+
+.. properties::
+
+Description
+-----------
+
+Determine various types of peak for reflectivity.
+Those include specular peaks and low-resolution direction signal range.
+
+.. categories::
+
+.. sourcelink::
diff --git a/docs/source/algorithms/LRReflectivityOutput-v1.rst b/docs/source/algorithms/LRReflectivityOutput-v1.rst
new file mode 100644
index 0000000000000000000000000000000000000000..dd826d0041a663301f254b4b339fe993016c6f98
--- /dev/null
+++ b/docs/source/algorithms/LRReflectivityOutput-v1.rst
@@ -0,0 +1,17 @@
+.. algorithm::
+
+.. summary::
+
+.. alias::
+
+.. properties::
+
+Description
+-----------
+
+Produces a single reflectivity curve from multiple reflectivity ranges. The algorithm can scale the specular plateau to unity.
+It will also write out the Q resolution.
+
+.. categories::
+
+.. sourcelink::