diff --git a/Code/Mantid/scripts/test/SANSCentreFinderTest.py b/Code/Mantid/scripts/test/SANSCentreFinderTest.py
new file mode 100644
index 0000000000000000000000000000000000000000..955269e543e76db6e80caa6662490bc25ef45188
--- /dev/null
+++ b/Code/Mantid/scripts/test/SANSCentreFinderTest.py
@@ -0,0 +1,270 @@
+import unittest
+import mantid
+from mantid.simpleapi import *
+import centre_finder as cf
+import ISISCommandInterface as command_iface
+from reducer_singleton import ReductionSingleton
+import isis_reduction_steps as reduction_steps
+
+
+class SANSBeamCentrePositionUpdater(unittest.TestCase):
+    def test_that_find_ALL_produces_correct_increment(self):
+        # Arrange
+        fac = cf.BeamCentrePositionUpdaterFactory()
+        position_updater = fac.create_beam_centre_position_updater(cf.FindDirectionEnum.ALL)
+        x = 1.0
+        y = 2.0
+        x_step = 0.1
+        y_step = 0.2
+
+        # Act
+        x_new, y_new = position_updater.increment_position(x, y, x_step, y_step)
+
+        # Assert
+        x_expected = 1.1
+        y_expected = 2.2
+        self.assertEqual(x_expected, x_new, "The x value should have been incremented.")
+        self.assertEqual(y_expected, y_new, "The y value should have been incremented.")
+
+    def test_that_find_LEFTRIGHT_produces_correct_increment(self):
+        # Arrange
+        fac = cf.BeamCentrePositionUpdaterFactory()
+        position_updater = fac.create_beam_centre_position_updater(cf.FindDirectionEnum.LEFT_RIGHT)
+        x = 1.0
+        y = 2.0
+        x_step = 0.1
+        y_step = 0.2
+
+        # Act
+        x_new, y_new = position_updater.increment_position(x, y, x_step, y_step)
+
+        # Assert
+        x_expected = 1.1
+        y_expected = 2.0
+        self.assertEqual(x_expected, x_new, "The x value should have been incremented.")
+        self.assertEqual(y_expected, y_new, "The y value should have been incremented.")
+
+    def test_that_find_UPPDOWN_produces_correct_increment(self):
+        # Arrange
+        fac = cf.BeamCentrePositionUpdaterFactory()
+        position_updater = fac.create_beam_centre_position_updater(cf.FindDirectionEnum.UP_DOWN)
+        x = 1.0
+        y = 2.0
+        x_step = 0.1
+        y_step = 0.2
+
+        # Act
+        x_new, y_new = position_updater.increment_position(x, y, x_step, y_step)
+
+        # Assert
+        x_expected = 1.0
+        y_expected = 2.2
+        self.assertEqual(x_expected, x_new, "The x value should have been incremented.")
+        self.assertEqual(y_expected, y_new, "The y value should have been incremented.")
+
+class TestPositionProvider(unittest.TestCase):
+    workspace_name = 'dummy_ws'
+
+    class MockSample(object):
+        '''
+        Mocking out the sample
+        '''
+        def __init__(self, ws_name):
+            super(TestPositionProvider.MockSample,self).__init__()
+            self.wksp_name = ws_name
+        def get_wksp_name(self):
+            return self.wksp_name
+
+    def _provide_reducer(self, is_larmor, is_new = True):
+        '''
+        Provide a reducer with either Larmor or non-Larmor. If we have Larmor,
+        then we want to be able to set the run number as well
+        '''
+        command_iface.Clean()
+        if is_larmor and is_new:
+            command_iface.LARMOR()
+            CreateSampleWorkspace(OutputWorkspace=self.workspace_name)
+            AddSampleLog(Workspace=self.workspace_name,LogName='run_number', LogText='3000', LogType='Number')
+            sample = self.MockSample(self.workspace_name)
+            ReductionSingleton()._sample_run = sample
+            return ReductionSingleton()
+
+        elif is_larmor and not is_new:
+            command_iface.LARMOR()
+            CreateSampleWorkspace(OutputWorkspace=self.workspace_name)
+            AddSampleLog(Workspace=self.workspace_name,LogName='run_number', LogText='1000', LogType='Number')
+            sample = self.MockSample(self.workspace_name)
+            ReductionSingleton()._sample_run = sample
+            return ReductionSingleton()
+        else:
+            command_iface.LOQ()
+            return ReductionSingleton()
+
+    def _clean_up(self, workspace_name):
+        if workspace_name in mtd.getObjectNames():
+            mtd.remove(workspace_name)
+
+    def test_that_XY_increment_provider_is_created_for_non_larmor(self):
+        # Arrange
+        increment_coord1 = 1
+        increment_coord2 = 2
+        tolerance = 200
+        is_larmor = False
+        reducer = self._provide_reducer(is_larmor)
+        # Act
+        factory = cf.PositionProviderFactory(increment_coord1 = increment_coord1,
+                                             increment_coord2 = increment_coord2,
+                                             tolerance = tolerance,
+                                             position_type = cf.FindDirectionEnum.ALL)
+        provider = factory.create_position_provider(reducer = reducer)
+        # Asssert
+        self.assertTrue(isinstance(provider, cf.PositionProviderXY), "Should create a XY increment provider")
+        # Clean up
+        self._clean_up(self.workspace_name)
+
+    def test_that_YAngle_increment_provider_is_created_for_larmor(self):
+        # Arrange
+        increment_coord1 = 1
+        increment_coord2 = 2
+        tolerance = 200
+        is_larmor = True
+        is_new = True
+        reducer = self._provide_reducer(is_larmor, is_new)
+        # Act
+        factory = cf.PositionProviderFactory(increment_coord1 = increment_coord1,
+                                              increment_coord2 = increment_coord2,
+                                              tolerance = tolerance,
+                                              position_type = cf.FindDirectionEnum.ALL)
+        provider = factory.create_position_provider(reducer = reducer)
+
+        # Asssert
+        self.assertTrue(isinstance(provider, cf.PositionProviderAngleY), "Should create a AngleY increment provider")
+        # Clean up
+        self._clean_up(self.workspace_name)
+
+    def test_that_XY_increment_provider_is_created_for_old_larmor(self):
+        # Arrange
+        increment_coord1 = 1
+        increment_coord2 = 2
+        tolerance = 200
+        is_larmor = True
+        is_new = False
+        reducer = self._provide_reducer(is_larmor, is_new)
+        # Act
+        factory = cf.PositionProviderFactory(increment_coord1 = increment_coord1,
+                                              increment_coord2 = increment_coord2,
+                                              tolerance = tolerance,
+                                              position_type = cf.FindDirectionEnum.ALL)
+        provider = factory.create_position_provider(reducer = reducer)
+
+        # Asssert
+        self.assertTrue(isinstance(provider, cf.PositionProviderXY), "Should create a XY increment provider")
+        # Clean up
+        self._clean_up(self.workspace_name)
+
+    def test_that_XY_increment_provider_halves_the_step(self):
+        # Arrange
+        increment_coord1 = 1
+        increment_coord2 = 2
+        tolerance = 200
+        provider = cf.PositionProviderXY(increment_coord1,increment_coord2,tolerance)
+
+        # Act and Assert
+        self.assertTrue(increment_coord1 == provider.get_increment_coord1())
+        self.assertTrue(increment_coord2 == provider.get_increment_coord2())
+
+        provider.half_and_reverse_increment_coord1()
+        provider.half_and_reverse_increment_coord2()
+
+        self.assertTrue(-increment_coord1/2.0 == provider.get_increment_coord1())
+        self.assertTrue(-increment_coord2/2.0 == provider.get_increment_coord2())
+
+    def test_that_AngleY_increment_provider_halves_the_step(self):
+        # Arrange
+        increment_coord1 = 1
+        increment_coord2 = 2
+        tolerance = 200
+        tolerance_angle = 33
+        bench_rotation = 1
+        coord1_scale_factor = 1
+        provider = cf.PositionProviderAngleY(increment_coord1,increment_coord2,tolerance,tolerance_angle, bench_rotation, coord1_scale_factor)
+
+        # Act and Assert
+        self.assertTrue(increment_coord1 == provider.get_increment_coord1())
+        self.assertTrue(increment_coord2 == provider.get_increment_coord2())
+
+        provider.half_and_reverse_increment_coord1()
+        provider.half_and_reverse_increment_coord2()
+
+        self.assertTrue(-increment_coord1/2.0 == provider.get_increment_coord1())
+        self.assertTrue(-increment_coord2/2.0 == provider.get_increment_coord2())
+
+    def test_that_XY_increment_is_smaller_than_tolerance(self):
+        # Arrange
+        increment_coord1 = 1
+        increment_coord2 = 2
+        tolerance = 200
+        provider = cf.PositionProviderXY(increment_coord1,increment_coord2,tolerance)
+
+        # Act
+        is_smaller_coord1 = provider.is_coord1_increment_smaller_than_tolerance()
+        is_smaller_coord2 = provider.is_coord2_increment_smaller_than_tolerance()
+
+        # Assert
+        self.assertTrue(is_smaller_coord1)
+        self.assertTrue(is_smaller_coord2)
+
+    def test_that_XY_increment_is_larger_than_tolerance(self):
+        # Arrange
+        increment_coord1 = 1
+        increment_coord2 = 2
+        tolerance = 0.2
+        provider = cf.PositionProviderXY(increment_coord1,increment_coord2,tolerance)
+
+        # Act
+        is_smaller_coord1 = provider.is_coord1_increment_smaller_than_tolerance()
+        is_smaller_coord2 = provider.is_coord2_increment_smaller_than_tolerance()
+
+        # Assert
+        self.assertFalse(is_smaller_coord1)
+        self.assertFalse(is_smaller_coord2)
+
+    def test_that_AngleY_increment_is_smaller_than_tolerance(self):
+        # Arrange
+        increment_coord1 = 1
+        increment_coord2 = 2
+        tolerance = 100
+        tolerance_angle = 233
+        bench_rotation = 1
+        coord1_scale_factor = 1
+        provider = cf.PositionProviderAngleY(increment_coord1,increment_coord2,tolerance,tolerance_angle, bench_rotation, coord1_scale_factor)
+
+        # Act
+        is_smaller_coord1 = provider.is_coord1_increment_smaller_than_tolerance()
+        is_smaller_coord2 = provider.is_coord2_increment_smaller_than_tolerance()
+
+        # Assert
+        self.assertTrue(is_smaller_coord1)
+        self.assertTrue(is_smaller_coord2)
+
+    def test_that_AngleY_increment_is_larger_than_tolerance(self):
+        # Arrange
+        increment_coord1 = 1
+        increment_coord2 = 2
+        tolerance = 0.2
+        tolerance_angle = 0.1
+        bench_rotation = 1
+        coord1_scale_factor = 1
+        provider = cf.PositionProviderAngleY(increment_coord1,increment_coord2,tolerance,tolerance_angle, bench_rotation, coord1_scale_factor)
+
+        # Act
+        is_smaller_coord1 = provider.is_coord1_increment_smaller_than_tolerance()
+        is_smaller_coord2 = provider.is_coord2_increment_smaller_than_tolerance()
+
+        # Assert
+        self.assertFalse(is_smaller_coord1)
+        self.assertFalse(is_smaller_coord2)
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/SANSRunWindow.h b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/SANSRunWindow.h
index 432717a89f94aab0aa22890cd4e5e4bb8958ae20..0dc60514e8c56492f6a263d1a0f58d40ac7aeb7c 100644
--- a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/SANSRunWindow.h
+++ b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/SANSRunWindow.h
@@ -306,6 +306,10 @@ private slots:
   void onTransmissionRadiusCheckboxChanged();
   /// Transmission setting for ROI files
   void onTransmissionROIFilesCheckboxChanged();
+  /// React to change in Left/Right checkbox
+  void onLeftRightCheckboxChanged();
+  /// React to change in Up/Down checkbox
+  void onUpDownCheckboxChanged();
 
 private:
   /// used to specify the range of validation to do
@@ -449,8 +453,12 @@ private:
   void checkWaveLengthAndQValues(bool &isValid, QString &message,
                                  QLineEdit *min, QLineEdit *max,
                                  QComboBox *selection, QString type);
+  /// Update the beam center fields
+  void updateBeamCenterCoordinates();
   /// LOQ specific settings
   void applyLOQSettings(bool isNowLOQ);
+  /// Set the beam finder details
+  void setBeamFinderDetails();
 
   UserSubWindow *slicingWindow;
 };
diff --git a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/SANSRunWindow.ui b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/SANSRunWindow.ui
index 8aabf49ddde2a81d1f1236a03cfc878865273fcb..984a9246bc607f29b47f881614a2fd3cec300f03 100644
--- a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/SANSRunWindow.ui
+++ b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/SANSRunWindow.ui
@@ -3201,9 +3201,9 @@ p, li { white-space: pre-wrap; }
           <property name="checkable">
            <bool>false</bool>
           </property>
-          <layout class="QVBoxLayout" name="verticalLayout_12" stretch="1,1,2">
+          <layout class="QVBoxLayout" name="verticalLayout_12" stretch="0,1,2">
            <item>
-            <widget class="QGroupBox" name="groupBox_12">
+            <widget class="QGroupBox" name="beam_centre_finder_groupbox">
              <property name="title">
               <string>Current ( x , y ) [mm]</string>
              </property>
@@ -3271,6 +3271,37 @@ p, li { white-space: pre-wrap; }
                 </property>
                </widget>
               </item>
+              <item row="1" column="3">
+               <layout class="QHBoxLayout" name="horizontalLayout_24">
+                <item>
+                 <widget class="QCheckBox" name="left_right_checkbox">
+                  <property name="text">
+                   <string>Left/Right</string>
+                  </property>
+                  <property name="checked">
+                   <bool>true</bool>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <widget class="QCheckBox" name="up_down_checkbox">
+                  <property name="text">
+                   <string>Up/Down</string>
+                  </property>
+                  <property name="checked">
+                   <bool>true</bool>
+                  </property>
+                 </widget>
+                </item>
+               </layout>
+              </item>
+              <item row="0" column="3">
+               <widget class="QLabel" name="label_16">
+                <property name="text">
+                 <string>Find Beam Centre for:</string>
+                </property>
+               </widget>
+              </item>
              </layout>
              <zorder>rear_beam_y</zorder>
              <zorder>rear_radio</zorder>
