Package reflectometry :: Package reduction :: Module properties

Source Code for Module reflectometry.reduction.properties

  1  # This program is public domain. 
  2   
  3  """ 
  4  Support for rarely varying instrument configuration parameters. 
  5   
  6  Instrument configuration parameters will change throughout the 
  7  lifetime of an instrument.  For example, the properties of the 
  8  beam such as wavelength and wavelength divergence will change 
  9  when a new monochromator is installed on the instrument.  Ideally, 
 10  all such parameters would be encoded in the data file (this is 
 11  one goal of the NeXus file format), but this is not the case for 
 12  all instrument formats available today. 
 13   
 14  We cannot simply hard code the current value of the instrument 
 15  parameters in the file reader for the data file format.  Such a 
 16  reader will give bad values for old data files and for new data 
 17  files after the format has changed.  Nor should we burden the user 
 18  with knowing and entering values for such parameters on their own. 
 19   
 20  Instead, we provide support for dated values.  Each instrument has 
 21  a table of values and the date the values come into effect.  When 
 22  a file is loaded, the software scans the list of values, extracting 
 23  all that are in effect on the file date. 
 24   
 25  As instrument parameters change add additional lines to the configuration 
 26  file indicating the new value and the date of the change.  The order of 
 27  # the entries does not matter.  The timestamp on the file will 
 28  determine which value will be used. 
 29   
 30  The format of the entries should be:: 
 31      default.NAME = (VALUE, 'YYYY-MM-DD')  # value after MM/DD/YYYY 
 32      default.NAME = (VALUE, '')            # value at commissioning 
 33   
 34  [Not implemented] Each data reader has an associated URL which 
 35  contains the configuration file for the instrument.  On file 
 36  load, the program will fetch dated values from the URL and use 
 37  them to populate the configuration data for the instrument.  This 
 38  gives control of the instrument parameters to the instrument 
 39  scientist where it belongs. 
 40   
 41  Example 
 42  ======= 
 43   
 44  The following parameters are needed for the NG71reflectometer:: 
 45   
 46      config = properties.DatedValues() 
 47      config.wavelength = (4.76,'')  # in case ICP records the wrong value 
 48   
 49      # Detector response is uniform below 15000 counts/s.  The efficiency 
 50      # curve above 15000 has not been measured. 
 51      config.saturation = (numpy.array([[1,15000,0]]),'') 
 52   
 53      config.detector_distance = (36*25.4, '') # mm 
 54      config.psd_width = (20, '') # mm 
 55      config.slit1_distance = (-75*25.4, '') # mm 
 56      config.slit2_distance = (-14*25.4, '') # mm 
 57      config.slit3_distance = (9*25.4, '') # mm 
 58      config.slit4_distance = (42*25.4, '') # mm 
 59      config.detector_distance = (48*25.4, '2004-02-15') 
 60   
 61  The defaults are used as follows:: 
 62   
 63      class Data: 
 64           def load(filename): 
 65               data = readheaders(filename) 
 66               self.config = config(str(data.date)) 
 67               self.detector.distance = self.config.detector_distance 
 68               ... 
 69   
 70  """ 
 71   
 72  # TODO: provide URI for the instrument configuration 
 73  # Check the URI if the file date is newer than the configuration date.  This 
 74  # will normally be true for the user, but there is no other way to make sure 
 75  # that they are using the most up-to-date values available.  The URI will 
 76  # be given on the constructor as DatedValues('URI'). 
 77   
 78  # TODO: optimize repeated lookups. 
 79  # Currently we scan the table once for each file. A cheap optimization is to 
 80  # identify the range of dates surrounding the current date for which the 
 81  # value is correct and check if the new file falls in that range.  The next 
 82  # level is to cache a set of these ordered by date.  A third option is to 
 83  # build an in-memory database while the configuration values are registered 
 84  # so they don't need to be scanned on file load. 
 85   
 86  # TODO: identify data reader version required 
 87  # As the data format for the instrument evolves, old data readers may not 
 88  # be sufficient to read the new data.  For example, if the reflectometer 
 89  # gets a 2-D detector but the reflectometry data format does not yet 
 90  # support 2-D detectors, then a new reader will be required. 
 91   
 92  # TODO: property sheet editor 
 93  # Once the file parameters are loaded the values are displayed to 
 94  # the user on a property sheet.  Values different from the default 
 95  # are highlighted.  Users can edit the values, with changes noted in 
 96  # the reduction log so that data history is preserved. 
 97   
 98  # TODO: XML support 
 99  # We should probably support to/from xml for the purposes of 
100  # saving and reloading corrections. 
101   
102   
103  import re 
104  datepattern = re.compile(r'^(19|20)\d\d-\d\d-\d\d$') 
105 -class DatedValuesInstance: pass
106 -class DatedValues(object):
107 - def __init__(self):
108 self.__dict__['_parameters'] = {}
109
110 - def __setattr__(self, name, pair):
111 """ 112 Record the parameter value and the date it was set. The pair should 113 contain the value and the date. The assignment will look like: 114 datedvalue.name = (value, 'yyyy-mm-dd') 115 """ 116 # Check that the date is valid 117 value,date = pair 118 assert date == "" or datepattern.match(date), \ 119 "Expected default.%s = (value,'YYYYMMDD')"%(name) 120 121 # Record the value-date pair on the list of values for that parameters 122 if name not in self._parameters: 123 self._parameters[name] = [] 124 self._parameters[name].append(pair)
125
126 - def __call__(self, date):
127 """ 128 Recover the parameter value for a specific date. 129 """ 130 instance = DatedValuesInstance() 131 for name,values in self._parameters.iteritems(): 132 # Sort parameter entries by date 133 values.sort(lambda a,b: cmp(a[0],b[0])) 134 for v,d in values: 135 if d <= date: setattr(instance,name,v) 136 else: break 137 return instance
138
139 -def test():
140 default = DatedValues() 141 default.a = (1,'') 142 default.a = (2,'2000-12-15') 143 default.a = (3,'2004-02-05') 144 assert default('1993-01-01').a == 1 145 assert default('2000-12-14').a == 1 146 assert default('2000-12-15').a == 2 147 assert default('2000-12-16').a == 2 148 assert default('2006-02-19').a == 3
149 150 if __name__ == "__main__": test() 151