From 7d59f2982c3e839b3fd46766f1f63e73ca272d1c Mon Sep 17 00:00:00 2001
From: David Fairbrother <DavidFair@users.noreply.github.com>
Date: Tue, 15 Nov 2016 16:52:09 +0000
Subject: [PATCH] Re #17949 Use Mantid to Load + move from dict to class for
 cal

---
 .../analysis/ISIS_PowderOldApiCalTest.py      |  3 +
 .../tests/analysis/ISIS_PowderPolarisTest.py  | 11 ++-
 scripts/Diffraction/isis_powder/RunDetails.py | 24 ++++++
 .../Diffraction/isis_powder/abstract_inst.py  | 30 ++------
 scripts/Diffraction/isis_powder/calibrate.py  | 29 ++++---
 scripts/Diffraction/isis_powder/common.py     | 28 +++----
 scripts/Diffraction/isis_powder/focus.py      | 16 ++--
 .../isis_powder/mock_instrument.py            |  4 +-
 scripts/Diffraction/isis_powder/pearl.py      | 58 ++++++++++----
 .../pearl_routines/fmode_output.py            |  2 +-
 scripts/Diffraction/isis_powder/polaris.py    | 76 +++++++++++--------
 .../polaris_routines/polaris_calib_parser.py  | 40 ++++++++--
 .../polaris_routines/polaris_calibration.yaml | 22 +++++-
 scripts/PearlPowderISIS/pearl_routines.py     |  4 +-
 scripts/test/ISIS_Powder_PearlTest.py         |  4 +-
 15 files changed, 220 insertions(+), 131 deletions(-)
 create mode 100644 scripts/Diffraction/isis_powder/RunDetails.py

diff --git a/Testing/SystemTests/tests/analysis/ISIS_PowderOldApiCalTest.py b/Testing/SystemTests/tests/analysis/ISIS_PowderOldApiCalTest.py
index 37173396f3f..e06d5211b58 100644
--- a/Testing/SystemTests/tests/analysis/ISIS_PowderOldApiCalTest.py
+++ b/Testing/SystemTests/tests/analysis/ISIS_PowderOldApiCalTest.py
@@ -14,6 +14,8 @@ DIRS = config['datasearch.directories'].split(';')
 
 
 class PowderDiffOldApiCalibrateTest(stresstesting.MantidStressTest):
+    _existing_config = None
+
     def requiredFiles(self):
         filenames = []
 
@@ -49,6 +51,7 @@ class PowderDiffOldApiCalibrateTest(stresstesting.MantidStressTest):
 
     def runTest(self):
         self._success = False
+        self._existing_config = config['datasearch.directories']
 
         pearl_routines.PEARL_startup("Calib", "15_3")
 
diff --git a/Testing/SystemTests/tests/analysis/ISIS_PowderPolarisTest.py b/Testing/SystemTests/tests/analysis/ISIS_PowderPolarisTest.py
index e1b8229a3de..f53d98b793d 100644
--- a/Testing/SystemTests/tests/analysis/ISIS_PowderPolarisTest.py
+++ b/Testing/SystemTests/tests/analysis/ISIS_PowderPolarisTest.py
@@ -14,6 +14,7 @@ DIRS = config['datasearch.directories'].split(';')
 class isis_powder_PolarisVanadiumCalTest(stresstesting.MantidStressTest):
 
     calibration_results = None
+    existing_config = config['datasearch.directories']
 
     def requiredFiles(self):
         return _gen_required_files()
@@ -26,12 +27,14 @@ class isis_powder_PolarisVanadiumCalTest(stresstesting.MantidStressTest):
 
     def cleanup(self):
         # TODO clean up reference files properly
+        config['datasearch.directories'] = self.existing_config
         _clean_up()
 
 
 class isis_powder_PolarisFocusTest(stresstesting.MantidStressTest):
 
     focus_results = None
+    existing_config = config['datasearch.directories']
 
     def requiredFiles(self):
         return _gen_required_files()
@@ -45,7 +48,7 @@ class isis_powder_PolarisFocusTest(stresstesting.MantidStressTest):
         return _calibration_validation(self, self.focus_results)
 
     def cleanup(self):
-        pass
+        config['datasearch.directories'] = self.existing_config
         # TODO
 
 
@@ -115,11 +118,11 @@ def setup_polaris_instrument():
     user_name = "Test"
 
     calibration_dir = _get_calibration_dir()
-    raw_data_dir = os.path.join(DIRS[0], "POLARIS")
+    path_to_add = os.path.join(DIRS[0], "POLARIS")
+    config['datasearch.directories'] += ";" + path_to_add
     output_dir = _get_output_dir()
 
-    polaris_obj = polaris.Polaris(user_name=user_name, calibration_dir=calibration_dir, raw_data_dir=raw_data_dir,
-                                  output_dir=output_dir)
+    polaris_obj = polaris.Polaris(user_name=user_name, calibration_dir=calibration_dir, output_dir=output_dir)
     return polaris_obj
 
 