@@ -3278,6 +3309,7 @@ p, li { white-space: pre-wrap; }
              <zorder>front_beam_x</zorder>
              <zorder>front_radio</zorder>
              <zorder>rear_beam_x</zorder>
+             <zorder>label_16</zorder>
             </widget>
            </item>
            <item>
diff --git a/MantidQt/CustomInterfaces/src/SANSRunWindow.cpp b/MantidQt/CustomInterfaces/src/SANSRunWindow.cpp
index 34f19ca07dfc7dcb706b601a631f14e895888ee7..fdb9b58f4dec0cb0c1b326bdd302e3c0b6d53e45 100644
--- a/MantidQt/CustomInterfaces/src/SANSRunWindow.cpp
+++ b/MantidQt/CustomInterfaces/src/SANSRunWindow.cpp
@@ -244,6 +244,8 @@ void SANSRunWindow::initLayout() {
   m_uiForm.centre_logging->attachLoggingChannel();
   connect(m_uiForm.clear_centre_log, SIGNAL(clicked()), m_uiForm.centre_logging,
           SLOT(clear()));
+  connect(m_uiForm.up_down_checkbox, SIGNAL(stateChanged(int)), this, SLOT(onUpDownCheckboxChanged()));
+  connect(m_uiForm.left_right_checkbox, SIGNAL(stateChanged(int)), this, SLOT(onLeftRightCheckboxChanged()));
 
   // Create the widget hash maps
   initWidgetMaps();
@@ -422,7 +424,7 @@ void SANSRunWindow::setupSaveBox() {
             SLOT(enableOrDisableDefaultSave()));
   }
 }
