Package reflectometry :: Package reduction :: Module ncnr_ng1

Source Code for Module reflectometry.reduction.ncnr_ng1

  1  # This program is public domain 
  2  """ 
  3  Data file reader for NCNR NG-1 data. 
  4  """ 
  5   
  6  import numpy,os 
  7  from numpy import inf,cos,pi,arctan2 
  8  from reflectometry.reduction import refldata, icpformat, properties 
  9   
 10   
 11  # Instrument parameters 
 12  # As instrument parameters change add additional lines to this file 
 13  # indicating the new value and the date of the change.  The order of 
 14  # the entries does not matter.  The timestamp on the file will 
 15  # determine which value will be used. 
 16  # The format of the entries should be: 
 17  #      default.NAME = (VALUE, 'YYYY-MM-DD')  # value in effect after DD/MM/YYYY 
 18  #      default.NAME = (VALUE, '')          # value in effect at commissioning 
 19   
 20  # ===================================================================== 
 21  # NG-1 defaults 
 22  ng1default = properties.DatedValues() 
 23  ng1default.wavelength = (4.76,'')  # in case ICP records the wrong value 
 24   
 25  # Detector saturates at 15000 counts/s.  The efficiency curve above 
 26  # 15000 has not been measured. 
 27  ng1default.saturation = (numpy.array([[1,15000,0]]),'') 
 28  ng1default.psd_saturation = (numpy.array([[1,8000,0]]),'') 
 29   
 30  # NG-1 detector closer than slit 4? 
 31  ng1default.detector_distance = (36*25.4, '') # mm 
 32  ng1default.pencil_size = ((100,20), '') # mm 
 33  ng1default.psd_size = ((200,170), '') # mm 
 34  ng1default.slit1_distance = (-75*25.4, '') # mm 
 35  ng1default.slit2_distance = (-14*25.4, '') # mm 
 36  ng1default.slit3_distance = (9*25.4, '') # mm 
 37  ng1default.slit4_distance = (42*25.4, '') # mm 
 38  ng1default.monitor_timestep = (60./100,'') # s ; ICP records 1/100ths of min 
 39   
 40  # ===================================================================== 
 41  # CG-1 defaults 
 42  cg1default = properties.DatedValues() 
 43  cg1default.wavelength = (5.0,'')  # in case ICP records the wrong value 
 44   
 45  # Detector saturates at 15000 counts/s.  The efficiency curve above 
 46  # 15000 has not been measured. 
 47  cg1default.saturation = (numpy.array([[1,15000,0]]),'') 
 48  cg1default.psd_saturation = (numpy.array([[1,8000,0]]),'') 
 49   
 50  # NG-1 detector closer than slit 4? 
 51  cg1default.detector_distance = (1600., '') # mm 
 52  cg1default.pencil_size = ((100,20), '') # mm 
 53  cg1default.psd_size = ((200,170), '') # mm 
 54  cg1default.slit1_distance = (-75*25.4, '') # mm 
 55  cg1default.slit2_distance = (-14*25.4, '') # mm 
 56  cg1default.slit3_distance = (9*25.4, '') # mm 
 57  cg1default.slit4_distance = (42*25.4, '') # mm 
 58  cg1default.monitor_timestep = (60./100,'') # s ; ICP records 1/100ths of min 
 59   
 60  # ===================================================================== 
 61   