diff --git a/scripts/Diffraction/isis_powder/RunDetails.py b/scripts/Diffraction/isis_powder/RunDetails.py
new file mode 100644
index 00000000000..e19c72251da
--- /dev/null
+++ b/scripts/Diffraction/isis_powder/RunDetails.py
@@ -0,0 +1,24 @@
+from __future__ import (absolute_import, division, print_function)
+
+
+class RunDetails(object):
+    """
+    This class holds the full file paths associated with each run and various other useful attributes
+    """
+
+    def __init__(self, calibration_path, grouping_path, vanadium_name, run_number):
+        # Essential attributes
+        self.run_number = run_number
+        self.calibration = calibration_path
+        self.grouping = grouping_path
+        self.vanadium = vanadium_name
+
+        # Optional Attributes
+        self.instrument_version = None
+        self.vanadium_absorption = None
+        self.splined_vanadium = None
+        self.sample_empty = None
+
+        self.solid_angle_corr = None
+
+        self.label = None
diff --git a/scripts/Diffraction/isis_powder/abstract_inst.py b/scripts/Diffraction/isis_powder/abstract_inst.py
index 8335e1fa3b0..d93e103be05 100644
--- a/scripts/Diffraction/isis_powder/abstract_inst.py
+++ b/scripts/Diffraction/isis_powder/abstract_inst.py
@@ -15,14 +15,13 @@ from isis_powder import calibrate
 
 @add_metaclass(ABCMeta)
 class AbstractInst(object):