-/** Raises a saveWorkspaces dialog which allows people to save any workspace or
+/** Raises a saveWorkspaces dialog which allows people to save any workspace 
 *  workspaces the user chooses
 */
 void SANSRunWindow::saveWorkspacesDialog() {
@@ -969,10 +971,10 @@ bool SANSRunWindow::loadUserFile() {
   m_uiForm.smpl_offset->setText(QString::number(dbl_param * unit_conv));
 
   // Centre coordinates
-  // from the ticket #5942 both detectors have center coordinates
-  dbl_param =
-      runReduceScriptFunction(
-          "print i.ReductionSingleton().get_beam_center('rear')[0]").toDouble();
+  // Update the beam centre coordinates
+  updateBeamCenterCoordinates();
+  // Set the beam finder specific settings
+  setBeamFinderDetails();
   // get the scale factor1 for the beam centre to scale it correctly
   double dbl_paramsf =
       runReduceScriptFunction(
@@ -997,7 +999,6 @@ bool SANSRunWindow::loadUserFile() {
                   "print i.ReductionSingleton().get_beam_center('front')[1]")
                   .toDouble();
   m_uiForm.front_beam_y->setText(QString::number(dbl_param * 1000.0));
-
   // Gravity switch
   QString param =
       runReduceScriptFunction("print i.ReductionSingleton().to_Q.get_gravity()")
@@ -2096,8 +2097,14 @@ bool SANSRunWindow::handleLoadButtonClick() {
   m_sample_file = sample;
   setProcessingState(Ready);
   m_uiForm.load_dataBtn->setText("Load Data");
+
+  // Update the beam center position
+  updateBeamCenterCoordinates();
+  // Set the beam finder specific settings
+  setBeamFinderDetails();
   return true;
 }
+
 /** Queries the number of periods from the Python object whose name was passed
 *  @param RunStep name of the RunStep Python object
 *  @param output where the number will be displayed
@@ -2721,6 +2728,8 @@ void SANSRunWindow::handleRunFindCentre() {
   if (m_uiForm.front_radio->isChecked())
     py_code += "i.SetDetectorFloodFile('')\n";
 
+  // We need to load the FinDirectionEnum class
+  py_code += "from centre_finder import FindDirectionEnum as FindDirectionEnum \n";
   // Find centre function
   py_code += "i.FindBeamCentre(rlow=" + m_uiForm.beam_rmin->text() + ",rupp=" +
              m_uiForm.beam_rmax->text() + ",MaxIter=" +
@@ -2747,9 +2756,21 @@ void SANSRunWindow::handleRunFindCentre() {
     setProcessingState(Ready);
     return;
   }
-  py_code += ", tolerance=" + QString::number(tolerance) + ")";
+  py_code += ", tolerance=" + QString::number(tolerance); 
+
+  // Set which part of the beam centre finder should be used
+  auto updownIsRequired = m_uiForm.up_down_checkbox->isChecked();
+  auto leftRightIsRequired = m_uiForm.left_right_checkbox->isChecked();
+  if (updownIsRequired && leftRightIsRequired) {
+    py_code += ", find_direction=FindDirectionEnum.ALL";
+  } else if (updownIsRequired) {
+    py_code += ", find_direction=FindDirectionEnum.UP_DOWN";
+  } else if (leftRightIsRequired) {
+    py_code += ", find_direction=FindDirectionEnum.LEFT_RIGHT";
+  }
+  py_code += ")";
 
-  g_centreFinderLog.notice("Iteration 1\n");
+  g_centreFinderLog.notice("Beam Centre Finder Start\n");
   m_uiForm.beamstart_box->setFocus();
 
   // Execute the code
@@ -3996,6 +4017,29 @@ void SANSRunWindow::setM3M4Logic(TransSettings setting, bool isNowChecked) {
   this->m_uiForm.trans_roi_files_checkbox->setChecked(false);
 }
 
+/**
+ * React to changes of the Up/Down checkbox
+ */
+void SANSRunWindow::onUpDownCheckboxChanged() {
+  auto checked = m_uiForm.up_down_checkbox->isChecked();
+  if (m_uiForm.rear_radio->isChecked()) {
+    m_uiForm.rear_beam_y->setEnabled(checked);
+  } else {
+    m_uiForm.front_beam_y->setEnabled(checked);
+  }
+}
+
+/**
+ * React to changes of the Left/Right checkbox
+ */
+void SANSRunWindow::onLeftRightCheckboxChanged() {
+  auto checked = m_uiForm.left_right_checkbox->isChecked();
+  if (m_uiForm.rear_radio->isChecked()) {
+    m_uiForm.rear_beam_x->setEnabled(checked);
+  } else {
+    m_uiForm.front_beam_x->setEnabled(checked);
+  }
+}
 /**
  * Set beam stop logic for Radius, ROI and Mask
  * @param setting :: the checked item
@@ -4405,5 +4449,63 @@ void SANSRunWindow::checkWaveLengthAndQValues(bool &isValid, QString &message,
   }
 }
 
+/**
+ *  Update the beam centre coordinates
+ */
+void SANSRunWindow::updateBeamCenterCoordinates() {
+  // Centre coordinates
+  // from the ticket #5942 both detectors have center coordinates
+  double dbl_param =
+      runReduceScriptFunction(
+          "print i.ReductionSingleton().get_beam_center('rear')[0]")
+          .toDouble();
+  // get the scale factor1 for the beam centre to scale it correctly
+  double dbl_paramsf =
+      runReduceScriptFunction(
+          "print i.ReductionSingleton().get_beam_center_scale_factor1()")
+          .toDouble();
+  m_uiForm.rear_beam_x->setText(QString::number(dbl_param * dbl_paramsf));
+  // get scale factor2 for the beam centre to scale it correctly
+  dbl_paramsf =
+      runReduceScriptFunction(
+          "print i.ReductionSingleton().get_beam_center_scale_factor2()")
+          .toDouble();
+  dbl_param = runReduceScriptFunction(
+                  "print i.ReductionSingleton().get_beam_center('rear')[1]")
+                  .toDouble();
+  m_uiForm.rear_beam_y->setText(QString::number(dbl_param * dbl_paramsf));
+  // front
+  dbl_param = runReduceScriptFunction(
+                  "print i.ReductionSingleton().get_beam_center('front')[0]")
+                  .toDouble();
+  m_uiForm.front_beam_x->setText(QString::number(dbl_param * 1000.0));
+  dbl_param = runReduceScriptFunction(
+                  "print i.ReductionSingleton().get_beam_center('front')[1]")
+                  .toDouble();
+  m_uiForm.front_beam_y->setText(QString::number(dbl_param * 1000.0));
+}
+
+/**
+ * Set the beam finder details
+ */
+void SANSRunWindow::setBeamFinderDetails() {
+  // The instrument name
+  auto instrumentName = m_uiForm.inst_opt->currentText();
+
+  // Set the labels according to the instrument
+  auto requiresAngle = runReduceScriptFunction(
+                           "print i.is_current_workspace_an_angle_workspace()")
+                           .simplified();
+  QString labelPosition;
+  if (requiresAngle == m_constants.getPythonTrueKeyword()) {
+    labelPosition = "Current ( " + QString(QChar(0x03B2)) + " , y ) [";
+    labelPosition.append(QChar(0xb0));
+    labelPosition += ",mm]";
+  } else {
+    labelPosition = "Current ( x , y ) [mm,mm]";
+  }
+  m_uiForm.beam_centre_finder_groupbox->setTitle(labelPosition);
+}
+
 } // namespace CustomInterfaces
 } // namespace MantidQt
diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt
index 7cd45300f74869139a60cf613343c6613fe322d6..ed23cdbc4dca37bcb9b589337e3e278249f466e8 100644
--- a/scripts/CMakeLists.txt
+++ b/scripts/CMakeLists.txt
@@ -31,6 +31,7 @@ set ( TEST_PY_FILES
       test/RunDescriptorTest.py
       test/SansIsisGuiSettings.py
       test/SANSBatchModeTest.py
+      test/SANSCentreFinderTest.py
       test/SANSCommandInterfaceTest.py
       test/SANSUtilityTest.py
       test/SANSIsisInstrumentTest.py
diff --git a/scripts/SANS/ISISCommandInterface.py b/scripts/SANS/ISISCommandInterface.py
index 3f06b3229bf67e2a871806a1a62459c5db70707b..41ab1de5f8bb008e1a871655be6858323e7b6f33 100644
--- a/scripts/SANS/ISISCommandInterface.py
+++ b/scripts/SANS/ISISCommandInterface.py
@@ -11,7 +11,7 @@ sanslog = Logger("SANS")
 
 import isis_reduction_steps
 import isis_reducer
-from centre_finder import CentreFinder as CentreFinder
+from centre_finder import *
 #import SANSReduction
 from mantid.simpleapi import *
 from mantid.api import WorkspaceGroup
@@ -340,7 +340,8 @@ def WavRangeReduction(wav_start=None, wav_end=None, full_trans_wav=None, name_su
 
         @param wav_start: the first wavelength to be in the output data
         @param wav_end: the last wavelength in the output data
-        @param full_trans_wav: if to use a wide wavelength range, the instrument's default wavelength range, for the transmission correction, false by default
+        @param full_trans_wav: if to use a wide wavelength range, the instrument's default wavelength range,
+                               for the transmission correction, false by default
         @param name_suffix: append the created output workspace with this
         @param combineDet: combineDet can be one of the following:
                            'rear'                (run one reduction for the 'rear' detector data)
@@ -350,7 +351,8 @@ def WavRangeReduction(wav_start=None, wav_end=None, full_trans_wav=None, name_su
                             None                 (run one reduction for whatever detector has been set as the current detector
                                                   before running this method. If front apply rescale+shift)
         @param resetSetup: if true reset setup at the end
-        @param out_fit_settings: An output parameter. It is used, specially when resetSetup is True, in order to remember the 'scale and fit' of the fitting algorithm.
+        @param out_fit_settings: An output parameter. It is used, specially when resetSetup is True, in order to remember the
+                                 'scale and fit' of the fitting algorithm.
         @return Name of one of the workspaces created
     """
     _printMessage('WavRangeReduction(' + str(wav_start) + ', ' + str(wav_end) + ', '+str(full_trans_wav)+')')
@@ -674,7 +676,8 @@ def _WavRangeReduction(name_suffix=None):
     def _common_substring(val1, val2):
         l = []
         for i in range(len(val1)):
-            if val1[i]==val2[i]: l.append(val1[i])
+            if val1[i]==val2[i]:
+                l.append(val1[i])
             else:
                 return ''.join(l)
 
@@ -878,9 +881,11 @@ def SetDetectorOffsets(bank, x, y, z, rot, radius, side, xtilt=0.0, ytilt=0.0 ):
     detector.y_tilt = ytilt
 
 def SetCorrectionFile(bank, filename):
-    # 10/03/15 RKH, create a new routine that allows change of "direct beam file" = correction file, for a given
-    # detector, this simplify the iterative process used to adjust it. Will still have to keep changing the name of the file
-    # for each iteratiom to avoid Mantid using a cached version, but can then use only a single user (=mask) file for each set of iterations.
+    # 10/03/15 RKH, create a new routine that allows change of "direct beam file" = correction file,
+    # for a given detector, this simplify the iterative process used to adjust it.
+    # Will still have to keep changing the name of the file
+    # for each iteratiom to avoid Mantid using a cached version, but can then use
+    # only a single user (=mask) file for each set of iterations.
     # Modelled this on SetDetectorOffsets above ...
     """
         @param bank: Must be either 'front' or 'rear' (not case sensitive)
@@ -905,8 +910,11 @@ def LimitsR(rmin, rmax, quiet=False, reducer=None):
 def LimitsWav(lmin, lmax, step, bin_type):
     _printMessage('LimitsWav(' + str(lmin) + ', ' + str(lmax) + ', ' + str(step) + ', '  + bin_type + ')')
 
-    if  bin_type.upper().strip() == 'LINEAR': bin_type = 'LIN'
-    if  bin_type.upper().strip() == 'LOGARITHMIC': bin_type = 'LOG'
+    if  bin_type.upper().strip() == 'LINEAR':
+        bin_type = 'LIN'
+    if  bin_type.upper().strip() == 'LOGARITHMIC':
+        bin_type = 'LOG'
+
     if bin_type == 'LOG':
         bin_sym = '-'
     else:
@@ -1061,7 +1069,7 @@ def createColetteScript(inputdata, format, reduced, centreit , plotresults, csvf
 
     return script
 
-def FindBeamCentre(rlow, rupp, MaxIter = 10, xstart = None, ystart = None, tolerance=1.251e-4):
+def FindBeamCentre(rlow, rupp, MaxIter = 10, xstart = None, ystart = None, tolerance=1.251e-4, find_direction = FindDirectionEnum.ALL):
     """
         Estimates the location of the effective beam centre given a good initial estimate. For more
         information go to this page
@@ -1069,20 +1077,27 @@ def FindBeamCentre(rlow, rupp, MaxIter = 10, xstart = None, ystart = None, toler
         @param rlow: mask around the (estimated) centre to this radius (in millimetres)
         @param rupp: don't include further out than this distance (mm) from the centre point
         @param MaxInter: don't calculate more than this number of iterations (default = 10)
-        @param xstart: initial guess for the horizontal distance of the beam centre from the
-                        detector centre in meters (default the values in the mask file)
-        @param ystart: initial guess for the distance of the beam centre from the detector
-                       centre vertically in metres (default the values in the mask file)
-    @param tolerance: define the precision of the search. If the step is smaller than the tolerance,
-                      it will be considered stop searching the centre (default=1.251e-4 or 1.251um)
+        @param xstart: initial guess for the horizontal distance of the beam centre
+                       from the detector centre in meters (default the values in the mask file), or in the
+                       case of rotated instruments a rotation about the y axis. The unit is degree/XSF
+        @param ystart: initial guess for the distance of the beam centre from the detector centre
+                       vertically in metres (default the values in the mask file)
+        @param tolerance: define the precision of the search. If the step is smaller than the
+                          tolerance, it will be considered stop searching the centre (default=1.251e-4 or 1.251um)
+        @param find_only: if only Up/Down or only Left/Right is
+                          required then variable is set to
         @return: the best guess for the beam centre point
     """
-    XSTEP = ReductionSingleton().inst.cen_find_step
-    YSTEP = ReductionSingleton().inst.cen_find_step2
+    COORD1STEP = ReductionSingleton().inst.cen_find_step
+    COORD2STEP = ReductionSingleton().inst.cen_find_step2
 
     XSF = ReductionSingleton().inst.beam_centre_scale_factor1
     YSF = ReductionSingleton().inst.beam_centre_scale_factor2
+    coord1_scale_factor = XSF
+    coord2_scale_factor = YSF
 
+    # Here we have to be careful as the original position can be either in [m, m] or [degree, m], we need to make sure
+    # that we are consistent to not mix with [degree/XSF, m]
     original = ReductionSingleton().get_instrument().cur_detector_position(ReductionSingleton().get_sample().get_wksp_name())
 
     if ReductionSingleton().instrument.lowAngDetSet:
@@ -1096,43 +1111,61 @@ def FindBeamCentre(rlow, rupp, MaxIter = 10, xstart = None, ystart = None, toler
             float(xstart), float(ystart)),det_bank)
 
     beamcoords = ReductionSingleton().get_beam_center()
-    XNEW = beamcoords[0]
-    YNEW = beamcoords[1]
-    xstart = beamcoords[0]
-    ystart = beamcoords[1]
-
 
     #remove this if we know running the Reducer() doesn't change i.e. all execute() methods are const
     centre_reduction = copy.deepcopy(ReductionSingleton().reference())
     LimitsR(str(float(rlow)), str(float(rupp)), quiet=True, reducer=centre_reduction)
 
-    centre = CentreFinder(original)
-    centre.logger.notice("xstart,ystart="+str(XNEW*1000.)+" "+str(YNEW*1000.))
-    centre.logger.notice("Starting centre finding routine ...")
-    #this function moves the detector to the beam center positions defined above and
+    # Create an object which handles the positions and increments
+    centre_positioner = CentrePositioner(reducer = centre_reduction,
+                                         position_type = find_direction,
+                                         coord1_start = beamcoords[0],
+                                         coord2_start = beamcoords[1],
+                                         coord1_step = COORD1STEP,
+                                         coord2_step = COORD2STEP,
+                                         tolerance = tolerance)
+
+    # Produce the initial position
+    COORD1NEW, COORD2NEW = centre_positioner.produce_initial_position()
+
+    # Set the CentreFinder
+    sign_policy = centre_positioner.produce_sign_policy()
+    centre = CentreFinder(original, sign_policy, find_direction)
+
+    # Produce a logger for this the Beam Centre Finder
+    beam_center_logger = BeamCenterLogger(centre_reduction,
+                                          coord1_scale_factor,
+                                          coord2_scale_factor)
+
+    # If we have 0 iterations then we should return here
+    if MaxIter <= 0:
+        zero_iterations_msg = ("You have selected 0 iterations. The beam centre" +
+                               "will be positioned at (" + str(xstart) + ", " + str(ystart) +")")
+        beam_center_logger.report(zero_iterations_msg)
+        return xstart, ystart
+
+    beam_center_logger.report_init(COORD1NEW, COORD2NEW)
+
+    # this function moves the detector to the beam center positions defined above and
     # returns an estimate of where the beam center is relative to the new center
-    resX_old, resY_old = centre.SeekCentre(centre_reduction, [XNEW, YNEW])
+    resCoord1_old, resCoord2_old = centre.SeekCentre(centre_reduction, [COORD1NEW, COORD2NEW])
     centre_reduction = copy.deepcopy(ReductionSingleton().reference())
     LimitsR(str(float(rlow)), str(float(rupp)), quiet=True, reducer=centre_reduction)
-
-    logger.notice(centre.status_str(0, resX_old, resY_old))
-
+    beam_center_logger.report_status(0, original[0], original[1], resCoord1_old, resCoord2_old)
     # take first trial step
-    XNEW = xstart + XSTEP
-    YNEW = ystart + YSTEP
+    COORD1NEW, COORD2NEW = centre_positioner.increment_position(COORD1NEW, COORD2NEW)
     graph_handle = None
     it = 0
     for i in range(1, MaxIter+1):
         it = i
-
         centre_reduction.set_beam_finder(
-            isis_reduction_steps.BaseBeamFinder(XNEW, YNEW), det_bank)
+            isis_reduction_steps.BaseBeamFinder(COORD1NEW, COORD2NEW), det_bank)
+        resCoord1, resCoord2 = centre.SeekCentre(centre_reduction, [COORD1NEW, COORD2NEW])
 
-        resX, resY = centre.SeekCentre(centre_reduction, [XNEW, YNEW])
         centre_reduction = copy.deepcopy(ReductionSingleton().reference())
         LimitsR(str(float(rlow)), str(float(rupp)), quiet=True, reducer=centre_reduction)
 
-        centre.logger.notice(centre.status_str(it, resX, resY))
+        beam_center_logger.report_status(it, COORD1NEW, COORD2NEW, resCoord1, resCoord2)
 
         if mantidplot:
             try :
@@ -1140,37 +1173,38 @@ def FindBeamCentre(rlow, rupp, MaxIter = 10, xstart = None, ystart = None, toler
                     #once we have a plot it will be updated automatically when the workspaces are updated
                     graph_handle = mantidplot.plotSpectrum(centre.QUADS, 0)
                 graph_handle.activeLayer().setTitle(\
-                        centre.status_str(it, resX, resY))
+                         beam_center_logger.get_status_message(it, COORD1NEW, COORD2NEW, resCoord1, resCoord2))
             except :
                 #if plotting is not available it probably means we are running outside a GUI, in which case do everything but don't plot
                 pass
-
         #have we stepped across the y-axis that goes through the beam center?
-        if resX > resX_old:
+        if resCoord1 > resCoord1_old:
             # yes with stepped across the middle, reverse direction and half the step size
-            XSTEP = -XSTEP/2.
-        if resY > resY_old:
-            YSTEP = -YSTEP/2.
-        if abs(XSTEP) < tolerance and abs(YSTEP) < tolerance :
+            centre_positioner.set_new_increment_coord1()
+        if resCoord2 > resCoord2_old:
+            centre_positioner.set_new_increment_coord2()
+        if (centre_positioner.is_increment_coord1_smaller_than_tolerance() and
+            centre_positioner.is_increment_coord2_smaller_than_tolerance()):
             # this is the success criteria, we've close enough to the center
-            centre.logger.notice("Converged - check if stuck in local minimum!")
+            beam_center_logger.report("Converged - check if stuck in local minimum!")
             break
 
-        resX_old = resX
-        resY_old = resY
-        XNEW += XSTEP
-        YNEW += YSTEP
+        resCoord1_old = resCoord1
+        resCoord2_old = resCoord2
 
-    if it == MaxIter:
-        centre.logger.notice("Out of iterations, new coordinates may not be the best!")
-        XNEW -= XSTEP
-        YNEW -= YSTEP
+        if it != MaxIter:
+            COORD1NEW, COORD2NEW = centre_positioner.increment_position(COORD1NEW, COORD2NEW)
+        else:
+            beam_center_logger.report("Out of iterations, new coordinates may not be the best!")
+
+    # Create the appropriate return values
+    coord1_centre, coord2_centre = centre_positioner.produce_final_position(COORD1NEW, COORD2NEW)
 
     ReductionSingleton().set_beam_finder(
-        isis_reduction_steps.BaseBeamFinder(XNEW, YNEW), det_bank)
-    centre.logger.notice("Centre coordinates updated: [" + str(XNEW*XSF) + ", " + str(YNEW*YSF) + ']')
+        isis_reduction_steps.BaseBeamFinder(coord1_centre, coord2_centre), det_bank)
+    beam_center_logger.report_final(coord1_centre, coord2_centre)
 
-    return XNEW, YNEW
+    return coord1_centre, coord2_centre
 
 
 ###################### Utility functions ####################################################
@@ -1408,6 +1442,19 @@ def AddRuns(runs, instrument ='sans2d', saveAsEvent=False, binning = "Monitors",
              isOverlay = isOverlay,
              time_shifts = time_shifts)
 
+def is_current_workspace_an_angle_workspace():
+    '''
+    Queries if the current workspace, stored in the reducer is a workspace
+    which uses [angle, pos] to denote its location
+    @returns true if it is an angle workspace else false
+    '''
+    is_angle = False
+    # pylint: disable=bare-except
+    try:
+        is_angle = is_workspace_which_requires_angle(reducer = ReductionSingleton())
+    except:
+        is_angle = False
+    return is_angle
 ###############################################################################
 ######################### Start of Deprecated Code ############################
 ###############################################################################
diff --git a/scripts/SANS/centre_finder.py b/scripts/SANS/centre_finder.py
index b179ee8328d451445d135384b4818756b3e47ab3..db86f5be5c480e821de4adb636a74f7908b855b4 100644
--- a/scripts/SANS/centre_finder.py
+++ b/scripts/SANS/centre_finder.py
@@ -1,10 +1,53 @@
 #pylint: disable=invalid-name
-import isis_reducer
 from isis_reduction_steps import StripEndNans
+from isis_instrument import LARMOR
 from mantid.simpleapi import *
 from mantid.kernel import Logger
 import SANSUtility
 
+class FindDirectionEnum(object): #pylint: disable=R0903
+    ALL = 0
+    UP_DOWN = 1
+    LEFT_RIGHT=2
+
+    def __init__(self):
+        super(FindDirectionEnum,self).__init__()
+
+
+def is_workspace_which_requires_angle(reducer):
+    '''
+    Check if the sample worksapce requires the first
+    coordinate to be an angle
+    @param reducer: the reducer object
+    @returns true if the workspace requires an angle otherwise false
+    '''
+    instrument_name = reducer.instrument.name()
+    if instrument_name != LARMOR._NAME:
+        return False
+    workspace_name = reducer.get_sample().wksp_name
+    if workspace_name:
+        ws_ref = mtd[workspace_name]
+        return LARMOR.is_run_new_style_run(ws_ref)
+    return False
+
+
+
+def get_bench_rotation(reducer):
+    '''
+    Extract the bench rotation from the instrument
+    of the reducer
+    @param reducer: the reducer object
+    @returns the bench rotation in degrees
+    '''
+    bench_rotation = 0.0
+    # pylint: disable=bare-except
+    try:
+        ws = mtd[str(reducer.get_sample().get_wksp_name())]
+        bench_rotation = reducer.instrument.getDetValues(ws)[0]
+    except:
+        bench_rotation = 0.0
+    return bench_rotation
+
 class CentreFinder(object):
     """
         Aids estimating the effective centre of the particle beam by calculating Q in four
@@ -12,17 +55,26 @@ class CentreFinder(object):
         better estimate for the beam centre position can hence be calculated iteratively
     """
     QUADS = ['Left', 'Right', 'Up', 'Down']
-    def __init__(self, guess_centre):
+    def __init__(self, guess_centre, sign_policy, find_direction = FindDirectionEnum.ALL):
         """
             Takes a loaded reducer (sample information etc.) and the initial guess of the centre
             position that are required for all later iterations
             @param guess_centre: the starting position that the trial x and y are relative to
+            @param sign_policy: sets the sign for the move operation.
+            @param find_direction: Find beam centre for directions, ie if all or only up/down
+                                   or only left right
         """
         self.logger = Logger("CentreFinder")
         self._last_pos = guess_centre
         self.detector = None
-        self.XSF = 1.0
-        self.YSF = 1.0
+        self.coord1_scale_factor = 1.0
+        self.coord2_scale_factor = 1.0
+        self.find_direction = find_direction
+        self.sign_coord1 = -1.
+        self.sign_coord2 = -1.
+        if sign_policy is not None and len(sign_policy) == 2:
+            self.sign_coord1 = sign_policy[0]
+            self.sign_coord2 = sign_policy[1]
 
     def SeekCentre(self, setup, trial):
         """
@@ -32,13 +84,13 @@ class CentreFinder(object):
             @param trial: the coordinates of the location to test as a list in the form [x, y]
             @return: the asymmetry in the calculated Q in the x and y directions
         """
-
         self.detector = setup.instrument.cur_detector().name()
 
         # populate the x and y scale factor values at this point for the text box
-        self.XSF = setup.instrument.beam_centre_scale_factor1
-        self.YSF = setup.instrument.beam_centre_scale_factor2
+        self.coord1_scale_factor = setup.instrument.beam_centre_scale_factor1
+        self.coord2_scale_factor = setup.instrument.beam_centre_scale_factor2
 
+         # We are looking only at the differnce between the old position and the trial.
         self.move(setup, trial[0]-self._last_pos[0], trial[1]-self._last_pos[1])
 
         #phi masking will remove areas of the detector that we need
@@ -50,13 +102,13 @@ class CentreFinder(object):
         steps = steps[0:len(steps)-1]
         setup._reduce(init=False, post=False, steps=steps)
 
-        self._group_into_quadrants(setup, 'centre', trial[0], trial[1], suffix='_tmp')
+        self._group_into_quadrants(setup, 'centre', suffix='_tmp')
 
         if setup.get_can():
             #reduce the can here
             setup.reduce_can('centre_can', run_Q=False)
 
-            self._group_into_quadrants(setup, 'centre_can', trial[0], trial[1], suffix='_can')
+            self._group_into_quadrants(setup, 'centre_can', suffix='_can')
             Minus(LHSWorkspace='Left_tmp',RHSWorkspace= 'Left_can',OutputWorkspace= 'Left_tmp')
             Minus(LHSWorkspace='Right_tmp',RHSWorkspace= 'Right_can',OutputWorkspace= 'Right_tmp')
             Minus(LHSWorkspace='Up_tmp',RHSWorkspace= 'Up_can',OutputWorkspace= 'Up_tmp')
@@ -70,7 +122,8 @@ class CentreFinder(object):
         DeleteWorkspace(Workspace='centre')
         self._last_pos = trial
 
-        #prepare the workspaces for "publication", after they have their standard names calculations will be done on them and they will be plotted
+        # prepare the workspaces for "publication", after they have their
+        # standard names calculations will be done on them and they will be plotted
         for out_wksp in self.QUADS:
             in_wksp = out_wksp+'_tmp'
             ReplaceSpecialValues(InputWorkspace=in_wksp,OutputWorkspace=in_wksp,NaNValue=0,InfinityValue=0)
@@ -81,21 +134,6 @@ class CentreFinder(object):
 
         return self._calculate_residue()
 
-    def status_str(self, iter, x_res, y_res):
-        """
-            Creates a human readble string from the numbers passed to it
-            @param iter: iteration number
-            @param x_res: asymmetry in the x direction
-            @param y_res: asymmetry in y
-            @return: a human readable string
-        """
-
-        x_str = str(self._last_pos[0] * self.XSF).ljust(10)[0:9]
-        y_str = str(self._last_pos[1] * self.YSF).ljust(10)[0:9]
-        x_res = '    SX='+str(x_res).ljust(7)[0:6]
-        y_res = '    SY='+str(y_res).ljust(7)[0:6]
-        return 'Itr '+str(iter)+':  ('+x_str+',  '+y_str+')'+x_res+y_res
-
     def move(self, setup, x, y):
         """
             Move the selected detector in both the can and sample workspaces, remembering the
@@ -104,19 +142,34 @@ class CentreFinder(object):
             @param x: the distance to move in the x (-x) direction in metres
             @param y: the distance to move in the y (-y) direction in metres
         """
-        x = -x
-        y = -y
-        MoveInstrumentComponent(Workspace=setup.get_sample().wksp_name,\
-            ComponentName=self.detector, X=x, Y=y, RelativePosition=True)
+        # Displacing the beam by +5 is equivalent to displacing the isntrument by -5. Hence we change
+        # the sign here. LARMOR does this correction in the instrument itself, while for the others
+        # we don't
+        x = self.sign_coord1*x
+        y = self.sign_coord2*y
+
+        setup.instrument.elementary_displacement_of_single_component(workspace=setup.get_sample().wksp_name,
+                                                                     component_name=self.detector,
+                                                                     coord1 = x,
+                                                                     coord2 = y,
+                                                                     coord1_scale_factor = 1.,
+                                                                     coord2_scale_factor = 1.,
+                                                                     relative_displacement = True)
         if setup.get_can():
-            MoveInstrumentComponent(Workspace=setup.get_can().wksp_name,\
-                ComponentName=self.detector, X=x, Y=y, RelativePosition=True)
+            setup.instrument.elementary_displacement_of_single_component(workspace=setup.get_can().wksp_name,
+                                                                         component_name=self.detector,
+                                                                         coord1 = x,
+                                                                         coord2 = y,
+                                                                         coord1_scale_factor = 1.,
+                                                                         coord2_scale_factor = 1.,
+                                                                         relative_displacement = True)
 
     # Create a workspace with a quadrant value in it
-    def _create_quadrant(self, setup, reduced_ws, quadrant, xcentre, ycentre, r_min, r_max, suffix):
+    def _create_quadrant(self, setup, reduced_ws, quadrant, r_min, r_max, suffix):
         out_ws = quadrant+suffix
         # Need to create a copy because we're going to mask 3/4 out and that's a one-way trip
         CloneWorkspace(InputWorkspace=reduced_ws,OutputWorkspace= out_ws)
+
         objxml = SANSUtility.QuadrantXML([0, 0, 0.0], r_min, r_max, quadrant)
         # Mask out everything outside the quadrant of interest
         MaskDetectorsInShape(Workspace=out_ws,ShapeXML= objxml)
@@ -125,12 +178,12 @@ class CentreFinder(object):
         #Q1D(output,rawcount_ws,output,q_bins,AccountForGravity=GRAVITY)
 
     # Create 4 quadrants for the centre finding algorithm and return their names
-    def _group_into_quadrants(self, setup, input, xcentre, ycentre, suffix=''):
+    def _group_into_quadrants(self, setup, input_value, suffix=''):
         r_min = setup.CENT_FIND_RMIN
         r_max = setup.CENT_FIND_RMAX
 
         for q in self.QUADS:
-            self._create_quadrant(setup, input, q, xcentre, ycentre, r_min, r_max, suffix)
+            self._create_quadrant(setup, input_value, q, r_min, r_max, suffix)
 
     def _calculate_residue(self):
         """
@@ -138,46 +191,679 @@ class CentreFinder(object):
             and Down. This assumes that a workspace with one spectrum for each of the quadrants
             @return: difference left to right, difference up down
         """
-        yvalsA = mtd['Left'].readY(0)
-        yvalsB = mtd['Right'].readY(0)
-        qvalsA = mtd['Left'].readX(0)
-        qvalsB = mtd['Right'].readX(0)
-        qrange = [len(yvalsA), len(yvalsB)]
-        nvals = min(qrange)
         residueX = 0
-        indexB = 0
-        for indexA in range(0, nvals):
-            if qvalsA[indexA] < qvalsB[indexB]:
-                self.logger.notice("LR1 "+str(indexA)+" "+str(indexB))
-                continue
-            elif qvalsA[indexA] > qvalsB[indexB]:
-                while qvalsA[indexA] > qvalsB[indexB]:
-                    self.logger.notice("LR2 "+str(indexA)+" "+str(indexB))
-                    indexB += 1
-            if indexA > nvals - 1 or indexB > nvals - 1:
-                break
-            residueX += pow(yvalsA[indexA] - yvalsB[indexB], 2)
-            indexB += 1
+        if self.find_direction == FindDirectionEnum.ALL or self.find_direction == FindDirectionEnum.LEFT_RIGHT:
+            yvalsAX = mtd['Left'].readY(0)
+            yvalsBX = mtd['Right'].readY(0)
+            qvalsAX = mtd['Left'].readX(0)
+            qvalsBX = mtd['Right'].readX(0)
+            qrangeX = [len(yvalsAX), len(yvalsBX)]
+            nvalsX = min(qrangeX)
+            id1X = "LR1"
+            id2X = "LR2"
+            residueX = self._residual_calculation_for_single_direction(yvalsA = yvalsAX,
+                                                                       yvalsB = yvalsBX,
+                                                                       qvalsA = qvalsAX,
+                                                                       qvalsB = qvalsBX,
+                                                                       qrange = qrangeX,
+                                                                       nvals = nvalsX,
+                                                                       id1 = id1X,
+                                                                       id2 = id2X)
 
-        yvalsA = mtd['Up'].readY(0)
-        yvalsB = mtd['Down'].readY(0)
-        qvalsA = mtd['Up'].readX(0)
-        qvalsB = mtd['Down'].readX(0)
-        qrange = [len(yvalsA), len(yvalsB)]
-        nvals = min(qrange)
         residueY = 0
+        if self.find_direction == FindDirectionEnum.ALL or self.find_direction == FindDirectionEnum.UP_DOWN:
+            yvalsAY = mtd['Up'].readY(0)
+            yvalsBY = mtd['Down'].readY(0)
+            qvalsAY = mtd['Up'].readX(0)
+            qvalsBY = mtd['Down'].readX(0)
+            qrangeY = [len(yvalsAY), len(yvalsBY)]
+            nvalsY = min(qrangeY)
+            id1Y = "UD1"
+            id2Y = "UD2"
+            residueY = self._residual_calculation_for_single_direction(yvalsA = yvalsAY,
+                                                                       yvalsB = yvalsBY,
+                                                                       qvalsA = qvalsAY,
+                                                                       qvalsB = qvalsBY,
+                                                                       qrange = qrangeY,
+                                                                       nvals = nvalsY,
+                                                                       id1 = id1Y,
+                                                                       id2 = id2Y)
+        return residueX, residueY
+
+    def _residual_calculation_for_single_direction(self, yvalsA, yvalsB, qvalsA, qvalsB, qrange, nvals, id1, id2):
+        dummy_1 = qrange
+        residue = 0
         indexB = 0
         for indexA in range(0, nvals):
             if qvalsA[indexA] < qvalsB[indexB]:
-                self.logger.notice("UD1 "+str(indexA)+" "+str(indexB))
+                self.logger.notice(id1 + " " +str(indexA)+" "+str(indexB))
                 continue
             elif qvalsA[indexA] > qvalsB[indexB]:
                 while qvalsA[indexA] > qvalsB[indexB]:
-                    self.logger("UD2 "+str(indexA)+" "+str(indexB))
+                    self.logger(id2 + " " +str(indexA)+" "+str(indexB))
                     indexB += 1
             if indexA > nvals - 1 or indexB > nvals - 1:
                 break
-            residueY += pow(yvalsA[indexA] - yvalsB[indexB], 2)
+            residue += pow(yvalsA[indexA] - yvalsB[indexB], 2)
             indexB += 1
+        return residue
 
-        return residueX, residueY
+    def _get_cylinder_direction(self, workspace):
+        '''
+        Get the direction that the masking clyinder needs to point at. This should be the normal
+        of the tilted detector bench. The original normal is along the beam axis as defined in
+        the instrument definition file.
+        @param workspace: the workspace with the tilted detector bench
+        @returns the required direction of the cylinder axis
+        '''
+        ws = mtd[workspace]
+        instrument = ws.getInstrument()
+        quat = instrument.getComponentByName(self.detector).getRotation()
+        cylinder_direction = instrument.getReferenceFrame().vecPointingAlongBeam()
+        quat.rotate(cylinder_direction)
+        return cylinder_direction.X(), cylinder_direction.Y(), cylinder_direction.Z()
+
+
+
+class CentrePositioner(object):
+    '''
+    Handles the positions and increments for beam finding.
+    '''
+    def __init__(self, reducer, position_type, coord1_start, coord2_start,coord1_step,coord2_step, tolerance): #pylint: disable=too-many-arguments
+        '''
+        Set the CentrePositioner. It requires:
+        @param reducer:: The reducer
+        @param position_type: If do a full search or only UP/DOWN or LEFT/RIGHT
+        @param coord1_start: The initial value for the first coordinate
+        @param coord2_start: The initial value for the second coordinate
+        @param coord1_step: The initial step size for the first coordinate
+        @param coord2_step: The initial step size for the second coordinate
+        @param tolerance: The tolerance
+        @param scale_factor_coord1: the scale factor for the first coordinate
+        @param scale_factor_coord2: the scale factor for the second coordinate
+        '''
+        super(CentrePositioner,self).__init__()
+        # Create the appropriate position updater
+        pos_factory = BeamCentrePositionUpdaterFactory()
+        self.position_updater = pos_factory.create_beam_centre_position_updater(position_type)
+
+        # Create the appropriate increment converter
+        position_provider_factory = PositionProviderFactory(coord1_step,coord2_step, tolerance, position_type)
+        self.position_provider = position_provider_factory.create_position_provider(reducer)
+
+        # set the correct units starting coordinate. such that we are dealing with units of
+        # either [m,m] or [degree, m]
+        self.coord1_start = self.position_provider.get_coord1_for_input_with_correct_scaling(coord1_start)
+        self.coord2_start = coord2_start
+
+        self.current_coord1 = self.coord1_start
+        self.current_coord2 = self.coord2_start
+
+
+    def increment_position(self, coord1_old, coord2_old):
+        '''
+        Increment the coordinates
+        @param coord1_old: the first old coordinate
+        @param coord2_old: the seocond old coordinate
+        @returns the incremented coordinates
+        '''
+        coord1_increment = self.position_provider.get_increment_coord1()
+        coord2_increment = self.position_provider.get_increment_coord2()
+        return self.position_updater.increment_position(coord1_old, coord2_old, coord1_increment, coord2_increment)
+
+    def set_new_increment_coord1(self):
+        '''
+        Set the new increment for the first coordinate.
+        '''
+        self.position_provider.half_and_reverse_increment_coord1()
+
+    def set_new_increment_coord2(self):
+        '''
+        Set the new increment for the second coordinate.
+        '''
+        self.position_provider.half_and_reverse_increment_coord2()
+
+    def produce_final_position(self, coord1_new, coord2_new):
+        '''
+        Produce the final coordinates
+        @param coord1_new: the newest version of coordinate 1
+        @param coord2_new: the newest version of coordinate 2
+        @returns the final coordinates
+        '''
+        # We need to make sure that the first coordinate is returned with the correct scaling for the BaseBeamFinder,
+        # ie either [m, m] or [degree/1000, m] also if it might have a bench rotation applied to it
+        coord1 = self.position_provider.get_coord1_for_output_with_correct_scaling_and_offset(coord1_new)
+        return coord1, coord2_new
+
+    def produce_initial_position(self):
+        '''
+        Produce the initial position. This is important especially for the LARMOR
+        case where we can have an additional BENCH Rotation.
+        @returns the initital position for coord1 and coord2
+        '''
+        return self.position_provider.produce_initial_position(self.coord1_start, self.coord2_start)
+
+    def is_increment_coord1_smaller_than_tolerance(self):
+        '''
+        Check if the increment for the first coordinate is smaller than the tolerance
+        @returns True if the increment of the first coordinate is smaller than tolerance else False
+        '''
+        return self.position_provider.is_coord1_increment_smaller_than_tolerance()
+
+    def is_increment_coord2_smaller_than_tolerance(self):
+        '''
+        Check if the increment for the second coordinate is smaller than the tolerance
+        @returns True if the increment of the second coordinate is smaller than tolerance else False
+        '''
+        return self.position_provider.is_coord2_increment_smaller_than_tolerance()
+
+    def produce_sign_policy(self):
+        '''
+        Gets a tuple of sign policies for the translation of the instrument.
+        '''
+        return self.position_provider.provide_sign_policy()
+
+# Thes classes make sure that only the relevant directions are updated
+# They are not instrument dependent, they should only dependt on the user's choice.
+class BeamCentrePositionUpdaterFactory(object): #pylint: disable=R0903
+    '''
+    Creates the required beam centre position updater.
+    '''
+    def __init__(self):
+        super(BeamCentrePositionUpdaterFactory, self).__init__()
+
+    def create_beam_centre_position_updater(self, beam_centre_position_udpater_type):
+        '''
+        Factory method to create the appropriate Beam Centre Position Updater
+        @param beam_centre_position_udpater_type: the type of updater
+        '''
+        if beam_centre_position_udpater_type == FindDirectionEnum.LEFT_RIGHT:
+            return BeamCentrePositionUpdaterLeftRight()
+        elif beam_centre_position_udpater_type == FindDirectionEnum.UP_DOWN:
+            return BeamCentrePositionUpdaterUpDown()
+        elif beam_centre_position_udpater_type == FindDirectionEnum.ALL:
+            return BeamCentrePositionUpdaterAll()
+        else:
+            RuntimeError("Error in BeamCentrePositionUpdaterFactory: You need to provide a position update"
+                         "policy, ie up/down, left/right or all")
+
+class BeamCentrePositionUpdater(object): #pylint: disable=R0903
+    '''
+    Handles the position updates, ie if we are only intereseted in left/right or up/down or all
+    '''
+    def __init__(self):
+        super(BeamCentrePositionUpdater, self).__init__()
+
+    def increment_position(self, coord1_old, coord2_old, coord1_increment, coord2_increment):
+        dummy_1 = coord1_old
+        dummy_2 = coord2_old
+        dummy_3 = coord1_increment
+        dummy_4 = coord2_increment
+        raise RuntimeError("BeamCentrePositionUpdater is not implemented")
+
+
+class BeamCentrePositionUpdaterAll(BeamCentrePositionUpdater):
+    '''
+    Handles the position updates when all directions are being selected
+    '''
+    def __init__(self):
+        super(BeamCentrePositionUpdaterAll, self).__init__()
+
+    def increment_position(self, coord1_old, coord2_old, coord1_increment, coord2_increment):
+        '''
+        Increment the coordinates
+        @param coord1_old: the old first coordinate
+        @param coord2_old: the old second coordinate
+        @param coord1_increment: the increment for the first coordinate
+        @param coord2_increment: the increment for the second coordinate
+        '''
+        return coord1_old + coord1_increment, coord2_old + coord2_increment
+
+
+class BeamCentrePositionUpdaterUpDown(BeamCentrePositionUpdater):
+    '''
+    Handles the position updates when only up/down is selected
+    '''
+    def __init__(self):
+        super(BeamCentrePositionUpdaterUpDown, self).__init__()
+
+    def increment_position(self, coord1_old, coord2_old, coord1_increment, coord2_increment):
+        '''
+        Increment the coordinates.
+        @param coord1_old: the old first coordinate
+        @param coord2_old: the old second coordinate
+        @param coord1_increment: the increment for the first coordinate
+        @param coord2_increment: the increment for the second coordinate
+        @returns the incremented position
+        '''
+        dummy_1 = coord1_increment
+        return coord1_old, coord2_old + coord2_increment
+
+
+class BeamCentrePositionUpdaterLeftRight(BeamCentrePositionUpdater):
+    '''
+    Handles the position updates when only right/left is selected
+    '''
+    def __init__(self):
+        super(BeamCentrePositionUpdaterLeftRight, self).__init__()
+
+    def increment_position(self, coord1_old, coord2_old, coord1_increment, coord2_increment):
+        '''
+        Increment the coordinates.
+        @param coord1_old: the old first coordinate
+        @param coord2_old: the old second coordinate
+        @param coord1_increment: the increment for the first coordinate
+        @param coord2_increment: the increment for the second coordinate
+        @returns the incremented position
+        '''
+        dummy_1 = coord2_increment
+        return coord1_old + coord1_increment, coord2_old
+
+    def produce_final_position(self, x_new, x_initial, y_new, y_initial):
+        '''
+        Produce the final position.
+        @param coord1_new: the new first coordinate
+        @param coord1_initial: the first initial coordinate
+        @param coord2_new: the new second coordinate
+        @param coord2_initial: the second initial coordinate
+        @returns the final position
+        '''
+        dummy_1 = y_new
+        dummy_2 = x_initial
+        return x_new, y_initial
+
+
+
+# Provides the positions and increments with the correct scaling
+# These set of classes are instrument dependent.
+class PositionProviderFactory(object):
+    '''
+    Creates the required increment provider. The increments for the two coordinates
+    depend on the instrument, eg Larmor's first coordinate is an angle for certain
+    run numbers.
+    '''
+    def __init__(self, increment_coord1, increment_coord2, tolerance, position_type):
+        '''
+        Initialize the PositionProviderFactory
+        @param increment_coord1: The increment for the first coordinate
+        @param increment_coord2: The increment for the second coordinate
+        @param tolerance: The tolerance setting
+        @param position_type: The type of the psoitio, ie left/right, up/down or all
+        '''
+        super(PositionProviderFactory,self).__init__()
+        self.increment_coord1 = increment_coord1
+        self.increment_coord2 = increment_coord2
+        self.tolerance = tolerance
+        self.position_type = position_type
+
+    def create_position_provider(self, reducer):
+        '''
+        Factory method for the PositionProvider
+        @param reducer: The reducer object
+        @returns The correct increment provider
+        '''
+        if is_workspace_which_requires_angle(reducer):
+            # The angle increment is currently not specified in the instrument parameters file,
+            # hence we set a value here. This is also true for the tolerance
+            #increment_coord1_angle = self.get_increment_coord1_angle(reducer, self.tolerance)
+            #tolerance_angle = self.get_tolerance_for_angle(self.tolerance, self.increment_coord1, increment_coord1_angle)
+            increment_coord1_angle = self.increment_coord1 /1000. # The tolerance needs to be specified in angles
+            tolerance_angle = self.tolerance
+
+            # Find the bench rotation. Only supply the bench rotation if it is really needed. If we supply an offset
+            # through a bench rotation we need to take into account that the directionality of the angles is not
+            # the same as in Mantid. We need to reverse the sign of the bench rotation
+            coord1_offset = -1*get_bench_rotation(reducer)
+
+            # Get the scale factor for the first coordinate
+            coord1_scale_factor = reducer.get_beam_center_scale_factor1()
+
+            return PositionProviderAngleY(increment_coord1 = increment_coord1_angle,
+                                           increment_coord2 = self.increment_coord2,
+                                           tolerance = self.tolerance,
+                                           tolerance_angle = tolerance_angle,
+                                           coord1_offset = coord1_offset,
+                                           coord1_scale_factor = coord1_scale_factor)
+        else:
+            return PositionProviderXY(increment_coord1 = self.increment_coord1,
+                                      increment_coord2 = self.increment_coord2,
+                                      tolerance = self.tolerance)
+
+    def get_increment_coord1_angle(self, reducer, tolerance):
+        '''
+        Estimate an increment for the angle based on the specified coord1 increment for linear
+        displacements and the distance from the sample to the detector.
+        For a distance D and an increment dx, we estimate dAlpha to be tan(dAlpha)=dx/D.
+        Since D >> dx, we can use Taylor expansion to set dAlpha = dx/D
+        @param reducer: the reducer object
+        @param tolerance: the tolerance
+        '''
+        workspace_name = reducer.get_sample().wksp_name
+        workspace = mtd[workspace_name]
+
+        instrument = workspace.getInstrument()
+        detector_name = reducer.instrument.cur_detector().name()
+
+        sample = instrument.getSample()
+        component = instrument.getComponentByName(detector_name)
+
+        # We use here the first detector entry. Do we need to have this smarter in the future?
+        detector_bench = component[0]
+        distance = detector_bench.getDistance(sample)
+
+        return tolerance/distance
+
+
+    def get_tolerance_for_angle(self, tolerance_linear, increment_linear, increment_angle):
+        '''
+        The tolerance associated with a linear disaplacement is translated into
+        a tolerance for an angle. Tol_Angle = Increment_Angle *(Tol_Linear/Increment_Linear)
+        @param tolerance_linear: the tolerance for the linear displacement
+        @param increment_lienar: the increment of the linear displacement
+        @param increment_angle: the increment of the rotation
+        '''
+        return (increment_angle/increment_linear)*tolerance_linear
+
+class PositionProvider(object):
+    def __init__(self, increment_coord1, increment_coord2, tolerance):
+        super(PositionProvider,self).__init__()
+        dummy_1 = increment_coord1
+        dummy_2 = increment_coord2
+        dummy_3 = tolerance
+
+    def get_coord1_for_input_with_correct_scaling(self, coord1):
+        dummy_coord1 = coord1
+        RuntimeError("The PositionProvider interface is not implemented")
+
+    def get_coord1_for_output_with_correct_scaling_and_offset(self, coord1):
+        dummy_coord1 = coord1
+        RuntimeError("The PositionProvider interface is not implemented")
+
+    def produce_initial_position(self, coord1, coord2):
+        dummy_coord1 = coord1
+        dummy_coord2 = coord2
+        RuntimeError("The PositionProvider interface is not implemented")
+
+    def half_and_reverse_increment_coord1(self):
+        RuntimeError("The PositionProvider interface is not implemented")
+
+    def half_and_reverse_increment_coord2(self):
+        RuntimeError("The PositionProvider interface is not implemented")
+
+    def is_coord1_increment_smaller_than_tolerance(self):
+        RuntimeError("The PositionProvider interface is not implemented")
+
+    def is_coord2_increment_smaller_than_tolerance(self):
+        RuntimeError("The PositionProvider interface is not implemented")
+
+    def get_increment_coord1(self):
+        RuntimeError("The PositionProvider interface is not implemented")
+
+    def get_increment_coord2(self):
+        RuntimeError("The PositionProvider interface is not implemented")
+
+    def check_is_smaller_than_tolerance(self,to_check,tolerance):
+        if abs(to_check) < tolerance:
+            return True
+        else:
+            return False
+
+    def provide_sign_policy(self):
+        RuntimeError("The PositionProvider interface is not implemented")
+
+class PositionProviderXY(PositionProvider):
+    '''
+    Handles the increments for the case when both coordinates are cartesian
+    '''
+    def __init__(self, increment_coord1, increment_coord2, tolerance):
+        super(PositionProviderXY,self).__init__(increment_coord1, increment_coord2, tolerance)
+        self.increment_x = increment_coord1
+        self.increment_y = increment_coord2
+        self.tolerance = tolerance
+        # The sign policy
+        self.sign_policy_x = -1.
+        self.sign_policy_y = -1.
+
+    def get_coord1_for_input_with_correct_scaling(self, coord1):
+        '''
+        Get the coordinate with the correct scaling;
+        in this case it is already correct as we have [m,m]
+        @param coord1: first coordinate in m
+        @returns the first coordinate in m
+        '''
+        return coord1
+
+    def get_coord1_for_output_with_correct_scaling_and_offset(self, coord1):
+        '''
+        Get the coordinate with the correct scaling
+        @param coord1: first coordinate in m
+        @returns the first coordinate in m
+        '''
+        return coord1
+
+    def produce_initial_position(self, coord1, coord2):
+        return coord1, coord2
+
+    def half_and_reverse_increment_coord1(self):
+        '''
+        Halves the step size and reverses the step direction
+        '''
+        self.increment_x = -self.increment_x/2.0
+
+    def half_and_reverse_increment_coord2(self):
+        '''
+        Halves the step size and reverses the step direction
+        '''
+        self.increment_y = -self.increment_y/2.0
+
+    def is_coord1_increment_smaller_than_tolerance(self):
+        '''
+        Check if the increment for the first coordinate is smaller than the tolerance
+        @returns True if the increment is smaller than the tolerance, otherwise false
+        '''
+        return self.check_is_smaller_than_tolerance(self.increment_x, self.tolerance)
+
+    def is_coord2_increment_smaller_than_tolerance(self):
+        '''
+        Check if the increment for the second coordinate is smaller than the tolerance
+        @returns True if the increment is smaller than the tolerance, otherwise false
+        '''
+        return self.check_is_smaller_than_tolerance(self.increment_y, self.tolerance)
+
+    def get_increment_coord1(self):
+        return self.increment_x
+
+    def get_increment_coord2(self):
+        return self.increment_y
+
+    def provide_sign_policy(self):
+        '''
+        Get the sign policy for the x, y translations. Displacing the beam by 5mm
+        is equivalent to displacing the instrument by -5mm, hence we need the
+        minus sign here.
+        '''
+        return self.sign_policy_x, self.sign_policy_y
+
+class PositionProviderAngleY(PositionProvider):
+    '''
+    Handles the increments for the case when the first coordinate is an angle
+    and the second is a cartesian coordinate
+    '''
+    def __init__(self, increment_coord1, increment_coord2, tolerance, tolerance_angle, coord1_offset, coord1_scale_factor):  #pylint: disable=too-many-arguments
+        super(PositionProviderAngleY,self).__init__(increment_coord1, increment_coord2, tolerance)
+        self.increment_angle = increment_coord1
+        self.increment_y = increment_coord2
+        self.tolerance = tolerance
+        self.tolerance_angle = tolerance_angle
+        self.coord1_offset = coord1_offset
+        self.coord1_scale_factor = coord1_scale_factor
+        # The sign policy
+        self.sign_policy_angle = 1.
+        self.sign_policy_y = -1.
+
+
+    def get_coord1_for_input_with_correct_scaling(self, coord1):
+        '''
+        Get the coordinate with the correct scaling
+        @param coord1: first coordinate in degree/COORD1SF
+        @returns the first coordinate in degree
+        '''
+        return coord1*self.coord1_scale_factor
+
+    def get_coord1_for_output_with_correct_scaling_and_offset(self, coord1):
+        '''
+        Get the coordinate with the correct scaling
+        @param coord1: first coordinate in degree
+        @returns the first coordinate in degree/COORD1SF
+        '''
+        # At this point we want to take out the offset, hence we need to substract it.
+        return (coord1 - self.coord1_offset)/self.coord1_scale_factor
+
+    def produce_initial_position(self, coord1, coord2):
+        '''
+        The initial position might require a correction for the bench rotation.
+        '''
+        return coord1 + self.coord1_offset, coord2
+
+    def half_and_reverse_increment_coord1(self):
+        '''
+        Halves the step size and reverses the step direction
+        '''
+        self.increment_angle = -self.increment_angle/2.0
+
+    def half_and_reverse_increment_coord2(self):
+        '''
+        Halves the step size and reverses the step direction
+        '''
+        self.increment_y = -self.increment_y/2.0
+
+    def is_coord1_increment_smaller_than_tolerance(self):
+        '''
+        Check if the increment for the first coordinate is smaller than the tolerance
+        @returns True if the increment is smaller than the tolerance, otherwise false
+        '''
+        return self.check_is_smaller_than_tolerance(self.increment_angle,self.tolerance_angle)
+
+    def is_coord2_increment_smaller_than_tolerance(self):
+        '''
+        Check if the increment for the second coordinate is smaller than the tolerance
+        @returns True if the increment is smaller than the tolerance, otherwise false
+        '''
+        return self.check_is_smaller_than_tolerance(self.increment_y,self.tolerance)
+
+    def get_increment_coord1(self):
+        return self.increment_angle*self.coord1_scale_factor
+
+    def get_increment_coord2(self):
+        return self.increment_y
+
+    def provide_sign_policy(self):
+        '''
+        Get the sign policy for the angle, y translations. Displacing the beam by 5mm
+        is equivalent to displacing the instrument by -5mm. The angle displacement in
+        LARMOR does the sign switch already. Hence we have a positve sign policy for
+        the angle direction
+        '''
+        return self.sign_policy_angle, self.sign_policy_y
+
+
+# The classes below provide a logging facility for the Beam Centre Finder mechanism
+class BeamCenterLogger(object):
+    '''
+    Logger during the beam centre operation. The logging will
+    depend partially on the type of the first coordinate, ie [m, m] or [degree, m].
+    It will also perform a correction for potential offsets like bench rotations.
+    '''
+    def __init__(self, reducer, coord1_scale_factor, coord2_scale_factor):
+        super(BeamCenterLogger, self).__init__()
+        self.logger = Logger("CentreFinder")
+        self.using_angle = False
+        if is_workspace_which_requires_angle(reducer):
+            self.coord1_scale_factor = 1.
+            self.using_angle = True
+            # Find the bench rotation. Only supply the bench rotation if it is really needed. If we supply an offset
+            # through a bench rotation we need to take into account that the directionality of the angles is not
+            # the same as in Mantid. We need to reverse the sign of the bench rotation to get the correct rotation.
+            self.offset_coord1 = -1*get_bench_rotation(reducer)
+        else:
+            self.coord1_scale_factor = coord1_scale_factor
+            self.offset_coord1 = 0.0
+
+        self.coord2_scale_factor = coord2_scale_factor
+        self.offset_coord2 = 0.0
+
+    def report_init(self, coord1, coord2):
+        '''
+        Report the initial setup
+        @param coord1: the first coordinate
+        @param coord2: the second coordinate
+        '''
+        if self.using_angle:
+            initial_msg = "beta_start"
+        else:
+            initial_msg = "x_start"
+        # We need to substract the offset from the coordinate, since we do not want to display the offset
+        # which is on the data
+        val1 = (coord1 - self.offset_coord1)*self.coord1_scale_factor
+        val2 = (coord2 - self.offset_coord2)*self.coord2_scale_factor
+
+        msg = initial_msg + ",ystart= %s    %s" % (str(val1), str(val2))
+        self.logger.notice(msg)
+        self.logger.notice("Starting centre finding routine ...")
+
+    def report_status(self, iteration, coord1, coord2, resid1, resid2):  #pylint: disable=too-many-arguments
+        '''
+        Report the status of a beam finder iteration
+        @param iteration: the number of the iteration
+        @param coord1: the first coordinate
+        @param coord2: the second coordinate
+        @param resid1: the residual of the first coordinate
+        @param resid2: the residual of the second coordinate
+        '''
+        msg = self.get_status_message(iteration, coord1, coord2, resid1, resid2)
+        self.logger.notice(msg)
+
+    def get_status_message(self, iteration, coord1, coord2, resid1, resid2):  #pylint: disable=too-many-arguments
+        '''
+        Report the status of a beam finder iteration
+        @param iteration: the number of the iteration
+        @param coord1: the first coordinate
+        @param coord2: the second coordinate
+        @param resid1: the residual of the first coordinate
+        @param resid2: the residual of the second coordinate
+        '''
+        # We need to substract the offset from the coordinate, since we do not want to display the offset
+        # which is on the data
+        val1 = (coord1 - self.offset_coord1)* self.coord1_scale_factor
+        val2 = (coord2 - self.offset_coord2)* self.coord2_scale_factor
+        coord1str = str(val1).ljust(10)[0:9]
+        coord2str = str(val2).ljust(10)[0:9]
+        res1str = str(resid1).ljust(7)[0:6]
+        res2str = str(resid2).ljust(7)[0:6]
+        msg = "Itr %i: (%s,   %s)    SX=%s   SY=%s" %(iteration, coord1str, coord2str, res1str, res2str)
+        return msg
+
+    def report(self, msg):
+        '''
+        Report a general message
+        @param msg: the message to report
+        '''
+        self.logger.notice(msg)
+
+    def report_final(self, coord1, coord2):
+        '''
+        Report the final coordinates which are set in the reducer
+        @param coord1: the first coordinate
+        @param coord2: the second coordinate
+        '''
+        # We shouldn't need an offset correction at this point as a possible.
+        # Also we need to multiply both entries with the same (1000) scaling,
+        # Because the first coordinate should have been corrected before
+        # being passed into this method. For reporting purposes we revert this
+        # correction. Also we don't need to remove the offset, since it the input
+        # already has it removed.
+        general_scale = self.coord2_scale_factor
+        val1 = (coord1)*general_scale
+        val2 = (coord2)*general_scale
+        msg = "Centre coordinates updated: [ %f,  %f ]" %(val1, val2)
+        self.logger.notice(msg)
diff --git a/scripts/SANS/isis_instrument.py b/scripts/SANS/isis_instrument.py
index 821b956195403a29bfb2b308d0a83d29286235c9..7cbaff03481cbfafcb4b570dd99e3c7286527f70 100644
--- a/scripts/SANS/isis_instrument.py
+++ b/scripts/SANS/isis_instrument.py
@@ -507,6 +507,9 @@ class ISISInstrument(BaseInstrument):
         self.monitor_zs = {}
         # Used when new calibration required.
         self._newCalibrationWS = None
+        # Centre of beam after a move has been applied, 
+        self.beam_centre_pos1_after_move = 0.0
+        self.beam_centre_pos2_after_move = 0.0
 
     def get_incident_mon(self):
         """
@@ -712,8 +715,35 @@ class ISISInstrument(BaseInstrument):
         """Define how to move the bank to position beamX and beamY must be implemented"""
         raise RuntimeError("Not Implemented")
 
+    def elementary_displacement_of_single_component(self, workspace, component_name, coord1, coord2,
+                                                    coord1_scale_factor = 1., coord2_scale_factor = 1.,relative_displacement = True):
+        """
+        A simple elementary displacement of a single component.
+        This provides the adequate displacement for finding the beam centre.
+        @param workspace: the workspace which needs to have the move applied to it
+        @param component_name: the name of the component which being displaced
+        @param coord1: the first coordinate, which is x here
+        @param coord2: the second coordinate, which is y here
+        @param coord1_scale_factor: scale factor for the first coordinate
+        @param coord2_scale_factor: scale factor for the second coordinate
+        @param relative_displacement: If the the displacement is to be relative (it normally should be)
+        """
+        dummy_1 = workspace
+        dummy_2 = component_name
+        dummy_3 = coord1
+        dummy_3 = coord2
+        dummy_4 = relative_displacement
+        dummy_5 = coord1_scale_factor
+        dummy_6 = coord2_scale_factor
+        raise RuntimeError("Not Implemented")
+
     def cur_detector_position(self, ws_name):
-        """Return the position of the center of the detector bank"""
+        '''
+        Return the position of the center of the detector bank
+        @param ws_name: the input workspace name
+        @raise RuntimeError: Not implemented
+        '''
+        dummy_1 = ws_name
         raise RuntimeError("Not Implemented")
 
     def on_load_sample(self, ws_name, beamcentre, isSample):
@@ -736,7 +766,8 @@ class ISISInstrument(BaseInstrument):
             self.changeCalibration(ws_name)
 
         # centralize the bank to the centre
-        self.move_components(ws_name, beamcentre[0], beamcentre[1])
+        dummy_centre, centre_shift = self.move_components(ws_name, beamcentre[0], beamcentre[1])
+        return centre_shift
 
     def load_transmission_inst(self, ws_trans, ws_direct, beamcentre):
         """
@@ -755,6 +786,11 @@ class ISISInstrument(BaseInstrument):
         # this forces us to have 'copyable' objects.
         self._newCalibrationWS = str(ws_reference)
 
+    def get_updated_beam_centre_after_move(self):
+        '''
+        @returns the beam centre position after the instrument has moved
+        '''
+        return self.beam_centre_pos1_after_move, self.beam_centre_pos2_after_move
 
 
 class LOQ(ISISInstrument):
@@ -790,7 +826,9 @@ class LOQ(ISISInstrument):
 
         xshift = (317.5/1000.) - xbeam
         yshift = (317.5/1000.) - ybeam
-        MoveInstrumentComponent(Workspace=ws,ComponentName= self.cur_detector().name(), X = xshift, Y = yshift, RelativePosition="1")
+        MoveInstrumentComponent(Workspace=ws,
+                                ComponentName= self.cur_detector().name(),
+                                X = xshift, Y = yshift, RelativePosition="1")
 
         # Have a separate move for x_corr, y_coor and z_coor just to make it more obvious in the
         # history, and to expert users what is going on
@@ -800,8 +838,31 @@ class LOQ(ISISInstrument):
             xshift = xshift + det.x_corr/1000.0
             yshift = yshift + det.y_corr/1000.0
 
+        # Set the beam centre position afte the move, leave as they were
+        self.beam_centre_pos1_after_move = xbeam
+        self.beam_centre_pos2_after_move = ybeam
+
         return [xshift, yshift], [xshift, yshift]
 
+    def elementary_displacement_of_single_component(self, workspace, component_name, coord1, coord2,
+                                                    coord1_scale_factor = 1., coord2_scale_factor = 1.,relative_displacement = True):
+        """
+        A simple elementary displacement of a single component.
+        This provides the adequate displacement for finding the beam centre.
+        @param workspace: the workspace which needs to have the move applied to it
+        @param component_name: the name of the component which being displaced
+        @param coord1: the first coordinate, which is x here
+        @param coord2: the second coordinate, which is y here
+        @param coord1_scale_factor: scale factor for the first coordinate
+        @param coord2_scale_factor: scale factor for the second coordinate
+        @param relative_displacement: If the the displacement is to be relative (it normally should be)
+        """
+        MoveInstrumentComponent(Workspace=workspace,
+                                ComponentName=component_name,
+                                X=coord1,
+                                Y=coord2,
+                                RelativePosition=relative_displacement)
+
     def get_marked_dets(self):
         raise NotImplementedError('The marked detector list isn\'t stored for instrument '+self._NAME)
 
@@ -939,44 +1000,59 @@ class SANS2D(ISISInstrument):
 
         # Deal with front detector
         # 10/03/15 RKH need to add tilt of detector, in degrees, with respect to the horizontal or vertical of the detector plane
-        # this time we can rotate about the detector's own axis so can use RotateInstrumentComponent, ytilt rotates about x axis, xtilt rotates about z axis
+        # this time we can rotate about the detector's own axis so can use RotateInstrumentComponent,
+        # ytilt rotates about x axis, xtilt rotates about z axis
         #
         if frontDet.y_tilt != 0.0:
-            RotateInstrumentComponent(Workspace=ws,ComponentName= self.getDetector('front').name(), X = "1.", Y = "0.", Z = "0.", Angle = frontDet.y_tilt)
+            RotateInstrumentComponent(Workspace=ws,
+                                      ComponentName= self.getDetector('front').name(),
+                                      X = "1.",
+                                      Y = "0.",
+                                      Z = "0.",
+                                      Angle = frontDet.y_tilt)
         if frontDet.x_tilt != 0.0:
-            RotateInstrumentComponent(Workspace=ws,ComponentName= self.getDetector('front').name(), X = "0.", Y = "0.", Z = "1.", Angle = frontDet.x_tilt)
+            RotateInstrumentComponent(Workspace=ws,
+                                      ComponentName= self.getDetector('front').name(),
+                                      X = "0.",
+                                      Y = "0.",
+                                      Z = "1.",
+                                      Angle = frontDet.x_tilt)
         #
         # 9/1/12  this all dates to Richard Heenan & Russell Taylor's original python development for SANS2d
-    	# the rotation axis on the SANS2d front detector is actually set front_det_radius = 306mm behind the detector.
-    	# Since RotateInstrumentComponent will only rotate about the centre of the detector, we have to to the rest here.
+        # the rotation axis on the SANS2d front detector is actually set front_det_radius = 306mm behind the detector.
+        # Since RotateInstrumentComponent will only rotate about the centre of the detector, we have to to the rest here.
         # rotate front detector according to value in log file and correction value provided in user file
         rotateDet = (-FRONT_DET_ROT - frontDet.rot_corr)
         RotateInstrumentComponent(Workspace=ws,ComponentName= self.getDetector('front').name(), X="0.", Y="1.0", Z="0.", Angle=rotateDet)
         RotRadians = math.pi*(FRONT_DET_ROT + frontDet.rot_corr)/180.
         # The rear detector is translated to the beam position using the beam centre coordinates in the user file.
-    	# (Note that the X encoder values in NOT used for the rear detector.)
-    	# The front detector is translated using the difference in X encoder values, with a correction from the user file.
-    	# 21/3/12 RKH [In reality only the DIFFERENCE in X encoders is used, having separate X corrections for both detectors is unnecessary,
-    	# but we will continue with this as it makes the mask file smore logical and avoids a retrospective change.]
-    	# 21/3/12 RKH add .side_corr    allows rotation axis of the front detector being offset from the detector X=0
-    	# this inserts *(1.0-math.cos(RotRadians)) into xshift, and
-    	# - frontDet.side_corr*math.sin(RotRadians) into zshift.
-    	# (Note we may yet need to introduce further corrections for parallax errors in the detectors, which may be wavelength dependent!)
+        # (Note that the X encoder values in NOT used for the rear detector.)
+        # The front detector is translated using the difference in X encoder values, with a correction from the user file.
+        # 21/3/12 RKH [In reality only the DIFFERENCE in X encoders is used, having separate X corrections for
+        # both detectors is unnecessary,
+        # but we will continue with this as it makes the mask file smore logical and avoids a retrospective change.]
+        # 21/3/12 RKH add .side_corr    allows rotation axis of the front detector being offset from the detector X=0
+        # this inserts *(1.0-math.cos(RotRadians)) into xshift, and
+        # - frontDet.side_corr*math.sin(RotRadians) into zshift.
+        # (Note we may yet need to introduce further corrections for parallax errors in the detectors, which may be wavelength dependent!)
         xshift = (REAR_DET_X + rearDet.x_corr -frontDet.x_corr - FRONT_DET_X  -frontDet.side_corr*(1-math.cos(RotRadians)) + (self.FRONT_DET_RADIUS +frontDet.radius_corr)*math.sin(RotRadians) )/1000. - self.FRONT_DET_DEFAULT_X_M - xbeam
         yshift = (frontDet.y_corr/1000.  - ybeam)
-        # Note don't understand the comment below (9/1/12 these are comments from the original python code, you may remove them if you like!)
+        # Note don't understand the comment below (9/1/12 these are comments from the original python code,
+        # you may remove them if you like!)
         # default in instrument description is 23.281m - 4.000m from sample at 19,281m !
         # need to add ~58mm to det1 to get to centre of detector, before it is rotated.
-    	# 21/3/12 RKH add .radius_corr
+        # 21/3/12 RKH add .radius_corr
         zshift = (FRONT_DET_Z + frontDet.z_corr + (self.FRONT_DET_RADIUS +frontDet.radius_corr)*(1 - math.cos(RotRadians)) - frontDet.side_corr*math.sin(RotRadians))/1000.
         zshift -= self.FRONT_DET_DEFAULT_SD_M
-        MoveInstrumentComponent(Workspace=ws,ComponentName= self.getDetector('front').name(), X = xshift, Y = yshift, Z = zshift, RelativePosition="1")
-
+        MoveInstrumentComponent(Workspace=ws,
+                                ComponentName= self.getDetector('front').name(),
+                                X = xshift, Y = yshift, Z = zshift, RelativePosition="1")
 
         # deal with rear detector
 
         # 10/03/15 RKH need to add tilt of detector, in degrees, with respect to the horizontal or vertical of the detector plane
-        # Best to do the tilts first, while the detector is still centred on the z axis, ytilt rotates about x axis, xtilt rotates about z axis
+        # Best to do the tilts first, while the detector is still centred on the z axis,
+        # ytilt rotates about x axis, xtilt rotates about z axis
         # NOTE the beam centre coordinates may change
         if rearDet.y_tilt != 0.0:
             RotateInstrumentComponent(Workspace=ws,ComponentName= rearDet.name(), X = "1.", Y = "0.", Z = "0.", Angle = rearDet.y_tilt)
@@ -990,9 +1066,7 @@ class SANS2D(ISISInstrument):
         sanslog.notice("Setup move "+str(xshift*1000.)+" "+str(yshift*1000.)+" "+str(zshift*1000.))
         MoveInstrumentComponent(Workspace=ws,ComponentName= rearDet.name(), X = xshift, Y = yshift, Z = zshift, RelativePosition="1")
 
-
         self.move_all_components(ws)
-
         #this implements the TRANS/TRANSPEC=4/SHIFT=... line, this overrides any other monitor move
         if self.monitor_4_offset:
             #get the current location of the monitor
@@ -1020,8 +1094,31 @@ class SANS2D(ISISInstrument):
             beam_cen = [0.0,0.0]
             det_cen = [-xbeam, -ybeam]
 
+        # Set the beam centre position afte the move, leave as they were
+        self.beam_centre_pos1_after_move = xbeam
+        self.beam_centre_pos2_after_move = ybeam
+
         return beam_cen, det_cen
 
+    def elementary_displacement_of_single_component(self, workspace, component_name, coord1, coord2,
+                                                    coord1_scale_factor = 1., coord2_scale_factor = 1.,relative_displacement = True):
+        """
+        A simple elementary displacement of a single component.
+        This provides the adequate displacement for finding the beam centre.
+        @param workspace: the workspace which needs to have the move applied to it
+        @param component_name: the name of the component which being displaced
+        @param coord1: the first coordinate, which is x here
+        @param coord2: the second coordinate, which is y here
+        @param coord1_scale_factor: scale factor for the first coordinate
+        @param coord2_scale_factor: scale factor for the second coordinate
+        @param relative_displacement: If the the displacement is to be relative (it normally should be)
+        """
+        MoveInstrumentComponent(Workspace=workspace,
+                                ComponentName=component_name,
+                                X=coord1,
+                                Y=coord2,
+                                RelativePosition=relative_displacement)
+
     def get_detector_log(self, wksp):
         """
             Reads information about the state of the instrument on the information
@@ -1387,28 +1484,75 @@ class LARMOR(ISISInstrument):
         zshift = 0
         sanslog.notice("Setup move " + str(xshift*XSF) + " " + str(yshift*YSF) + " " + str(zshift*1000))
         MoveInstrumentComponent(ws, ComponentName=detBench.name(), X=xshift, Y=yshift, Z=zshift)
+
+        # Deal with the angle value
+        total_x_shift = self._rotate_around_y_axis(workspace = ws,
+                                               component_name = detBench.name(),
+                                               x_beam = xbeam,
+                                               x_scale_factor = XSF,
+                                               bench_rotation = BENCH_ROT)
+
+        # Set the beam centre position afte the move
+        self.beam_centre_pos1_after_move = xbeam # Need to provide the angle in 1000th of a degree
+        self.beam_centre_pos2_after_move = ybeam
+
+        # beam centre, translation, new beam position
+        return [0.0, 0.0], [-xbeam, -ybeam]
+
+    def elementary_displacement_of_single_component(self, workspace, component_name, coord1, coord2,
+                                                    coord1_scale_factor = 1., coord2_scale_factor = 1.,relative_displacement = True):
+        """
+        A simple elementary displacement of a single component.
+        This provides the adequate displacement for finding the beam centre.
+        @param workspace: the workspace which needs to have the move applied to it
+        @param component_name: the name of the component which being displaced
+        @param coord1: the first coordinate, which is x here
+        @param coord2: the second coordinate, which is y here
+        @param coord1_scale_factor: scale factor for the first coordinate
+        @param coord2_scale_factor: scale factor for the second coordinate
+        @param relative_displacement: If the the displacement is to be relative (it normally should be)
+        """
+        dummy_coord2_scale_factor = coord2_scale_factor
+        # Shift the component in the y direction
+        MoveInstrumentComponent(Workspace=workspace,
+                                ComponentName=component_name,
+                                Y=coord2,
+                                RelativePosition=relative_displacement)
+
+        # Rotate around the y-axis.
+        self._rotate_around_y_axis(workspace = workspace,
+                                   component_name = component_name,
+                                   x_beam = coord1,
+                                   x_scale_factor = coord1_scale_factor,
+                                   bench_rotation = 0.)
+
+    def _rotate_around_y_axis(self,workspace, component_name, x_beam, x_scale_factor, bench_rotation):
+        '''
+        Rotates the component of the workspace around the y axis or shift along x, depending on the run number
+        @param workspace: a workspace name
+        @param component_name: the component to rotate
+        @param x_beam: either a shift in mm or a angle in degree
+        @param x_scale_factor: 
+        '''
         # in order to avoid rewriting old mask files from initial commisioning during 2014.
-        ws_ref=mtd[ws]
-        try:
-            run_num = ws_ref.getRun().getLogData('run_number').value
-        except:
-            run_num = int(re.findall(r'\d+',str(ws))[0])
+        ws_ref=mtd[workspace]
 
         # The angle value
         # Note that the x position gets converted from mm to m when read from the user file so we need to reverse this if X is now an angle
-        if int(run_num) < 2217:
+        if not LARMOR.is_run_new_style_run(ws_ref):
             # Initial commisioning before run 2217 did not pay much attention to making sure the bench_rot value was meaningful
-            xshift = -xbeam
-            sanslog.notice("Setup move " + str(xshift*XSF) + " " + str(0.0) + " " + str(0.0))
-            MoveInstrumentComponent(ws, ComponentName=detBench.name(), X=xshift, Y=0.0, Z=0.0)
+            xshift = -x_beam
+            sanslog.notice("Setup move " + str(xshift*x_scale_factor) + " " + str(0.0) + " " + str(0.0))
+            MoveInstrumentComponent(workspace, ComponentName=component_name, X=xshift, Y=0.0, Z=0.0)
         else:
-            xshift = BENCH_ROT-xbeam*XSF
-            sanslog.notice("Setup move " + str(xshift*XSF) + " " + str(0.0) + " " + str(0.0))
-            RotateInstrumentComponent(ws, ComponentName=detBench.name(), X=0, Y=1, Z=0, Angle=xshift)
-            #logger.warning("Back from RotateInstrumentComponent")
-
-        # beam centre, translation
-        return [0.0, 0.0], [-xbeam, -ybeam]
+            # The x shift is in degree
+            # IMPORTANT NOTE: It seems that the definition of positive and negative angles is different
+            # between Mantid and the beam scientists. This explains the different signs for x_beam and
+            # bench_rotation.
+            xshift = bench_rotation -x_beam*x_scale_factor
+            sanslog.notice("Setup rotate " + str(xshift*x_scale_factor) + " " + str(0.0) + " " + str(0.0))
+            RotateInstrumentComponent(workspace, ComponentName=component_name, X=0, Y=1, Z=0, Angle=xshift)
+        return xshift
 
     def append_marked(self, detNames):
         self._marked_dets.append(detNames)
@@ -1456,11 +1600,8 @@ class LARMOR(ISISInstrument):
         ws_ref = mtd[str(ws_name)]
         # in order to avoid problems with files from initial commisioning during 2014.
         # these didn't have the required log entries for the detector position
-        try:
-            run_num = ws_ref.getRun().getLogData('run_number').value
-        except:
-            run_num = int(re.findall(r'\d+',str(ws_name))[0])
-        if int(run_num) >= 2217:
+
+        if LARMOR.is_run_new_style_run(ws_ref):
             try:
                 #logger.warning("Trying get_detector_log")
                 log = self.get_detector_log(ws_ref)
@@ -1479,5 +1620,23 @@ class LARMOR(ISISInstrument):
 
         ISISInstrument.on_load_sample(self, ws_name, beamcentre,  isSample)
 
+    @staticmethod
+    def is_run_new_style_run(workspace_ref):
+        '''
+        Checks if the run assiated with the workspace is pre or post 2217
+        Original comment:
+        In order to avoid problems with files from initial commisioning during 2014.
+        these didn't have the required log entries for the detector position
+        @param workspace_ref:: A handle to the workspace
+        '''
+        try:
+            run_num = workspace_ref.getRun().getLogData('run_number').value
+        except:
+            run_num = int(re.findall(r'\d+',str(ws_name))[-1])
+        if int(run_num) >= 2217:
+            return True
+        else:
+            return False
+
 if __name__ == '__main__':
     pass
diff --git a/scripts/SANS/isis_reducer.py b/scripts/SANS/isis_reducer.py
index 8b9195ee640a7dce0f32a1119cbd9efa08d64533..ad08212b01d0dff48e45ffae70d7deed7c818c28 100644
--- a/scripts/SANS/isis_reducer.py
+++ b/scripts/SANS/isis_reducer.py
@@ -271,7 +271,6 @@ class ISISReducer(Reducer):
         center = self.instrument.get_default_beam_center()
         self._beam_finder = isis_reduction_steps.BaseBeamFinder(center[0], center[1])
 
-
     def set_sample(self, run, reload, period):
         """
             Assigns and load the run that this reduction chain will analysis
@@ -671,3 +670,14 @@ class ISISReducer(Reducer):
                 #if the workspace can't be deleted this function does nothing
                 pass
 
+    def update_beam_center(self):
+        """
+        Gets a possible new beam center position from the instrument after translation
+        or rotation. Previously this was not necessary as the reducer told the instrument
+        how to position, but now the instrument can get further positioning information
+        from the Instrument Parameter File.
+        """
+        centre_pos1, centre_pos2 = self.instrument.get_updated_beam_centre_after_move()
+        # Update the beam centre finder for the rear
+        self._beam_finder.update_beam_center(centre_pos1, centre_pos2)
+
diff --git a/scripts/SANS/isis_reduction_steps.py b/scripts/SANS/isis_reduction_steps.py
index f98175cf90062335d14d3cf0894e08d7e117e840..fb7bd90f899a96141a3d26895628cecd71e70462 100644
--- a/scripts/SANS/isis_reduction_steps.py
+++ b/scripts/SANS/isis_reduction_steps.py
@@ -1138,6 +1138,7 @@ class LoadSample(LoadRun):
         num = 0
         while True:
             reducer.instrument.on_load_sample(self.wksp_name, reducer.get_beam_center(), isSample)
+            reducer.update_beam_center()
             num += 1
             if num == self.periods_in_file:
                 break
@@ -2104,6 +2105,15 @@ class BaseBeamFinder(ReductionStep):
     def execute(self, reducer, workspace=None):
         return "Beam Center set at: %s %s" % (str(self._beam_center_x), str(self._beam_center_y))
 
+    def update_beam_center(self, beam_center_x, beam_center_y):
+        '''
+        Update the beam center position of the BeamBaseFinder
+        @param beam_center_x: The first position
+        @param beam_center_y: The second position
+        '''
+        self._beam_center_x = beam_center_x
+        self._beam_center_y = beam_center_y
+
 
 class UserFile(ReductionStep):
     """
diff --git a/scripts/test/SANSCentreFinderTest.py b/scripts/test/SANSCentreFinderTest.py
new file mode 100644
index 0000000000000000000000000000000000000000..955269e543e76db6e80caa6662490bc25ef45188
--- /dev/null
+++ b/scripts/test/SANSCentreFinderTest.py
@@ -0,0 +1,270 @@
+import unittest
+import mantid
+from mantid.simpleapi import *
+import centre_finder as cf
+import ISISCommandInterface as command_iface
+from reducer_singleton import ReductionSingleton
+import isis_reduction_steps as reduction_steps
+
+
+class SANSBeamCentrePositionUpdater(unittest.TestCase):
+    def test_that_find_ALL_produces_correct_increment(self):
+        # Arrange
+        fac = cf.BeamCentrePositionUpdaterFactory()
+        position_updater = fac.create_beam_centre_position_updater(cf.FindDirectionEnum.ALL)
+        x = 1.0
+        y = 2.0
+        x_step = 0.1
+        y_step = 0.2
+
+        # Act
+        x_new, y_new = position_updater.increment_position(x, y, x_step, y_step)
+
+        # Assert
+        x_expected = 1.1
+        y_expected = 2.2
+        self.assertEqual(x_expected, x_new, "The x value should have been incremented.")
+        self.assertEqual(y_expected, y_new, "The y value should have been incremented.")
+
+    def test_that_find_LEFTRIGHT_produces_correct_increment(self):
+        # Arrange
+        fac = cf.BeamCentrePositionUpdaterFactory()
+        position_updater = fac.create_beam_centre_position_updater(cf.FindDirectionEnum.LEFT_RIGHT)
+        x = 1.0
+        y = 2.0
+        x_step = 0.1
+        y_step = 0.2
+
+        # Act
+        x_new, y_new = position_updater.increment_position(x, y, x_step, y_step)
+
+        # Assert
+        x_expected = 1.1
+        y_expected = 2.0
+        self.assertEqual(x_expected, x_new, "The x value should have been incremented.")
+        self.assertEqual(y_expected, y_new, "The y value should have been incremented.")
+
+    def test_that_find_UPPDOWN_produces_correct_increment(self):
+        # Arrange
+        fac = cf.BeamCentrePositionUpdaterFactory()
+        position_updater = fac.create_beam_centre_position_updater(cf.FindDirectionEnum.UP_DOWN)
+        x = 1.0
+        y = 2.0
+        x_step = 0.1
+        y_step = 0.2
+
+        # Act
+        x_new, y_new = position_updater.increment_position(x, y, x_step, y_step)
+
+        # Assert
+        x_expected = 1.0
+        y_expected = 2.2
+        self.assertEqual(x_expected, x_new, "The x value should have been incremented.")
+        self.assertEqual(y_expected, y_new, "The y value should have been incremented.")
+
+class TestPositionProvider(unittest.TestCase):
+    workspace_name = 'dummy_ws'
+
+    class MockSample(object):
+        '''
+        Mocking out the sample
+        '''
+        def __init__(self, ws_name):
+            super(TestPositionProvider.MockSample,self).__init__()
+            self.wksp_name = ws_name
+        def get_wksp_name(self):
+            return self.wksp_name
+
+    def _provide_reducer(self, is_larmor, is_new = True):
+        '''
+        Provide a reducer with either Larmor or non-Larmor. If we have Larmor,
+        then we want to be able to set the run number as well
+        '''
+        command_iface.Clean()
+        if is_larmor and is_new:
+            command_iface.LARMOR()
+            CreateSampleWorkspace(OutputWorkspace=self.workspace_name)
+            AddSampleLog(Workspace=self.workspace_name,LogName='run_number', LogText='3000', LogType='Number')
+            sample = self.MockSample(self.workspace_name)
+            ReductionSingleton()._sample_run = sample
+            return ReductionSingleton()
+
+        elif is_larmor and not is_new:
+            command_iface.LARMOR()
+            CreateSampleWorkspace(OutputWorkspace=self.workspace_name)
+            AddSampleLog(Workspace=self.workspace_name,LogName='run_number', LogText='1000', LogType='Number')
+            sample = self.MockSample(self.workspace_name)
+            ReductionSingleton()._sample_run = sample
+            return ReductionSingleton()
+        else:
+            command_iface.LOQ()
+            return ReductionSingleton()
+
+    def _clean_up(self, workspace_name):
+        if workspace_name in mtd.getObjectNames():
+            mtd.remove(workspace_name)
+
+    def test_that_XY_increment_provider_is_created_for_non_larmor(self):
+        # Arrange
+        increment_coord1 = 1
+        increment_coord2 = 2
+        tolerance = 200
+        is_larmor = False
+        reducer = self._provide_reducer(is_larmor)
+        # Act
+        factory = cf.PositionProviderFactory(increment_coord1 = increment_coord1,
+                                             increment_coord2 = increment_coord2,
+                                             tolerance = tolerance,
+                                             position_type = cf.FindDirectionEnum.ALL)
+        provider = factory.create_position_provider(reducer = reducer)
+        # Asssert
+        self.assertTrue(isinstance(provider, cf.PositionProviderXY), "Should create a XY increment provider")
+        # Clean up
+        self._clean_up(self.workspace_name)
+
+    def test_that_YAngle_increment_provider_is_created_for_larmor(self):
+        # Arrange
+        increment_coord1 = 1
+        increment_coord2 = 2
+        tolerance = 200
+        is_larmor = True
+        is_new = True
+        reducer = self._provide_reducer(is_larmor, is_new)
+        # Act
+        factory = cf.PositionProviderFactory(increment_coord1 = increment_coord1,
+                                              increment_coord2 = increment_coord2,
+                                              tolerance = tolerance,
+                                              position_type = cf.FindDirectionEnum.ALL)
+        provider = factory.create_position_provider(reducer = reducer)
+
+        # Asssert
+        self.assertTrue(isinstance(provider, cf.PositionProviderAngleY), "Should create a AngleY increment provider")
+        # Clean up
+        self._clean_up(self.workspace_name)
+
+    def test_that_XY_increment_provider_is_created_for_old_larmor(self):
+        # Arrange
+        increment_coord1 = 1
+        increment_coord2 = 2
+        tolerance = 200
+        is_larmor = True
+        is_new = False
+        reducer = self._provide_reducer(is_larmor, is_new)
+        # Act
+        factory = cf.PositionProviderFactory(increment_coord1 = increment_coord1,
+                                              increment_coord2 = increment_coord2,
+                                              tolerance = tolerance,
+                                              position_type = cf.FindDirectionEnum.ALL)
+        provider = factory.create_position_provider(reducer = reducer)
+
+        # Asssert
+        self.assertTrue(isinstance(provider, cf.PositionProviderXY), "Should create a XY increment provider")
+        # Clean up
+        self._clean_up(self.workspace_name)
+
+    def test_that_XY_increment_provider_halves_the_step(self):
+        # Arrange
+        increment_coord1 = 1
+        increment_coord2 = 2
+        tolerance = 200
+        provider = cf.PositionProviderXY(increment_coord1,increment_coord2,tolerance)
+
+        # Act and Assert
+        self.assertTrue(increment_coord1 == provider.get_increment_coord1())
+        self.assertTrue(increment_coord2 == provider.get_increment_coord2())
+
+        provider.half_and_reverse_increment_coord1()
+        provider.half_and_reverse_increment_coord2()
+
+        self.assertTrue(-increment_coord1/2.0 == provider.get_increment_coord1())
+        self.assertTrue(-increment_coord2/2.0 == provider.get_increment_coord2())
+
+    def test_that_AngleY_increment_provider_halves_the_step(self):
+        # Arrange
+        increment_coord1 = 1
+        increment_coord2 = 2
+        tolerance = 200
+        tolerance_angle = 33
+        bench_rotation = 1
+        coord1_scale_factor = 1
+        provider = cf.PositionProviderAngleY(increment_coord1,increment_coord2,tolerance,tolerance_angle, bench_rotation, coord1_scale_factor)
+
+        # Act and Assert
+        self.assertTrue(increment_coord1 == provider.get_increment_coord1())
+        self.assertTrue(increment_coord2 == provider.get_increment_coord2())
+
+        provider.half_and_reverse_increment_coord1()
+        provider.half_and_reverse_increment_coord2()
+
+        self.assertTrue(-increment_coord1/2.0 == provider.get_increment_coord1())
+        self.assertTrue(-increment_coord2/2.0 == provider.get_increment_coord2())
+
+    def test_that_XY_increment_is_smaller_than_tolerance(self):
+        # Arrange
+        increment_coord1 = 1
+        increment_coord2 = 2
+        tolerance = 200
+        provider = cf.PositionProviderXY(increment_coord1,increment_coord2,tolerance)
+
+        # Act
+        is_smaller_coord1 = provider.is_coord1_increment_smaller_than_tolerance()
+        is_smaller_coord2 = provider.is_coord2_increment_smaller_than_tolerance()
+
+        # Assert
+        self.assertTrue(is_smaller_coord1)
+        self.assertTrue(is_smaller_coord2)
+
+    def test_that_XY_increment_is_larger_than_tolerance(self):
+        # Arrange
+        increment_coord1 = 1
+        increment_coord2 = 2
+        tolerance = 0.2
+        provider = cf.PositionProviderXY(increment_coord1,increment_coord2,tolerance)
+
+        # Act
+        is_smaller_coord1 = provider.is_coord1_increment_smaller_than_tolerance()
+        is_smaller_coord2 = provider.is_coord2_increment_smaller_than_tolerance()
+
+        # Assert
+        self.assertFalse(is_smaller_coord1)
+        self.assertFalse(is_smaller_coord2)
+
+    def test_that_AngleY_increment_is_smaller_than_tolerance(self):
+        # Arrange
+        increment_coord1 = 1
+        increment_coord2 = 2
+        tolerance = 100
+        tolerance_angle = 233
+        bench_rotation = 1
+        coord1_scale_factor = 1
+        provider = cf.PositionProviderAngleY(increment_coord1,increment_coord2,tolerance,tolerance_angle, bench_rotation, coord1_scale_factor)
+
+        # Act
+        is_smaller_coord1 = provider.is_coord1_increment_smaller_than_tolerance()
+        is_smaller_coord2 = provider.is_coord2_increment_smaller_than_tolerance()
+
+        # Assert
+        self.assertTrue(is_smaller_coord1)
+        self.assertTrue(is_smaller_coord2)
+
+    def test_that_AngleY_increment_is_larger_than_tolerance(self):
+        # Arrange
+        increment_coord1 = 1
+        increment_coord2 = 2
+        tolerance = 0.2
+        tolerance_angle = 0.1
+        bench_rotation = 1
+        coord1_scale_factor = 1
+        provider = cf.PositionProviderAngleY(increment_coord1,increment_coord2,tolerance,tolerance_angle, bench_rotation, coord1_scale_factor)
+
+        # Act
+        is_smaller_coord1 = provider.is_coord1_increment_smaller_than_tolerance()
+        is_smaller_coord2 = provider.is_coord2_increment_smaller_than_tolerance()
+
+        # Assert
+        self.assertFalse(is_smaller_coord1)
+        self.assertFalse(is_smaller_coord2)
+
+
+if __name__ == "__main__":
+    unittest.main()