62 -def readentries(path):
63 return [NG1Icp(path)]
64
65 -class NG1Icp(refldata.ReflData):
66 probe = "neutron" 67 format = "NCNR ICP" 68
69 - def __init__(self, path, *args, **kw):
70 super(NG1Icp,self).__init__(*args, **kw) 71 self.path = os.path.abspath(path) 72 73 # Load file header 74 data = icpformat.summary(self.path) 75 if data.scantype != 'I': 76 raise TypeError, "Only I-Buffers supported for %s"%self.format 77 78 self.date = data.date 79 self.name = data.filename 80 self.description = data.comment 81 self.dataset = self.name[:5] 82 83 # Lookup defaults as of the current date 84 ext = os.path.splitext(data.filename)[1].lower() 85 if ext.startswith('c'): 86 self.instrument = "NCNR AND/R" 87 self.default = cg1default(str(data.date)) 88 else: 89 self.instrument = "NCNR NG-1" 90 self.default = ng1default(str(data.date)) 91 92 # Plug in instrument defaults 93 self.detector.distance = self.default.detector_distance 94 self.slit1.distance = self.default.slit1_distance 95 self.slit2.distance = self.default.slit2_distance 96 self.slit3.distance = self.default.slit3_distance 97 self.slit4.distance = self.default.slit4_distance 98 self.detector.rotation = 0 # degrees 99 self.monitor.time_step = self.default.monitor_timestep 100 101 # Initialize detector information 102 if data.PSD: 103 self.instrument += " PSD" 104 self.detector.saturation = self.default.psd_saturation 105 self.detector.size = self.default.psd_size 106 else: 107 self.detector.saturation = self.default.saturation 108 self.detector.size = self.default.pencil_size 109 self.display_monitor = data.monitor*data.prefactor 110 111 112 # Warn incorrect wavelength 113 if data.wavelength != self.default.wavelength: 114 self.warn("Unexpected wavelength: expected %g but got %g"\ 115 %(self.default.wavelength,data.wavelength)) 116 self.detector.wavelength = data.wavelength 117 118 # Callback for lazy data 119 self.detector.loadcounts = self.loadcounts 120 121 # Set initial Qz 122 self.resetQ()
123 124
125 - def loadcounts(self):
126 # Load the counts from the data file 127 data = icpformat.read(self.path) 128 return data.counts
129
130 - def load(self):
131 # Load the icp data 132 data = icpformat.read(self.path) 133 if data.counts.ndim == 1: 134 self.detector.dims = (1,1) 135 elif data.counts.ndim == 2: 136 self.detector.dims = (data.counts.shape[1],1) 137 elif data.counts.ndim == 3: 138 self.detector.dims = (data.counts.shape[1],data.counts.shape[2]) 139 else: 140 raise RuntimeError("Data has too many dimensions in "+self.path) 141 142 # Slits are either stored in the file or available from the 143 # motor information. For non-reflectometry scans they may 144 # not be available. 145 if 'a1' in data: self.slit1.x = data.column.a1 146 if 'a2' in data: self.slit2.x = data.column.a2 147 if 'a5' in data: self.slit3.x = data.column.a5 148 if 'a6' in data: self.slit4.x = data.column.a6 149 150 # Angles are either stored in the file or can be calculated 151 # from the motor details. For non-reflectometry scans they 152 # may not be available. 153 if 'a3' in data: self.sample.angle_x = data.column.a3 154 if 'a4' in data: self.detector.angle_x = data.column.a4 155 156 # Polarization was extracted from the comment line 157 self.polarization = data.polarization 158 159 # Monitor counts may be recorded or may be inferred from header 160 if 'monitor' in data: 161 # Prefer the monitor column if it exists 162 self.monitor.counts = data.column.monitor 163 elif data.count_type == 'NEUT': 164 # if count by neutron, the 'monitor' field stores counts 165 self.monitor.counts \ 166 = data.monitor*data.prefactor*numpy.ones(data.points,'i') 167 else: 168 # Need monitor rate for normalization; the application 169 # will have to provide the means of setting the rate 170 # and computing the counts based on that rate. 171 pass 172 173 # Counting time may be recorded or may be inferred from header 174 if data.count_type == 'TIME': 175 # Prefer the target value to the time column when counting by 176 # time because the time column is recorded in minutes rather 177 # than seconds and is not precise enough. 178 # if count by time, the 'monitor' field stores seconds 179 self.monitor.count_time \ 180 = data.monitor*data.prefactor*numpy.ones(data.points,'f') 181 elif 'time' in data: 182 self.monitor.count_time = data.column.time*60 183 else: 184 # Need monitor rate for normalization; the application 185 # will have to provide the means of setting the rate 186 # and computing the time based on that rate. 187 pass 188 189 # Set initial Qz 190 self.resetQ() 191 192 # TODO: if counts are huge we may want to make this lazy 193 self.detector.counts = data.counts
194
195 - def area_correction(self):
196 """ 197 Returns the default area correction that can be applied to the data. 198 """ 199 from reflectometry.reduction import areacor 200 nx,ny = self.detector.dims 201 Ax,Ay = self.detector.solid_angle 202 wx = (1+0.15*cos(2*pi*numpy.arange(nx)/32.))/nx * Ax 203 wy = (1+0.15*cos(2*pi*numpy.arange(ny)/32.))/ny * Ay 204 return areacor.AreaCorrection(wx,wy,source="15% * cos(2 pi k/32)")
205