-    def __init__(self, user_name=None, calibration_dir=None, raw_data_dir=None, output_dir=None,
+    def __init__(self, user_name=None, calibration_dir=None, output_dir=None,
                  default_input_ext=".raw"):
         # ----- Properties common to ALL instruments -------- #
         if user_name is None:
             raise ValueError("A user name must be specified")
         self._user_name = user_name
         self._calibration_dir = calibration_dir
-        self._raw_data_dir = raw_data_dir
         self._output_dir = output_dir
         self._default_input_ext = _prefix_dot_to_ext(default_input_ext)
         self._focus_mode = None
@@ -115,16 +114,6 @@ class AbstractInst(object):
 
         return out_file_names
 
-    def _generate_raw_data_cycle_dir(self, run_cycle):
-        if self._skip_appending_cycle_to_raw_dir():
-            return self.raw_data_dir
-        str_run_cycle = str(run_cycle)
-
-        # Append current cycle to raw data directory
-        generated_dir = os.path.join(self.raw_data_dir, str_run_cycle)
-
-        return generated_dir
-
     def _generate_input_full_path(self, run_number, input_dir):
         # Uses runtime polymorphism to generate the full run name
         file_name = self._generate_inst_file_name(run_number)
@@ -162,7 +151,7 @@ class AbstractInst(object):
     # Instrument specific methods
 
     @abstractmethod
-    def _get_calibration_full_paths(self, run_number):
+    def _get_run_details(self, run_number):
         pass
 
     @staticmethod
@@ -186,16 +175,6 @@ class AbstractInst(object):
         @return: The algorithm and save range in that order
         """
 
-    @staticmethod
-    @abstractmethod
-    def _get_cycle_information(run_number):
-        """
-        Gets all the information about this run for this cycle and returns it in a dictionary
-        @param run_number: The run to match the cycle to
-        @return: Dictionary with the following keys: "cycle", "instrument_version"
-        """
-        pass
-
     # --- Instrument optional hooks ----#
     # TODO cull some of these hooks once we unify the scripts
 
@@ -262,7 +241,7 @@ class AbstractInst(object):
     def _subtract_sample_empty(self, input_sample):
         return input_sample
 
-    def _apply_solid_angle_efficiency_corr(self, ws_to_correct, vanadium_number=None, calibration_dict=None):
+    def _apply_solid_angle_efficiency_corr(self, ws_to_correct, vanadium_number=None, run_details=None):
         return ws_to_correct
 
     def _load_monitor(self, number, cycle):
@@ -283,6 +262,9 @@ class AbstractInst(object):
     def calculate_focus_binning_params(self, sample):
         return None
 
+    def PEARL_populate_user_dirs(self, run_number):
+        return None
+
 # ----- Private Implementation ----- #
 # These should only be called by the abstract instrument class
 
diff --git a/scripts/Diffraction/isis_powder/calibrate.py b/scripts/Diffraction/isis_powder/calibrate.py
index 690e1812ef3..116d573c6df 100644
--- a/scripts/Diffraction/isis_powder/calibrate.py
+++ b/scripts/Diffraction/isis_powder/calibrate.py
@@ -15,23 +15,23 @@ def create_van(instrument, van, empty, output_van_file_name, num_of_splines, abs
     common.remove_intermediate_workspace(input_empty_ws)
     common.remove_intermediate_workspace(input_van_ws)
 
-    calibration_full_paths = instrument._get_calibration_full_paths(run_number=van)
+    run_details = instrument._get_run_details(run_number=van)
 
     corrected_van_ws = instrument. _apply_van_calibration_tof_rebinning(vanadium_ws=corrected_van_ws,
                                                                         tof_rebin_pass=1, return_units="TOF")
 
     corrected_van_ws = mantid.AlignDetectors(InputWorkspace=corrected_van_ws,
-                                             CalibrationFile=calibration_full_paths["calibration"])
+                                             CalibrationFile=run_details.calibration)
 
     corrected_van_ws = instrument._apply_solid_angle_efficiency_corr(ws_to_correct=corrected_van_ws,
                                                                      vanadium_number=van)
     if absorb:
         corrected_van_ws = _apply_absorb_corrections(instrument=instrument,
-                                                     calibration_full_paths=calibration_full_paths,
+                                                     run_details=run_details,
                                                      corrected_van_ws=corrected_van_ws, gen_absorb=gen_absorb)
 
     focused_van_file = mantid.DiffractionFocussing(InputWorkspace=corrected_van_ws,
-                                                   GroupingFileName=calibration_full_paths["grouping"])
+                                                   GroupingFileName=run_details.grouping)
 
     # Optional
     focused_van_file = instrument. _apply_van_calibration_tof_rebinning(vanadium_ws=focused_van_file,
@@ -39,20 +39,19 @@ def create_van(instrument, van, empty, output_van_file_name, num_of_splines, abs
 
     common.remove_intermediate_workspace(corrected_van_ws)
 
-    cycle_information = instrument._get_cycle_information(run_number=van)
+    cycle_information = instrument._get_run_details(run_number=van)
     splined_ws_list = instrument._spline_background(focused_van_file, num_of_splines,
-                                                    cycle_information["instrument_version"])
-
+                                                    cycle_information.instrument_version)
+    # Figure out who will provide the path name
     if instrument._PEARL_filename_is_full_path():
         out_van_file_path = output_van_file_name
     elif output_van_file_name:
-        # The user has manually specified the output file name
+        # The user has manually specified the output file
         out_van_file_path = os.path.join(instrument.calibration_dir, output_van_file_name)
+    elif run_details.splined_vanadium:
+        out_van_file_path = run_details.splined_vanadium
     else:
-        try:
-            out_van_file_path = calibration_full_paths["calibrated_vanadium"]
-        except KeyError:
-            raise ValueError("The output name must be manually specified for this instrument/run")
+        raise ValueError("The output name must be manually specified for this instrument/run")
 
     append = False
     for ws in splined_ws_list:
@@ -64,13 +63,13 @@ def create_van(instrument, van, empty, output_van_file_name, num_of_splines, abs
     return output_ws
 
 
-def _apply_absorb_corrections(instrument, calibration_full_paths, corrected_van_ws, gen_absorb):
+def _apply_absorb_corrections(instrument, run_details, corrected_van_ws, gen_absorb):
     corrected_van_ws = mantid.ConvertUnits(InputWorkspace=corrected_van_ws, Target="Wavelength")
 
     if gen_absorb:
-        absorb_ws = instrument._generate_vanadium_absorb_corrections(calibration_full_paths, corrected_van_ws)
+        absorb_ws = instrument._generate_vanadium_absorb_corrections(run_details, corrected_van_ws)
     else:
-        absorb_ws = mantid.LoadNexus(Filename=calibration_full_paths["vanadium_absorption"])
+        absorb_ws = mantid.LoadNexus(Filename=run_details.vanadium_absorption)
 
     # PEARL rebins whilst POLARIS does not as some of the older absorption files have different number of bins
     corrected_van_ws = instrument._calibration_rebin_to_workspace(ws_to_rebin=corrected_van_ws, ws_to_match=absorb_ws)
diff --git a/scripts/Diffraction/isis_powder/common.py b/scripts/Diffraction/isis_powder/common.py
index 4e37d557e7a..142d6254057 100644
--- a/scripts/Diffraction/isis_powder/common.py
+++ b/scripts/Diffraction/isis_powder/common.py
@@ -1,6 +1,8 @@
 from __future__ import (absolute_import, division, print_function)
-import mantid.simpleapi as mantid
 
+from six.moves import xrange
+
+import mantid.simpleapi as mantid
 
 # --- Public API --- #
 
@@ -56,7 +58,7 @@ def _load_monitor_sum_range(files, input_dir, instrument):
     # TODO refactor this - it probably doesn't work at the moment
     loop = 0
     num = files.split("_")
-    frange = list(range(int(num[0]), int(num[1]) + 1))
+    frange = xrange(int(num[0]), int(num[1]) + 1)
     mspectra = instrument._get_monitor_spectra(int(num[0]))
     for i in frange:
         file_path = instrument._generate_input_full_path(i, input_dir)
@@ -79,26 +81,26 @@ def _load_monitor_sum_range(files, input_dir, instrument):
 
 
 def _load_raw_files(run_number, instrument):
-    cycle_information = instrument._get_cycle_information(run_number=run_number)
-    input_dir = instrument._generate_raw_data_cycle_dir(cycle_information["cycle"])
+    run_name = instrument._generate_inst_file_name(run_number=run_number)
+
+    instrument.PEARL_populate_user_dirs(run_number=run_number)
 
     if isinstance(run_number, int):
-        infile = instrument._generate_input_full_path(run_number=run_number, input_dir=input_dir)
-        load_raw_ws = mantid.LoadRaw(Filename=infile, LoadLogFiles="0")
+        load_raw_ws = mantid.Load(Filename=run_name, LoadLogFiles="0")
     else:
-        load_raw_ws = _load_raw_file_range(run_number, input_dir, instrument)
+        load_raw_ws = _load_raw_file_range(run_number, instrument)
     return load_raw_ws
 
 
-def _load_raw_file_range(files, input_dir, instrument):
+def _load_raw_file_range(files, instrument):
     loop = 0
     num = files.split("_")
-    frange = list(range(int(num[0]), int(num[1]) + 1))
+    frange = xrange(int(num[0]), int(num[1]) + 1)
     out_ws = None
     for i in frange:
-        file_path = instrument._generate_input_full_path(i, input_dir)
+        file_name = instrument._generate_inst_file_name(run_number=i)
         outwork = "run" + str(i)
-        mantid.LoadRaw(Filename=file_path, OutputWorkspace=outwork, LoadLogFiles="0")
+        mantid.Load(Filename=file_name, OutputWorkspace=outwork, LoadLogFiles="0")
         loop += 1
         if loop == 2:
             firstwk = "run" + str(i - 1)
@@ -118,8 +120,8 @@ def _load_current_normalised_ws(number, instrument):
 
     read_in_ws = _load_raw_files(run_number=number, instrument=instrument)
 
-    cycle_information = instrument._get_cycle_information(run_number=number)
-    load_monitor_ws = instrument._load_monitor(number=number, cycle=cycle_information["cycle"])
+    run_information = instrument._get_run_details(run_number=number)
+    load_monitor_ws = instrument._load_monitor(number=number, cycle=run_information.label)
 
     read_ws = instrument._normalise_ws(ws_to_correct=read_in_ws, monitor_ws=load_monitor_ws, spline_terms=20)
 
diff --git a/scripts/Diffraction/isis_powder/focus.py b/scripts/Diffraction/isis_powder/focus.py
index da980a745a0..ff688ffe0fd 100644
--- a/scripts/Diffraction/isis_powder/focus.py
+++ b/scripts/Diffraction/isis_powder/focus.py
@@ -14,21 +14,20 @@ def _run_focus(instrument, run_number, perform_attenuation, perform_vanadium_nor
     read_ws = common._load_current_normalised_ws(number=run_number, instrument=instrument)
     input_workspace = instrument._do_tof_rebinning_focus(read_ws)  # Rebins for PEARL
 
-    calibration_file_paths = instrument._get_calibration_full_paths(run_number=run_number)
+    calibration_file_paths = instrument._get_run_details(run_number=run_number)
 
     # Compensate for empty sample if specified
     input_workspace = instrument._subtract_sample_empty(input_workspace)
 
     # Align / Focus
     input_workspace = mantid.AlignDetectors(InputWorkspace=input_workspace,
-                                            CalibrationFile=calibration_file_paths["calibration"])
+                                            CalibrationFile=calibration_file_paths.calibration)
 
-    # TODO fix this - maybe have it save solid angle corrections and just load/apply
     input_workspace = instrument._apply_solid_angle_efficiency_corr(ws_to_correct=input_workspace,
-                                                                    calibration_dict=calibration_file_paths)
+                                                                    run_details=calibration_file_paths)
 
     focused_ws = mantid.DiffractionFocussing(InputWorkspace=input_workspace,
-                                             GroupingFileName=calibration_file_paths["grouping"])
+                                             GroupingFileName=calibration_file_paths.grouping)
 
     # Process
     rebinning_params = instrument.calculate_focus_binning_params(sample=focused_ws)
@@ -56,14 +55,13 @@ def _run_focus(instrument, run_number, perform_attenuation, perform_vanadium_nor
 def _divide_sample_by_vanadium(instrument, run_number, input_workspace, perform_vanadium_norm):
     processed_spectra = []
 
-    input_file_paths = instrument._get_calibration_full_paths(run_number=run_number)
+    run_details = instrument._get_run_details(run_number=run_number)
 
-    cycle_information = instrument._get_cycle_information(run_number=run_number)
-    alg_range, save_range = instrument._get_instrument_alg_save_ranges(cycle_information["instrument_version"])
+    alg_range, save_range = instrument._get_instrument_alg_save_ranges(run_details.instrument_version)
 
     for index in range(0, alg_range):
         if perform_vanadium_norm:
-            vanadium_ws = mantid.LoadNexus(Filename=input_file_paths["calibrated_vanadium"], EntryNumber=index + 1)
+            vanadium_ws = mantid.LoadNexus(Filename=run_details.splined_vanadium, EntryNumber=index + 1)
 
             processed_spectra.append(
                 instrument.correct_sample_vanadium(focused_ws=input_workspace, index=index, vanadium_ws=vanadium_ws))
diff --git a/scripts/Diffraction/isis_powder/mock_instrument.py b/scripts/Diffraction/isis_powder/mock_instrument.py
index b7deba6bb45..e22ebecd6d1 100644
--- a/scripts/Diffraction/isis_powder/mock_instrument.py
+++ b/scripts/Diffraction/isis_powder/mock_instrument.py
@@ -21,7 +21,7 @@ class MockInstrument(AbstractInst):
     def _get_default_group_names(self):
         return None
 
-    def _get_calibration_full_paths(self, run_number):
+    def _get_run_details(self, run_number):
         # This is here to help remind people of the dict that is expected
         calibration_details = {"calibration": "cal",
                                "grouping": "group",
@@ -38,7 +38,7 @@ class MockInstrument(AbstractInst):
         return None
 
     @staticmethod
-    def _get_cycle_information(run_number):
+    def _get_label_information(run_number):
         # This is here to help remind people of the dict format
         cycle_information = {"cycle" : "123",
                              "instrument_version": "test_v1"}
diff --git a/scripts/Diffraction/isis_powder/pearl.py b/scripts/Diffraction/isis_powder/pearl.py
index 567264f93a4..0c7c9a6a429 100644
--- a/scripts/Diffraction/isis_powder/pearl.py
+++ b/scripts/Diffraction/isis_powder/pearl.py
@@ -1,6 +1,8 @@
 from __future__ import (absolute_import, division, print_function)
 
 import mantid.simpleapi as mantid
+from mantid import config
+
 import numpy as numpy
 
 import os
@@ -10,6 +12,7 @@ import isis_powder.common as common
 
 from isis_powder import pearl_calib_factory
 from isis_powder import pearl_cycle_factory
+from isis_powder.RunDetails import RunDetails
 
 from isis_powder.pearl_routines import fmode_output
 
@@ -30,7 +33,7 @@ class Pearl(AbstractInst):
     def __init__(self, user_name=None, calibration_dir=None, raw_data_dir=None, output_dir=None,
                  input_file_ext=".raw", tt_mode="TT88"):
 
-        super(Pearl, self).__init__(user_name=user_name, calibration_dir=calibration_dir, raw_data_dir=raw_data_dir,
+        super(Pearl, self).__init__(user_name=user_name, calibration_dir=calibration_dir,
                                     output_dir=output_dir, default_input_ext=input_file_ext)
 
         self._tt_mode = tt_mode
@@ -41,7 +44,7 @@ class Pearl(AbstractInst):
 
         # Old API support
         self._old_atten_file = None
-        self._old_api_uses_full_paths = False
+        self._existing_config = None
 
         # File names
         pearl_mc_absorption_file_name = "PRL112_DC25_10MM_FF.OUT"  # TODO
@@ -63,11 +66,13 @@ class Pearl(AbstractInst):
 
     # Methods #
 
-    def _get_calibration_full_paths(self, run_number):
-        cycle_dict = self._get_cycle_information(run_number=run_number)
+    def _get_run_details(self, run_number):
+        # TODO once we migrate this to another format (i.e. not the if/elif/else) implement cached val
+        cycle_dict = self._get_label_information(run_number=run_number)
 
         calibration_file, grouping_file, van_absorb, van_file =\
             pearl_calib_factory.get_calibration_filename(cycle=cycle_dict["cycle"], tt_mode=self._tt_mode)
+        cycle, instrument_version = pearl_cycle_factory.get_cycle_dir(run_number)
 
         calibration_dir = self.calibration_dir
 
@@ -76,17 +81,19 @@ class Pearl(AbstractInst):
         van_absorb_full_path = os.path.join(calibration_dir, van_absorb)
         van_file_full_path = os.path.join(calibration_dir, van_file)
 
-        # TODO when we move PEARL to save out splined vanadium files support them below
-        calibration_details = {"calibration": calibration_full_path,
-                               "grouping": grouping_full_path,
-                               "vanadium_absorption": van_absorb_full_path,
-                               "vanadium": van_file_full_path,
-                               "calibrated_vanadium": van_file_full_path}
+        run_details = RunDetails(calibration_path=calibration_full_path, grouping_path=grouping_full_path,
+                                 vanadium_name=van_file_full_path, run_number=run_number)
+        run_details.vanadium_absorption = van_absorb_full_path
+        run_details.label = cycle
+        run_details.instrument_version = instrument_version
 
-        return calibration_details
+        # TODO remove this when we move to saving splined van ws on PEARL
+        run_details.splined_vanadium = run_details.vanadium
 
-    @staticmethod
-    def _get_cycle_information(run_number):
+        return run_details
+
+    def _get_label_information(self, run_number):
+        # TODO remove this when we move to combining CAL/RUN factories
         run_input = ""
         if not run_number.isdigit():
             # Only take first valid number as it is probably of the form 12345_12350
@@ -119,7 +126,7 @@ class Pearl(AbstractInst):
 
     def _create_calibration(self, calibration_runs, offset_file_name, grouping_file_name):
         input_ws = common._load_current_normalised_ws(number=calibration_runs, instrument=self)
-        cycle_information = self._get_cycle_information(calibration_runs)
+        cycle_information = self._get_label_information(calibration_runs)
 
         if cycle_information["instrument_version"] == "new" or cycle_information["instrument_version"] == "new2":
             input_ws = mantid.Rebin(InputWorkspace=input_ws, Params="100,-0.0006,19950")
@@ -283,7 +290,7 @@ class Pearl(AbstractInst):
     def _do_silicon_calibration(self, runs_to_process, cal_file_name, grouping_file_name):
         # TODO fix all of this as the script is too limited to be useful
         create_si_ws = common._load_current_normalised_ws(number=runs_to_process, instrument=self)
-        cycle_details = self._get_cycle_information(runs_to_process)
+        cycle_details = self._get_label_information(runs_to_process)
         instrument_version = cycle_details["instrument_version"]
 
         if instrument_version == "new" or instrument_version == "new2":
@@ -364,6 +371,17 @@ class Pearl(AbstractInst):
             mspectra = 1
         return mspectra
 
+    def _generate_raw_data_cycle_dir(self, run_cycle):
+        if self._skip_appending_cycle_to_raw_dir():
+            return self.raw_data_dir
+        str_run_cycle = str(run_cycle)
+
+        # Append current cycle to raw data directory
+        generated_dir = os.path.join(self.raw_data_dir, str_run_cycle)
+        generated_dir += '/'
+
+        return generated_dir
+
     # Support for old API - This can be removed when PEARL_routines is removed
     def _old_api_constructor_set(self, user_name=None, calibration_dir=None, raw_data_dir=None, output_dir=None,
                                  input_file_ext=None, tt_mode=None):
@@ -388,7 +406,7 @@ class Pearl(AbstractInst):
         self._calibration_dir = calib_dir
 
     def _old_api_set_raw_data_dir(self, raw_data_dir):
-        self._disable_appending_cycle_to_raw_dir = True
+        self._old_api_uses_full_paths = True
         self._raw_data_dir = raw_data_dir
 
     def _old_api_set_output_dir(self, output_dir):
@@ -406,6 +424,14 @@ class Pearl(AbstractInst):
     def _PEARL_filename_is_full_path(self):
         return self._old_api_uses_full_paths
 
+    def PEARL_populate_user_dirs(self, run_number):
+        run_details = self._get_run_details(run_number=run_number)
+        generated_path = self._generate_raw_data_cycle_dir(run_cycle=run_details.label)
+        user_dirs = config['datasearch.directories']
+        user_dirs_list = user_dirs.split(';')
+        if generated_path not in user_dirs_list:
+            config['datasearch.directories'] += ';' + generated_path
+
     def _get_focus_tof_binning(self):
         return self._focus_tof_binning
 
diff --git a/scripts/Diffraction/isis_powder/pearl_routines/fmode_output.py b/scripts/Diffraction/isis_powder/pearl_routines/fmode_output.py
index 7beb0d4b84c..7eeb9ac9495 100644
--- a/scripts/Diffraction/isis_powder/pearl_routines/fmode_output.py
+++ b/scripts/Diffraction/isis_powder/pearl_routines/fmode_output.py
@@ -9,7 +9,7 @@ import isis_powder.common as common
 
 def generate_and_save_focus_output(instrument, processed_spectra, run_number, perform_attenuation, focus_mode=None,):
     output_file_paths = instrument._generate_out_file_paths(run_number, instrument.output_dir)
-    cycle_information = instrument._get_cycle_information(run_number)
+    cycle_information = instrument._get_label_information(run_number)
     unused, save_range = instrument._get_instrument_alg_save_ranges(cycle_information["instrument_version"])
 
     if focus_mode == "all":
diff --git a/scripts/Diffraction/isis_powder/polaris.py b/scripts/Diffraction/isis_powder/polaris.py
index f7149f37e26..651dfe6513c 100644
--- a/scripts/Diffraction/isis_powder/polaris.py
+++ b/scripts/Diffraction/isis_powder/polaris.py
@@ -5,10 +5,10 @@ import os
 import mantid.simpleapi as mantid
 
 from isis_powder.abstract_inst import AbstractInst
-from isis_powder.polaris_routines import polaris_calib_factory
 from isis_powder.polaris_routines import polaris_calib_parser
 
 import isis_powder.common as common
+from isis_powder.RunDetails import RunDetails
 
 
 class Polaris(AbstractInst):
@@ -25,13 +25,17 @@ class Polaris(AbstractInst):
     _number_of_banks = 5
 
     def __init__(self, user_name=None, calibration_dir=None, raw_data_dir=None, output_dir=None,
-                 input_file_ext=".raw", sample_empty_name=None):
+                 input_file_ext=".raw", chopper_on=True):
 
-        super(Polaris, self).__init__(user_name=user_name, calibration_dir=calibration_dir, raw_data_dir=raw_data_dir,
+        super(Polaris, self).__init__(user_name=user_name, calibration_dir=calibration_dir,
                                       output_dir=output_dir, default_input_ext=input_file_ext)
 
+        self._chopper_on = chopper_on
         self._masking_file_name = "VanaPeaks.dat"
-        self._sample_empty = sample_empty_name
+
+        # Caches the last dictionary to avoid us having to keep parsing the YAML
+        self._run_details_last_run_number = None
+        self._run_details_cached_obj = None
 
     # Abstract implementation
     def _get_lambda_range(self):
@@ -43,26 +47,33 @@ class Polaris(AbstractInst):
     def _get_default_group_names(self):
         return self._calibration_grouping_names
 
-    def _get_calibration_full_paths(self, run_number):
-        # offset_file_name, grouping_file_name, vanadium_file_name = polaris_calib_factory.get_calibration_file(cycle)
-        cycle_dict = self._get_cycle_information(run_number=run_number)
-        configuration = polaris_calib_parser.get_calibration_dict(cycle_dict["cycle"])
-        calibration_dir = self.calibration_dir
+    def _get_run_details(self, run_number):
+        # TODO rename this from get calibration to get run details
+        if self._run_details_last_run_number == run_number:
+            return self._run_details_cached_obj
 
-        # Assume the raw vanadium is with other raw files
-        vanadium_full_path = os.path.join(self.raw_data_dir, configuration["vanadium_file_name"])
+        configuration = polaris_calib_parser.get_calibration_dict(run_number=run_number)
+        calibration_dir = self.calibration_dir
 
+        # Common to all runs
         calibration_full_path = os.path.join(calibration_dir, configuration["offset_file_name"])
         grouping_full_path = os.path.join(calibration_dir, configuration["grouping_file_name"])
+        if self._chopper_on:
+            chopper_config = configuration["chopper_on"]
+        else:
+            chopper_config = configuration["chopper_off"]
+        vanadium_file = self._generate_inst_file_name(run_number=chopper_config["vanadium_file_name"])
+        splined_vanadium = os.path.join(calibration_dir, chopper_config["splined_vanadium_file_name"])
+        solid_angle_file_path = os.path.join(calibration_dir, chopper_config["solid_angle_file_name"])
 
-        calibrated_full_path = os.path.join(calibration_dir, configuration["calibrated_vanadium_file_name"])
-        solid_angle_file_path = os.path.join(calibration_dir, configuration["solid_angle_file_name"])
+        calibration_details = RunDetails(calibration_path=calibration_full_path, grouping_path=grouping_full_path,
+                                         vanadium_name=vanadium_file, run_number=run_number)
+        calibration_details.splined_vanadium = splined_vanadium
+        calibration_details.solid_angle_corr = solid_angle_file_path
+        calibration_details.label = configuration["label"]
 
-        calibration_details = {"calibration": calibration_full_path,
-                               "grouping": grouping_full_path,
-                               "vanadium": vanadium_full_path,
-                               "calibrated_vanadium": calibrated_full_path,
-                               "solid_angle_corr": solid_angle_file_path}
+        self._run_details_last_run_number = run_number
+        self._run_details_cached_obj = calibration_details
 
         return calibration_details
 
@@ -75,11 +86,6 @@ class Polaris(AbstractInst):
         alg_range = 5
         return alg_range, None
 
-    @staticmethod
-    def _get_cycle_information(run_number):
-        return {"cycle": "test",  # TODO implement properly
-                "instrument_version": ""}
-
     def _normalise_ws(self, ws_to_correct, monitor_ws=None, spline_terms=20):
         normalised_ws = mantid.NormaliseByCurrent(InputWorkspace=ws_to_correct)
         return normalised_ws
@@ -112,6 +118,8 @@ class Polaris(AbstractInst):
         return corrections_ws
 
     def _subtract_sample_empty(self, input_sample):
+        # TODO when calibration mapping has sample.empty enable this
+        return input_sample
         if self._sample_empty is not None:
             empty_sample_path = os.path.join(self.calibration_dir, self._sample_empty)
             empty_sample = mantid.Load(Filename=empty_sample_path)
@@ -120,13 +128,13 @@ class Polaris(AbstractInst):
             common.remove_intermediate_workspace(empty_sample)
         return input_sample
 
-    def _apply_solid_angle_efficiency_corr(self, ws_to_correct, vanadium_number=None, calibration_dict=None):
-        assert(vanadium_number or calibration_dict)
+    def _apply_solid_angle_efficiency_corr(self, ws_to_correct, vanadium_number=None, run_details=None):
+        assert(vanadium_number or run_details)
 
-        if not calibration_dict or not os.path.isfile(calibration_dict["solid_angle_corr"]):
-            corrections = self.generate_solid_angle_corrections(calibration_dict, vanadium_number)
+        if not run_details or not os.path.isfile(run_details.solid_angle_corr):
+            corrections = self.generate_solid_angle_corrections(run_details, vanadium_number)
         else:
-            corrections = mantid.Load(Filename=calibration_dict["solid_angle_corr"])
+            corrections = mantid.Load(Filename=run_details.solid_angle_corr)
 
         corrected_ws = mantid.Divide(LHSWorkspace=ws_to_correct, RHSWorkspace=corrections)
         common.remove_intermediate_workspace(corrections)
@@ -134,16 +142,18 @@ class Polaris(AbstractInst):
         ws_to_correct = corrected_ws
         return ws_to_correct
 
-    def generate_solid_angle_corrections(self, calibration_dict, vanadium_number):
+    def generate_solid_angle_corrections(self, run_details, vanadium_number):
         if vanadium_number:
             solid_angle_vanadium_ws = common._load_raw_files(run_number=vanadium_number, instrument=self)
+        elif run_details:
+            solid_angle_vanadium_ws = mantid.Load(Filename=run_details.vanadium)
         else:
-            solid_angle_vanadium_ws = mantid.Load(Filename=calibration_dict["vanadium"])
+            raise RuntimeError("Got no run_details of vanadium_number in gen solid angle corrections")
         normalised_vanadium_ws = self._normalise_ws(solid_angle_vanadium_ws)
         corrections = self._calculate_solid_angle_efficiency_corrections(normalised_vanadium_ws)
 
-        if calibration_dict:
-            mantid.SaveNexusProcessed(InputWorkspace=corrections, Filename=calibration_dict["solid_angle_corr"])
+        if run_details:
+            mantid.SaveNexusProcessed(InputWorkspace=corrections, Filename=run_details.solid_angle_corr)
 
         common.remove_intermediate_workspace(solid_angle_vanadium_ws)
         common.remove_intermediate_workspace(normalised_vanadium_ws)
@@ -241,7 +251,7 @@ class Polaris(AbstractInst):
     def _read_masking_file(self):
         all_banks_masking_list = []
         bank_masking_list = []
-        mask_path = os.path.join(self.raw_data_dir, self._masking_file_name)
+        mask_path = os.path.join(self.calibration_dir, self._masking_file_name)
 
         ignore_line_prefixes = (' ', '\n', '\t', '#')  # Matches whitespace or # symbol
 
diff --git a/scripts/Diffraction/isis_powder/polaris_routines/polaris_calib_parser.py b/scripts/Diffraction/isis_powder/polaris_routines/polaris_calib_parser.py
index 86589d2223c..42d54b3294e 100644
--- a/scripts/Diffraction/isis_powder/polaris_routines/polaris_calib_parser.py
+++ b/scripts/Diffraction/isis_powder/polaris_routines/polaris_calib_parser.py
@@ -1,16 +1,14 @@
 from __future__ import (absolute_import, division, print_function)
+from six.moves import xrange
 
 import os
 import yaml
 
 
-def get_calibration_dict(cycle):
+def get_calibration_dict(run_number):
     config_file = _open_yaml_file()
-    try:
-        output = config_file[str(cycle)]
-    except KeyError:
-        raise RuntimeError("Cycle " + str(cycle) + " not defined in calibration file")
-    return output
+    run_key = _get_dictionary_key(config_handle=config_file, run_number=run_number)
+    return config_file[run_key]
 
 
 def _open_yaml_file():
@@ -27,3 +25,33 @@ def _open_yaml_file():
             raise RuntimeError("Failed to parse POLARIS calibration YAML file")
 
     return read_config
+
+
+def _get_dictionary_key(config_handle, run_number):
+
+    for key in config_handle:
+        run_generator = _parse_number_key(input_string=key)
+        for run_list in run_generator:
+            if run_number in run_list:
+                return key
+
+    # If we hit this point the run_number isn't in any of the keys in the YAML file
+    raise ValueError("Run number " + str(run_number) + " not recognised in calibration mapping")
+
+
+def _parse_number_key(input_string):
+    # Expands run numbers of the form 1-10, 12, 14-20, 23 to 1,2,3,..,8,9,10,12,14,15,16...,19,20,23
+
+    string_to_parse = str(input_string).strip()
+
+    for entry in string_to_parse.split(','):
+        # Split between comma separated values
+        numbers = entry.split('-')
+        # Check if we are using a dash separator and return the range between those values
+        if len(numbers) == 1:
+            yield numbers
+        elif len(numbers) == 2:
+            # Add 1 so it includes the final number '-' range
+            yield xrange(int(numbers[0]), int(numbers[-1]) + 1)
+        else:
+            raise ValueError("The run number " + str(entry) + " is incorrect in calibration mapping")
diff --git a/scripts/Diffraction/isis_powder/polaris_routines/polaris_calibration.yaml b/scripts/Diffraction/isis_powder/polaris_routines/polaris_calibration.yaml
index 125f1d31298..03aace5d00e 100644
--- a/scripts/Diffraction/isis_powder/polaris_routines/polaris_calibration.yaml
+++ b/scripts/Diffraction/isis_powder/polaris_routines/polaris_calibration.yaml
@@ -7,9 +7,23 @@
 #  calibrated_vanadium_file_name : "calibrated_vanadium_name.nxs"
 #  solid_angle_file_name : "solid_angle_corrections.nxs"
 
-test:
+#test:
+#  offset_file_name : "offsets_2011_cycle111b.cal"
+#  grouping_file_name : "offsets_2011_cycle111b.cal"
+#  vanadium_file_name : "POL78338.raw"
+#  calibrated_vanadium_file_name : "calibrated_vanadium_name.nxs"
+#  solid_angle_file_name : "solid_angle_corrections.nxs"
+#
+
+78334-82415:
   offset_file_name : "offsets_2011_cycle111b.cal"
   grouping_file_name : "offsets_2011_cycle111b.cal"
-  vanadium_file_name : "POL78338.raw"
-  calibrated_vanadium_file_name : "calibrated_vanadium_name.nxs"
-  solid_angle_file_name : "solid_angle_corrections.nxs"
+  label : "15_2"
+  chopper_on:
+    vanadium_file_name : "78338"
+    splined_vanadium_file_name : "chopper_on_van_calib.nxs"
+    solid_angle_file_name : "chopper_on_sac.nxs"
+  chopper_off:
+    vanadium_file_name : "78338"
+    splined_vanadium_file_name : "chopper_off_van_calib.nxs"
+    solid_angle_file_name : "chopper_off_sac.nxs"
diff --git a/scripts/PearlPowderISIS/pearl_routines.py b/scripts/PearlPowderISIS/pearl_routines.py
index 0e1af82d14a..8092468d45c 100644
--- a/scripts/PearlPowderISIS/pearl_routines.py
+++ b/scripts/PearlPowderISIS/pearl_routines.py
@@ -100,7 +100,7 @@ def PEARL_getmonitorspectrum(runno):
 
 def PEARL_getcycle(number):
     pearl_obj = _pearl_obj_singleton()
-    cycle_information = pearl_obj._get_cycle_information(number)
+    cycle_information = pearl_obj._get_label_information(number)
     datadir = pearl_obj.output_dir
     updated_vals = {"cycle"   : cycle_information["cycle"],
                     "instver" : cycle_information["instrument_version"],
@@ -124,7 +124,7 @@ def PEARL_getcalibfiles():
     pearl_obj._old_api_set_tt_mode(g_oldParams["tt_mode"])
     pearl_obj._old_api_set_calib_dir(g_oldParams["pearl_file_dir"])
 
-    cal_dict = pearl_obj._get_calibration_full_paths(cycle=cycle)
+    cal_dict = pearl_obj._get_run_details(cycle=cycle)
     print ("Setting calibration for cycle", cycle)
 
     updated_vals = {"calfile"     : cal_dict["calibration"],
diff --git a/scripts/test/ISIS_Powder_PearlTest.py b/scripts/test/ISIS_Powder_PearlTest.py
index 0e6e4384f57..265c5cb9e7d 100644
--- a/scripts/test/ISIS_Powder_PearlTest.py
+++ b/scripts/test/ISIS_Powder_PearlTest.py
@@ -13,7 +13,7 @@ class isis_powder_PearlTest(unittest.TestCase):
 
     def test_cycle_information_generates_correctly(self):
         # This checks that the cycle information generates using the correct keys for the dict
-        output = self._get_pearl_inst_defaults()._get_cycle_information(85500)
+        output = self._get_pearl_inst_defaults()._get_label_information(85500)
         expected_cycle = "14_1"
         expected_inst_vers = "new2"
         self.assertEquals(output["cycle"], expected_cycle)
@@ -57,7 +57,7 @@ class isis_powder_PearlTest(unittest.TestCase):
         expected_calibration_dir = self.calibration_dir
 
         pearl_obj = self._get_pearl_inst_defaults()
-        output = pearl_obj._get_calibration_full_paths(input_cycle)
+        output = pearl_obj._get_run_details(input_cycle)
 
         self.assertEquals(output["calibration"], expected_calibration_dir + expected_calfile)
         self.assertEquals(output["grouping"], expected_calibration_dir + expected_grouping_file)
-- 
GitLab