From 7782b6888550995b456220d9bb83f35ab7d8d81d Mon Sep 17 00:00:00 2001 From: Alex Buts Date: Fri, 14 Nov 2014 19:18:31 +0000 Subject: [PATCH] refs #10532 Defined generic operations over reduction parameters. --- .../Inelastic/DirectEnergyConversion.py | 2 +- .../DirectEnergyConversionHelpers.py | 104 ---------- .../Inelastic/DirectReductionHelpers.py | 180 ++++++++++++++++++ .../test/DirectEnergyConversionTest.py | 28 --- .../test/DirectReductionHelpersTest.py | 162 +++++++++++++++- 5 files changed, 340 insertions(+), 136 deletions(-) delete mode 100644 Code/Mantid/scripts/Inelastic/DirectEnergyConversionHelpers.py create mode 100644 Code/Mantid/scripts/Inelastic/DirectReductionHelpers.py diff --git a/Code/Mantid/scripts/Inelastic/DirectEnergyConversion.py b/Code/Mantid/scripts/Inelastic/DirectEnergyConversion.py index b61d8f0acd23..e9400e830cb9 100644 --- a/Code/Mantid/scripts/Inelastic/DirectEnergyConversion.py +++ b/Code/Mantid/scripts/Inelastic/DirectEnergyConversion.py @@ -993,7 +993,7 @@ def initialise(self, instr_name,reload_instrument=False): 'diag_van_out_lo', 'diag_van_out_hi', 'diag_van_lo', 'diag_van_hi', 'diag_van_sig', 'diag_variation',\ 'diag_bleed_test','diag_bleed_pixels','diag_bleed_maxrate','diag_hard_mask_file','diag_use_hard_mask_only','diag_background_test_range'] - # before starting long run, makes sence to verify if all files requested for the run are in fact available. Here we specify the properties which describe these files + # before starting long run, makes sense to verify if all files requested for the run are in fact available. Here we specify the properties which describe these files self.__file_properties = ['det_cal_file','map_file','hard_mask_file'] self.__abs_norm_file_properties = ['monovan_mapfile'] diff --git a/Code/Mantid/scripts/Inelastic/DirectEnergyConversionHelpers.py b/Code/Mantid/scripts/Inelastic/DirectEnergyConversionHelpers.py deleted file mode 100644 index c324aa65454f..000000000000 --- a/Code/Mantid/scripts/Inelastic/DirectEnergyConversionHelpers.py +++ /dev/null @@ -1,104 +0,0 @@ -""" -Set of functions to assist with processing instrument parameters relevant to reduction. -""" - -def get_default_parameter(instrument, name): - """ Function gets the value of a default instrument parameter and - assign proper(the one defined in IPF ) type to this parameter - @param instrument -- - """ - - if instrument is None: - raise ValueError("Cannot initiate default parameter, instrument has not been properly defined.") - - type_name = instrument.getParameterType(name) - if type_name == "double": - val = instrument.getNumberParameter(name) - elif type_name == "bool": - val = instrument.getBoolParameter(name) - elif type_name == "string": - val = instrument.getStringParameter(name) - if val[0] == "None" : - return None - elif type_name == "int" : - val = instrument.getIntParameter(name) - else : - raise KeyError(" Instrument: {0} does not have parameter with name: {1}".format(instrument.getName(),name)) - - return val[0] - - - -def build_coupled_keys_dict(pInstrument,par_names,synonims) : - """function to build the dictionary of the keys which are expressed through other keys values - - e.g. to substitute key1 = key2,key3 with key = [value[key1],value[key2]] - """ - if pInstrument is None: - raise ValueError("Cannot initialize default parameter, instrument has not been loaded.") - - # dictionary used for substituting composite keys values. - composite_keys_subst = dict(); - # set of keys which are composite keys - composite_keys_set = set(); - - - for name in par_names : - if pInstrument.getParameterType(name)=="string": - val = self.get_default_parameter(name) - if val is None : - continue - val = val.strip() - keys = val.split(":") - n_keys = len(keys) - if n_keys>1 : # this is the property we want - for i in xrange(0,len(keys)) : - key = keys[i]; - if key in synonims: - key = synonims[key]; - - final_name = name - if final_name in synonims: - final_name = synonims[name]; - - composite_keys_subst[key] = (final_name,i,n_keys); - composite_keys_set.add(name) - - return composite_keys_subst,composite_keys_set - - - -def build_subst_dictionary(synonims_list=None) : - """Function to process "synonims_list" in the instrument parameters string, used to support synonyms in the reduction script - - it takes string of synonyms in the form key1=subs1=subst2=subts3;key2=subst4 and returns the dictionary - in the form dict[subs1]=key1 ; dict[subst2] = key1 ... dict[subst4]=key2 - - e.g. if one wants to use the IDF key word my_detector instead of e.g. norm-mon1-spec, he has to type - norm-mon1-spec=my_detector in the synonyms field of the IDF parameters file. - """ - if not synonims_list : # nothing to do - return dict(); - if type(synonims_list) == dict : # all done - return synonims_list - if type(synonims_list) != str : - raise AttributeError("The synonyms field of Reducer object has to be special format string or the dictionary") - # we are in the right place and going to transform string into dictionary - - subst_lines = synonims_list.split(";") - rez = dict() - for lin in subst_lines : - lin=lin.strip() - keys = lin.split("=") - if len(keys) < 2 : - raise AttributeError("The pairs in the synonyms fields have to have form key1=key2=key3 with at least two values present") - if len(keys[0]) == 0: - raise AttributeError("The pairs in the synonyms fields have to have form key1=key2=key3 with at least two values present, but the first key is empty") - for i in xrange(1,len(keys)) : - if len(keys[i]) == 0 : - raise AttributeError("The pairs in the synonyms fields have to have form key1=key2=key3 with at least two values present, but the key"+str(i)+" is empty") - kkk = keys[i].strip(); - rez[kkk]=keys[0].strip() - - return rez; - diff --git a/Code/Mantid/scripts/Inelastic/DirectReductionHelpers.py b/Code/Mantid/scripts/Inelastic/DirectReductionHelpers.py new file mode 100644 index 000000000000..0bad12f2b125 --- /dev/null +++ b/Code/Mantid/scripts/Inelastic/DirectReductionHelpers.py @@ -0,0 +1,180 @@ +""" +Set of functions to assist with processing instrument parameters relevant to reduction. +""" + +def get_default_parameter(instrument, name): + """ Function gets the value of a default instrument parameter and + assign proper(the one defined in IPF ) type to this parameter + @param instrument -- + """ + + if instrument is None: + raise ValueError("Cannot initiate default parameter, instrument has not been properly defined.") + + type_name = instrument.getParameterType(name) + if type_name == "double": + val = instrument.getNumberParameter(name) + elif type_name == "bool": + val = instrument.getBoolParameter(name) + elif type_name == "string": + val = instrument.getStringParameter(name) + if val[0] == "None" : + return None + elif type_name == "int" : + val = instrument.getIntParameter(name) + else : + raise KeyError(" Instrument: {0} does not have parameter with name: {1}".format(instrument.getName(),name)) + + return val[0] + +def get_default_idf_param_list(pInstrument,synonims_list=None): + """ Obtain default reduction parameters list from the instrument """ + + params = pInstrument.getParameterNames(); + par_list = {}; + for name in params: + par_list[name] = get_default_parameter(pInstrument,name); + + + return par_list; + + + +def build_coupled_keys_dict(param_map,synonims) : + """function to build the dictionary of the keys which are expressed through other keys + + e.g. builds dictionary from strings in a form key1 = key2:key3 + in the form key1 = ['key2','key3'] + """ + + # dictionary used for substituting composite keys. + composite_keys = dict(); + + for name in param_map: + if name in synonims: + final_name = synonims[name]; + else: + final_name = name + composite_keys[final_name]=None; + + param_keys = composite_keys.keys(); + + for name,val in param_map.items() : + if name in synonims: + final_name = synonims[name]; + else: + final_name = name + + if isinstance(val,str): + val = val.strip() + keys_candidates = val.split(":") + n_keys = len(keys_candidates) + # + if n_keys>1 : # this is the property we want to modify + composite_keys[final_name]= list(); + for key in keys_candidates : + if key in synonims: + rkey = synonims[key]; + else: + rkey = key; + if rkey in param_keys: + composite_keys[final_name].append(rkey); + else: + raise KeyError('Substitution key : {0} is not in the list of allowed keys'.format(rkey)); + else: + composite_keys[final_name] = keys_candidates[0]; + else: + composite_keys[final_name]=val; + + return composite_keys + + + +def build_subst_dictionary(synonims_list=None) : + """Function to process "synonims_list" in the instrument parameters string, used to support synonyms in the reduction script + + it takes string of synonyms in the form key1=subs1=subst2=subts3;key2=subst4 and returns the dictionary + in the form dict[subs1]=key1 ; dict[subst2] = key1 ... dict[subst4]=key2 + + e.g. if one wants to use the IDF key word my_detector instead of e.g. norm-mon1-spec, he has to type + norm-mon1-spec=my_detector in the synonyms field of the IDF parameters file. + """ + if not synonims_list : # nothing to do + return dict(); + if type(synonims_list) == dict : # all done + return synonims_list + if type(synonims_list) != str : + raise AttributeError("The synonyms field of Reducer object has to be special format string or the dictionary") + # we are in the right place and going to transform string into dictionary + + subst_lines = synonims_list.split(";") + rez = dict() + for lin in subst_lines : + lin=lin.strip() + keys = lin.split("=") + if len(keys) < 2 : + raise AttributeError("The pairs in the synonyms fields have to have form key1=key2=key3 with at least two values present") + if len(keys[0]) == 0: + raise AttributeError("The pairs in the synonyms fields have to have form key1=key2=key3 with at least two values present, but the first key is empty") + for i in xrange(1,len(keys)) : + if len(keys[i]) == 0 : + raise AttributeError("The pairs in the synonyms fields have to have form key1=key2=key3 with at least two values present, but the key"+str(i)+" is empty") + kkk = keys[i].strip(); + rez[kkk]=keys[0].strip() + + return rez; + +def gen_getter(keyval_dict,key): + """ function returns value from dictionary with substitution + + e.g. if keyval_dict[A] = 10, keyval_dict[B] = 20 and key_val[C] = [A,B] + gen_getter(keyval_dict,A) == 10; gen_getter(keyval_dict,B) == 20; + and gen_getter(keyval_dict,C) == [10,20]; + """ + + if key in keyval_dict: + test_val = keyval_dict[key]; + + + if not isinstance(test_val,list): + return test_val; + + fin_val = list(); + for ik in test_val: + fin_val.append(keyval_dict[ik]); + return fin_val; + + else: + raise KeyError(' key with name: {0} is not in the data dictionary'.format(key)); + +def gen_setter(keyval_dict,key,val): + """ function sets value to dictionary with substitution + + e.g. if keyval_dict[A] = 10, keyval_dict[B] = 20 and key_val[C] = [A,B] + + gen_setter(keyval_dict,A,20) causes keyval_dict[A] == 20 + gen_setter(keyval_dict,B,30) causes keyval_dict[B] == 30 + and gen_getter(keyval_dict,C,[1,2]) causes keyval_dict[A] == 1 and keyval_dict[B] == 2 + """ + + if key in keyval_dict: + test_key = keyval_dict[key]; + if not isinstance(test_key,list): + if isinstance(val,list): + raise KeyError(' Key {0} can not assigned a list value'.format(key)); + else: + keyval_dict[key] = val; + return; + + if isinstance(val,list): + if len(val) != len(test_key): + raise KeyError('Property: {0} needs list of the length {1} to be assigned to it'.format(key,len(test_key))) + else: + raise KeyError(' You can not assign non-list value to list property {0}'.format(key)); + pass + + for i,key in enumerate(test_key): + keyval_dict[key] = val[i]; + return; + else: + raise KeyError(' key with name: {0} is not in the data dictionary'.format(key)); diff --git a/Code/Mantid/scripts/test/DirectEnergyConversionTest.py b/Code/Mantid/scripts/test/DirectEnergyConversionTest.py index 6ea492371686..e08f871c5c55 100644 --- a/Code/Mantid/scripts/test/DirectEnergyConversionTest.py +++ b/Code/Mantid/scripts/test/DirectEnergyConversionTest.py @@ -21,34 +21,6 @@ def setUp(self): def tearDown(self): pass - def test_build_subst_dictionary(self): - self.assertEqual(dict(), DirectEnergyConversion.build_subst_dictionary("")) - self.assertEqual(dict(),DirectEnergyConversion.build_subst_dictionary()) - - self.assertRaises(AttributeError,DirectEnergyConversion.build_subst_dictionary,10) - self.assertRaises(AttributeError,DirectEnergyConversion.build_subst_dictionary,"A=") - self.assertRaises(AttributeError,DirectEnergyConversion.build_subst_dictionary,"B=C;A=") - - rez=dict(); - rez['A']='B'; - self.assertEqual(rez, DirectEnergyConversion.build_subst_dictionary(rez)) - - myDict = DirectEnergyConversion.build_subst_dictionary("A=B") - self.assertEqual(myDict['B'],'A') - - myDict = DirectEnergyConversion.build_subst_dictionary("A=B;C=DD") - self.assertEqual(myDict['B'],'A') - self.assertEqual(myDict['DD'],'C') - myDict = DirectEnergyConversion.build_subst_dictionary("A=B=C=DD") - self.assertEqual(myDict['B'],'A') - self.assertEqual(myDict['DD'],'A') - self.assertEqual(myDict['C'],'A') - - myDict = DirectEnergyConversion.build_subst_dictionary("A = B = C=DD") - self.assertEqual(myDict['B'],'A') - self.assertEqual(myDict['DD'],'A') - self.assertEqual(myDict['C'],'A') - #def test_build_coupled_keys_dict_simple(self): # params = ["]; diff --git a/Code/Mantid/scripts/test/DirectReductionHelpersTest.py b/Code/Mantid/scripts/test/DirectReductionHelpersTest.py index f597b0a21c9b..4e417aa10f5a 100644 --- a/Code/Mantid/scripts/test/DirectReductionHelpersTest.py +++ b/Code/Mantid/scripts/test/DirectReductionHelpersTest.py @@ -1,15 +1,23 @@ from mantid.simpleapi import * from mantid import api import unittest -import inspect -import os, sys -import DirectEnergyConversionHelpers as helpers +import DirectReductionHelpers as helpers class DirectReductionHelpersTest(unittest.TestCase): def __init__(self, methodName): return super(DirectReductionHelpersTest, self).__init__(methodName) + @staticmethod + def getInstrument(InstrumentName='MAR'): + idf_dir = config.getString('instrumentDefinition.directory') + idf_file=api.ExperimentInfo.getInstrumentFilename(InstrumentName) + 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(); + + def test_build_subst_dictionary(self): self.assertEqual(dict(), helpers.build_subst_dictionary("")) self.assertEqual(dict(),helpers.build_subst_dictionary()) @@ -38,6 +46,154 @@ def test_build_subst_dictionary(self): self.assertEqual(myDict['DD'],'A') self.assertEqual(myDict['C'],'A') + def test_get_default_idf_param_list(self): + pInstr=self.getInstrument(); + + param_list = helpers.get_default_idf_param_list(pInstr); + self.assertTrue(isinstance(param_list,dict)) + # check couple of parameters which are certainly in IDF + self.assertTrue('deltaE-mode' in param_list) + self.assertTrue('normalise_method' in param_list) + self.assertTrue('diag_samp_lo' in param_list) + + + def test_build_coupled_keys_dict(self): + kkdict = {}; + kkdict['first']='kkk1:kkk2'; + kkdict['kkk1']=19; + kkdict['kkk2']=1000; + kkdict['other']='unrelated'; + kkdict['second']='ssss1:ssss2:third'; + kkdict['third']='Babara'; + + subst = {}; + subst['ssss1']='kkk1'; + subst['ssss2']='other'; + + subst_dict = helpers.build_coupled_keys_dict(kkdict,subst) + + self.assertEqual(len(subst_dict),6); + self.assertTrue(isinstance(subst_dict['first'],list)) + self.assertEqual(subst_dict['first'][0],'kkk1'); + self.assertEqual(subst_dict['first'][1],'kkk2'); + self.assertEqual(subst_dict['other'],'unrelated'); + self.assertTrue(isinstance(subst_dict['second'],list)) + self.assertEqual(subst_dict['second'][0],'kkk1') + self.assertEqual(subst_dict['second'][1],'other') + self.assertEqual(subst_dict['second'][2],'third') + + def test_build_coupled_keys_dict_ksubst(self): + kkdict = {}; + kkdict['first']='kkk1:kkk2'; + kkdict['kkk1']=19; + kkdict['kkk2']=1000; + kkdict['other']='unrelated'; + kkdict['second']='ssss1:ssss2:third'; + kkdict['third']='Babara'; + + subst = {}; + subst['first']=1; + subst['ssss1']='kkk1'; + subst['ssss2']='other'; + subst['third']=3; + subst['second']=2; + + subst_dict = helpers.build_coupled_keys_dict(kkdict,subst) + + self.assertEqual(len(subst_dict),6); + self.assertTrue(isinstance(subst_dict[1],list)) + self.assertEqual(subst_dict[1][0],'kkk1'); + self.assertEqual(subst_dict[1][1],'kkk2'); + self.assertEqual(subst_dict['other'],'unrelated'); + self.assertTrue(isinstance(subst_dict[2],list)) + self.assertEqual(subst_dict[2][0],'kkk1') + self.assertEqual(subst_dict[2][1],'other') + self.assertEqual(subst_dict[2][2],3) + + def test_gen_getter(self): + kkdict = {}; + kkdict['first']='kkk1:kkk2'; + kkdict['kkk1']=19; + kkdict['kkk2']=1000; + kkdict['other']='unrelated'; + kkdict['second']='ssss1:ssss2:third'; + kkdict['third']='Babara'; + + subst = {}; + subst['ssss1']='kkk1'; + subst['ssss2']='other'; + + subst_dict = helpers.build_coupled_keys_dict(kkdict,subst) + self.assertEqual(helpers.gen_getter(subst_dict,'kkk1'),19); + self.assertEqual(helpers.gen_getter(subst_dict,'kkk2'),1000); + self.assertEqual(helpers.gen_getter(subst_dict,'first'),[19,1000]); + self.assertEqual(helpers.gen_getter(subst_dict,'other'),'unrelated'); + self.assertEqual(helpers.gen_getter(subst_dict,'second'),[19,'unrelated','Babara']); + self.assertEqual(helpers.gen_getter(subst_dict,'third'),'Babara'); + + def test_gen_setter(self): + kkdict = {}; + kkdict['A']=['B','C']; + kkdict['B']=19; + kkdict['C']=1000; + + + helpers.gen_setter(kkdict,'B',0) + self.assertEqual(kkdict['B'],0); + helpers.gen_setter(kkdict,'C',10) + self.assertEqual(kkdict['C'],10); + + self.assertRaises(KeyError,helpers.gen_setter,kkdict,'A',100) + self.assertEqual(kkdict['B'],0); + + helpers.gen_setter(kkdict,'A',[1,10]) + self.assertEqual(kkdict['B'],1); + self.assertEqual(kkdict['C'],10); + + def test_class_property_setter(self): + class test_class(object): + def __init__(self): + kkdict = {}; + kkdict['A']=['B','C']; + kkdict['B']=19; + kkdict['C']=1000; + self.__dict__.update(kkdict) + + + def __setattr__(self,name,val): + helpers.gen_setter(self.__dict__,name,val); + + + def __getattribute__(self,name): + tDict = object.__getattribute__(self,'__dict__'); + if name is '__dict__': + # first call with empty dictionary + return tDict; + else: + return helpers.gen_getter(tDict,name) + pass + + + t1 =test_class(); + + self.assertEqual(t1.B,19); + + t1.B=0; + self.assertEqual(t1.B,0); + + self.assertRaises(KeyError,setattr,t1,'non_existing_property','some value') + + + t1.A = [1,10]; + self.assertEqual(t1.A,[1,10]); + + # This does not work as the assignment occurs to temporary vector + # lets ban partial assignment + #t1.D[0] = 200; + #self.assertEqual(t1.B,200); + # This kind of assignment requests the whole list to be setup + self.assertRaises(KeyError,setattr,t1,'A',200) + if __name__=="__main__": unittest.main()