Skip to content
Snippets Groups Projects
RunDescriptor.py 8.5 KiB
Newer Older
""" File contains Descriptors used describe run for direct inelastic reduction """ 

from mantid.simpleapi import *
from PropertiesDescriptors import *


class RunDescriptor(PropDescriptor):
    """ descriptor supporting a run and a workspace  """

    # the host class referencing contained all instantiated descriptors. 
    # Descriptors methods rely on it to work (e.g. to extract file loader preferences) 
    # so it has to be set up manually by PropertyManager __init__ method
    __holder_class__=None
    logger = None

    def __init__(self,ws_preffix,DocString=None): 
        """ """
        # Run number 
        self._run_number  = None
        # Extension of the file to load data from
        self._run_ext     = None
        # Workspace name which corresponds to the run 
        self._run_ws_name = None
        # String used to identify the workspace related to this property w.r.t. other workspaces
        self._ws_preffix  = ws_preffix
        #
        if not DocString is None:
            self.__doc__ = DocString

        if RunDescriptor.__holder_class__:
            logger = RunDescriptor.__holder_class__.log()

    def __get__(self,instance,owner=None):
        """ return current run number""" 
        if instance is None:
           return self
        return self._run_number 

    def __set__(self,instance,value):
       """ Set up Run number from any source """
       if value == None: # clear current run number
           self._run_number = None
           self._run_ws_name = None
           self._run_ext     = None
           return
       if isinstance(value, api.Workspace):
           self._run_number = value.getRunNuber()
           self._run_ws_name= value.name()
           return

       if isinstance(value,str): # it may be run number as string or it may be a workspace name
          if value in mtd: # workspace
              self._run_ws_name = value
              ws = mtd[value]
              self._run_number = ws.getRunNumber()
              return 
          else:
              self._run_number = parse_run_number_string(value) # TODO: parser      
       elif isinstance(value,list):
           self._run_number = value
       else:
           self._run_number = int(value)

       self._run_ws_name = None
       self._run_ws_name = self.get_ws_name()


    def get_file_ext(self):
        """ Method returns current file extension for file to load workspace from 
            e.g. .raw or .nxs extension
        """ 
        if self._run_ext:
            return self._run_ext
        else: # return IDF default
            return RunDescriptor.__holder_class__.data_file_ext

    def set_file_ext(self,val):
        """ set non-default file extension """
        if isinstance(val,str):
            if val[0] != '.':
                value = '.' + val
            else:
                value = val
            self._run_ext = value
        else:
            raise AttributeError('Source file extension can be only a string')

    def find_file(self,run_num = None):
        """Use Mantid to search for the given run. """

        inst_name = RunDescriptor.__holder_class__.short_inst_name
        if run_num:
            run_num_str = str(run_num)
        else:
            run_num_str = str(self.__get__(RunDescriptor.__holder_class__))
        #
        file_hint =inst_name + run_num_str + self.get_file_ext()
        try:
            return FileFinder.findRuns(file_hint)[0]
        except RuntimeError:
            message = 'Cannot find file matching hint {0} on current search paths ' \
                      'for instrument {1}'.format(file_hint,inst_name)

            logger(message,'warning')
            return 'ERROR:find_file '+message

    def get_workspace(self):
        """ Method returns workspace correspondent to current run number(s)
            and loads this workspace if necessary 
        """ 
        if not self._run_ws_name:
           return None

        if self._run_ws_name in mtd:
           return mtd[self._run_ws_name]
        else:
           if self._run_number:
               inst_name   = RunDescriptor.__holder_class__.short_inst_name
               calibration = RunDescriptor.__holder_class__.det_cal_file
               return self.load_run(inst_name, calibration,False, RunDescriptor.__holder_class__.load_monitors_with_workspace)
           else:
              return None

    def get_ws_name(self):
        """ return workspace name. If ws name is not defined, build it first and set up as target ws name

        """ 

        if self._run_ws_name:
            return self._run_ws_name

        if RunDescriptor.__holder_class__:
            instr_name = RunDescriptor.__holder_class__.short_inst_name
        else:
            instr_name = '_test_instrument'

        if not RunDescriptor.__holder_class__.sum_runs:
            ws_name = common.create_resultname(self._run_number,instr_name+self._ws_preffix)
        else:
            ws_name = common.create_resultname(self._run_number,instr_name+self._ws_preffix,'-sum')
        self._run_ws_name = ws_name

        return ws_name


    def load_run(self,inst_name, calibration=None, force=False, load_with_workspace=False):
        """Loads run into the given workspace.

           If force is true then the file is loaded regardless of whether  its workspace exists already
        """
        # If a workspace with this name exists, then assume it is to be used in place of a file
        ws_name = self.get_ws_name()

        if ws_name in mtd and not(force):
            RunDescriptor.logger("{0} already loaded as workspace.".format(self._run_ws_name),'notice')
            loaded_ws = mtd[ws_name]
        else:
            # If it doesn't exists as a workspace assume we have to try and load a file
            data_file = self.find_file()
            if data_file[0:4] == 'ERROR':
                raise IOError(data_file)           


            Load(Filename=data_file, OutputWorkspace=ws_name,LoadMonitors = str(int(load_with_workspace)))
            RunDescriptor.logger("Loaded {0}".format(data_file),'notice')
            loaded_ws = mtd[ws_name]

        ######## Now we have the workspace
        self.apply_calibration(loaded_ws,calibration)
        return loaded_ws

    def apply_calibration(self,loaded_ws,calibration=None):
        """  If calibration is present, apply it to the workspace """

        if not calibration:
            return
        if not isinstance(loaded_ws, api.Workspace):
           raise RuntimeError(' Calibration can be applied to a workspace only and got object of type {0}'.format(type(loaded_ws)))
        
        if loaded_ws.run().hasProperty("calibrated"):
            return # already calibrated

        if type(calibration) == str : # It can be only a file (got it from calibration property)
            RunDescriptor.logger('load_data: Moving detectors to positions specified in cal file {0}'.format(calibration),'debug')
            # Pull in pressures, thicknesses & update from cal file
            LoadDetectorInfo(Workspace=loaded_ws, DataFilename=calibration, RelocateDets=True)
            AddSampleLog(Workspace=loaded_ws,LogName="calibrated",LogText=str(calibration))
        elif isinstance(calibration, api.Workspace):
            logger('load_data: Copying detectors positions from workspace {0}: '.format(calibration.name()),'debug')
            CopyInstrumentParameters(InputWorkspace=calibration,OutputWorkspace=loaded_ws)
            AddSampleLog(Workspace=loaded_ws,LogName="calibrated",LogText=str(calibration))
  
#-------------------------------------------------------------------------------------------------------------------------------
class RunDescriptorDependent(RunDescriptor):
    def __init__(self,host_run,ws_preffix,DocString=None):
        RunDescriptor.__init__(self,ws_preffix,DocString)
        self._host = host_run
        self._this_run_defined=False
    def __get__(self,instance,owner=None):
       """ return dependent run number which is host run number if this one has not been set or this run number if it was""" 
       if instance is None:
           return self
       if self._this_run_defined:
          return self._run_number
       else:
          return self._host.__get__(instance,owner)

    def __set__(self,instance,value):
        if value is None:
            self._this_run_defined = False
            return
        self._this_run_defined = True
        super(RunDescriptorDependent,self).__set__(instance,value)

    def get_workspace(self):
        """ overloaded get workspace method """ 
        if self._this_run_defined:
            self.get_workspace()
        else:
            self._host.get_workspace()