diff --git a/Code/Mantid/scripts/Inelastic/Direct/CommonFunctions.py b/Code/Mantid/scripts/Inelastic/Direct/CommonFunctions.py index 68fb32d6294f9173f68202f381ffecbcb03d71ce..07458064372fb08135657ec1982f887cfa3f6bfd 100644 --- a/Code/Mantid/scripts/Inelastic/Direct/CommonFunctions.py +++ b/Code/Mantid/scripts/Inelastic/Direct/CommonFunctions.py @@ -4,16 +4,6 @@ from mantid import api import os import string -def find_file(run_number): - """Use Mantid to search for the given run. - """ - file_hint = str(run_number) - try: - return FileFinder.findRuns(file_hint)[0] - except RuntimeError: - message = 'Cannot find file matching hint "%s" on current search paths ' + \ - 'for instrument "%s"' - raise ValueError( message % (file_hint, config['default.instrument'])) def create_resultname(run_number, prefix='', suffix=''): """Create a string based on the run number and optional prefix and @@ -106,80 +96,6 @@ def load_runs(inst_name, runs, sum=True, calibration=None,load_with_workspace=Fa # Try a single run return load_run(inst_name, runs, calibration,False,load_with_workspace) -def load_run(inst_name, run_number, 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 - if str(run_number) in mtd: - logger.notice("%s already loaded as workspace." % str(run_number)) - if type(run_number) == str: - loaded_ws = mtd[run_number] - else: - loaded_ws = run_number - else: - # If it doesn't exists as a workspace assume we have to try and load a file - if type(run_number) == int: - filename = find_file(run_number) - elif type(run_number) == list: - raise TypeError('load_run() cannot handle run lists') - else: - # Check if it exists, else tell Mantid to try and - # find it - if os.path.exists(run_number): - filename = run_number - else: - filename = find_file(run_number) - # The output name - output_name = create_dataname(filename) - if (not force) and (output_name in mtd): - logger.notice("%s already loaded" % filename) - return mtd[output_name] - - args={}; - ext = os.path.splitext(filename)[1].lower(); - wrong_monitors_name = False; - if ext.endswith("raw"): - if load_with_workspace: - args['LoadMonitors']='Include' - else: - args['LoadMonitors']='Separate' - - elif ext.endswith('nxs'): - args['LoadMonitors'] = '1' - - loaded_ws = Load(Filename=filename, OutputWorkspace=output_name,**args) - if isinstance(loaded_ws,tuple) and len(loaded_ws)>1: - mon_ws = loaded_ws[1]; - loaded_ws=loaded_ws[0]; - - logger.notice("Loaded %s" % filename) - - ######## Now we have the workspace - apply_calibration(inst_name, loaded_ws, calibration) - return loaded_ws - -def apply_calibration(inst_name, loaded_ws, calibration): - """ - """ - if loaded_ws.run().hasProperty("calibrated"): - return - - if type(calibration) == str or type(calibration) == int: - logger.debug('load_data: Moving detectors to positions specified in cal file "%s"' % str(calibration)) - filename = calibration - skip_lines = None - if type(filename) == int: # assume run number - filename = inst_name + str(filename) - # Pull in pressures, thicknesses & update from cal file - LoadDetectorInfo(Workspace=loaded_ws, DataFilename=filename, RelocateDets=True) - AddSampleLog(Workspace=loaded_ws,LogName="calibrated",LogText=str(calibration)) - elif isinstance(calibration, mantid.api.Workspace): - logger.debug('load_data: Copying detectors positions from workspace "%s": ' % calibration.name()) - CopyInstrumentParameters(InputWorkspace=calibration,OutputWorkspace=loaded_ws) - AddSampleLog(Workspace=loaded_ws,LogName="calibrated",LogText=str(calibration)) def sum_files(accumulator, files, file_type): """ diff --git a/Code/Mantid/scripts/Inelastic/Direct/NonIDF_Properties.py b/Code/Mantid/scripts/Inelastic/Direct/NonIDF_Properties.py index 64db72478b2c74507463b5ad2e01272155e201f0..c9d6b6adb25d0d9efeeb39d47d92fa30b5388745 100644 --- a/Code/Mantid/scripts/Inelastic/Direct/NonIDF_Properties.py +++ b/Code/Mantid/scripts/Inelastic/Direct/NonIDF_Properties.py @@ -44,21 +44,12 @@ class NonIDF_Properties(object): object.__setattr__(self,'_save_file_name',None) self._set_instrument_and_facility(Instrument,run_workspace) + + # set up descriptors holder class reference + RunDescriptor.__holder_class__ = self + RunDescriptor.logger = self.log #end - def get_sample_ws_name(self): - """ build and return sample workspace name - - See similar property save_file_name TODO: (leave only one) - """ - if not self.sum_runs: - return common.create_resultname(self.sample_run,self.instr_name) - else: - return common.create_resultname(self.sample_run,self.instr_name,'-sum') - - def getDefaultParameterValue(self,par_name): - """ method to get default parameter value, specified in IDF """ - return prop_helpers.get_default_parameter(self.instrument,par_name) #----------------------------------------------------------------------------- # Complex properties with personal descriptors #----------------------------------------------------------------------------- @@ -74,14 +65,17 @@ class NonIDF_Properties(object): # van_rmm = VanadiumRMM() # Run descriptors - sample_run = RunDescriptor("Run ID (number) to convert to energy or list of the such run numbers") - wb_run = RunDescriptor("Run ID (number) for vanadium run used in detectors calibration") - monovan_run = RunDescriptor("Run ID (number) for monochromatic vanadium used in absolute units normalization ") - wb_for_monovan_run = RunDescriptorDependent(wb_run,""" white beam run used To calculating monovanadium integrals.\n If not explicitly set, white beam for processing run is used instead """) + sample_run = RunDescriptor("_RUN","Run ID (number) to convert to energy or list of the such run numbers") + wb_run = RunDescriptor("_WB","Run ID (number) for vanadium run used in detectors calibration") + monovan_run = RunDescriptor("_MONO","Run ID (number) for monochromatic vanadium used in absolute units normalization ") + wb_for_monovan_run = RunDescriptorDependent(wb_run,"_MONOWB",""" white beam run used to calculate monovanadium integrals.\n If not explicitly set, white beam for processing run is used instead """) # TODO: do something about it. Second white is explicitly used in - # diagnostics. + # diagnostics but not accessed at all seclond_white = RunDescriptor("Second white beam currently unused in the workflow. Should it be used for Monovan Diagnostics?") #----------------------------------------------------------------------------------- + def getDefaultParameterValue(self,par_name): + """ method to get default parameter value, specified in IDF """ + return prop_helpers.get_default_parameter(self.instrument,par_name) @property def instrument(self): if self._pInstrument is None: diff --git a/Code/Mantid/scripts/Inelastic/Direct/PropertyManager.py b/Code/Mantid/scripts/Inelastic/Direct/PropertyManager.py index 72a8f29e938e17d3090a3e4d155999a27445fd82..b2beb5402bb3b74a56c1198b7253fa4970ba5954 100644 --- a/Code/Mantid/scripts/Inelastic/Direct/PropertyManager.py +++ b/Code/Mantid/scripts/Inelastic/Direct/PropertyManager.py @@ -54,6 +54,9 @@ class PropertyManager(NonIDF_Properties): This is why any new descriptor should never place a key with its name in __dict__. Current design automatically remove IDF name from __dict__ if a descriptor with such name exist, so further development should support this behavior. + 5) In many places (descriptors, RunDescriptor itself), PropertyManager assumed to be a singleton. + If this changes, careful refactoring may be needed + Copyright © 2014 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory diff --git a/Code/Mantid/scripts/Inelastic/Direct/RunDescriptor.py b/Code/Mantid/scripts/Inelastic/Direct/RunDescriptor.py index 86fe1a8dc4ef4944069b94645c30564794a59b80..27390b3a704ff11051ea3f1f9a5e2aa04ed4d78b 100644 --- a/Code/Mantid/scripts/Inelastic/Direct/RunDescriptor.py +++ b/Code/Mantid/scripts/Inelastic/Direct/RunDescriptor.py @@ -1,17 +1,36 @@ """ File contains Descriptors used describe run for direct inelastic reduction """ + from mantid.simpleapi import * from PropertiesDescriptors import * class RunDescriptor(PropDescriptor): - """ descriptor for property energy or range of incident energies to be processed """ - def __init__(self,DocString=None): + """ 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: @@ -23,6 +42,11 @@ class RunDescriptor(PropDescriptor): 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 @@ -30,30 +54,150 @@ class RunDescriptor(PropDescriptor): self._run_ws_name = value ws = mtd[value] self._run_number = ws.getRunNumber() - elif [',',':'] in value: # range of runs provided # TODO: parser - raise NotImplementedError('Range of run numbers is not yet implemented') - else: # filename or run number is provided - self._run_number = value # TODO: parser + 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) """ - if self._run_ws_name: + """ 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: - raise NotImplementedError('Load is not yet implemented') + 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,DocString=None): + 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: @@ -69,3 +213,10 @@ class RunDescriptorDependent(RunDescriptor): 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() \ No newline at end of file diff --git a/Code/Mantid/scripts/test/RunDescriptorTest.py b/Code/Mantid/scripts/test/RunDescriptorTest.py index a9db9d629b12f1cf526c5276a6859ec6b7bc3e35..071f65aebf05764c369cb856c417ea800de954c7 100644 --- a/Code/Mantid/scripts/test/RunDescriptorTest.py +++ b/Code/Mantid/scripts/test/RunDescriptorTest.py @@ -34,11 +34,11 @@ class RunDescriptorTest(unittest.TestCase): tmp_ws_name = '__empty_' + InstrumentName if not mtd.doesExist(tmp_ws_name): LoadEmptyInstrument(Filename=idf_file,OutputWorkspace=tmp_ws_name) - return mtd[tmp_ws_name].getInstrument(); + return mtd[tmp_ws_name].getInstrument() - def test_basic_descr(self): - propman = self.prop_man; + def test_descr_basic(self): + propman = self.prop_man self.assertTrue(propman.sample_run is None) self.assertTrue(PropertyManager.sample_run.get_workspace() is None) @@ -55,8 +55,69 @@ class RunDescriptorTest(unittest.TestCase): self.assertEqual(rez,'Success!') + def test_descr_dependend(self): + propman = self.prop_man + propman.wb_run = 100 + self.assertEqual(propman.wb_run,100) + self.assertEqual(propman.wb_for_monovan_run,100) + + propman.wb_for_monovan_run = 200 + self.assertEqual(propman.wb_for_monovan_run,200) + self.assertEqual(propman.wb_run,100) + + def test_find_file(self): + propman = self.prop_man + propman.sample_run = 11001 + + file=PropertyManager.sample_run.find_file() + self.assertTrue(len(file)>0) + + ext = PropertyManager.sample_run.get_file_ext() + self.assertEqual(ext,'.raw') + + PropertyManager.sample_run.set_file_ext('nxs') + ext = PropertyManager.sample_run.get_file_ext() + self.assertEqual(ext,'.nxs') + + test_dir = config.getString('defaultsave.directory') + + testFile1=os.path.normpath(test_dir+'MAR101111.nxs') + testFile2=os.path.normpath(test_dir+'MAR101111.raw') + + f=open(testFile1,'w') + f.write('aaaaaa'); + f.close() + + f=open(testFile2,'w') + f.write('bbbb') + f.close() + + + propman.sample_run = 101111 + file=PropertyManager.sample_run.find_file() + self.assertEqual(testFile1,os.path.normpath(file)) + PropertyManager.sample_run.set_file_ext('.raw') + file=PropertyManager.sample_run.find_file() + self.assertEqual(testFile2,os.path.normpath(file)) + + os.remove(testFile1) + os.remove(testFile2) + + def test_load_workspace(self): + propman = self.prop_man + + # MARI run with number 11001 and extension raw must among unit test files + propman.sample_run = 11001 + PropertyManager.sample_run.set_file_ext('raw') + + ws = PropertyManager.sample_run.get_workspace() + + self.assertTrue(isinstance(ws, api.Workspace)) + self.assertEqual(ws.name(), PropertyManager.sample_run.get_ws_name()) + + if __name__=="__main__": unittest.main()