Package reflectometry :: Package reduction :: Module refldata

Source Code for Module reflectometry.reduction.refldata

  1  # This program is public domain 
  2  """ 
  3  Reflectometry data representation. 
  4   
  5  Need to support collections of data from TOF, monochromatic 
  6  and white beam instruments. 
  7   
  8  Conceptually each data point is a tuple:: 
  9   
 10      incident angles (sample tilt and rotation) 
 11      reflected angles (polar angle of detector pixel) 
 12      slit distances and openings 
 13      detector pixel distance and size 
 14      incident/reflected polarization 
 15      wavelength distribution 
 16      measurement start and duration 
 17      monitor and detector counts 
 18      sample environment 
 19   
 20  Reflectometers are either vertical or horizontal geometry. 
 21  For vertical geometry (sample surface parallel to gravity), 
 22  x refers to horizontal slit opening and horizontal detector 
 23  pixels.  For horizontal geometry (sample surface perpendicular 
 24  to gravity) x refers to vertical slit opening and vertical 
 25  detector pixels. Other than gravitational corrections to 
 26  resolution and detector pixels, the analysis for the two 
 27  instrument types should be identical. 
 28   
 29  Monochromatic reflectometers have a single wavelength per 
 30  measurement but scan the measurements.  Time-of-flight and 
 31  polychromatic reflectometers have multiple wavelengths per 
 32  measurement but perform one measurement.  In either case 
 33  a dataset consists of detector frames versus Q.  We will 
 34  ignore scans on multichannel instruments since these can 
 35  be treated as combined independent scans with non-overlapping 
 36  data, and handled outside this data structure. 
 37   
 38  Data points are gathered together into measurements.  Some 
 39  files may have multiple measurements, and other measurements 
 40  may be spread over multiple files.  Files may be local or 
 41  remote, ascii or binary.  It is up to the individual format 
 42  reader to assign map measurements to files. 
 43   
 44  Different polarization states will be treated as belonging 
 45  to different measurements.  These will need to be aligned 
 46  before polarization correction can be performed.  Multiple 
 47  measurements may occur on the same detector.  In this 
 48  case each measurement should have a separate 'region of 
 49  interest' to isolate it from the others, presenting a virtual 
 50  detector to the reduction and analysis program. 
 51   
 52  Some information about the measurements may be missing 
 53  from the files, or recorded incorrectly.  Changes and 
 54  additions to the metadata must be recorded in any reduced 
 55  data file format, along with a list of transformations 
 56  that went into the reduction. 
 57   
 58  See notes in properties.py regarding dated values. 
 59  """ 
 60  __all__ = ['ReflData'] 
 61   
 62  import datetime 
 63  import weakref 
 64   
 65  import numpy 
 66  from numpy import inf, pi, sin, cos, arcsin, arctan2, sqrt 
 67   
 68  from qxqz import ABL_to_QxQz 
 69   
 70  # TODO: attribute documentation and units should be integrated with the 
 71  # TODO: definition of the attributes.  Value attributes should support 
 72  # TODO: unit conversion 
73 -class Slit(object):
74 """ 75 Define a slit for the instrument. This is needed for correct resolution 76 calculations and for ab initio footprint calculations. 77 78 distance (inf millimetre) 79 Distance from sample. Positive numbers are after the sample, 80 negative numbers are before the sample along the beam path. 81 offset (4 x 0 millimetre) 82 Offset of the slit blades relative to the distance from the sample. 83 For vertical geometry, this is left, right, up, down. For horizontal 84 geometry this is up, down, left, right. Offset + distance gives the 85 distances of the individual blades from the sample, with negative 86 numbers occurring before the sample position and positive numbers 87 after. 88 shape (shape='rectangular') 89 Whether we have slit blades ('rectangular') or a circular 90 aperature ('circular'). 91 x (n x inf millimetre) 92 Slit opening in the primary direction. For vertical geometry 93 this is the horizontal opening, for horizontal geometry this is 94 the vertical opening. This may be a constant (fixed slits) or 95 of length n for the number of measurements. 96 y (n x inf millimetre) 97 Slit opening in the secondary direction. This may be a constant 98 (fixed slits) or of length n for the number of measurements. 99 """ 100 properties = ['distance','offset','x','y','shape'] 101 distance = inf 102 offset = [0.]*4 103 x = inf 104 y = inf 105 shape = "rectangular" # rectangular or circular 106
107 - def __init__(self, **kw): _set(self,kw)
108 - def __str__(self): return _str(self)
109
110 -class Sample(object):
111 """ 112 Define the sample geometry. Size and shape areneeded for correct 113 resolution calculations and for ab initio footprint calculations. 114 Angles are needed for correct calculation of Q. Rotation and 115 environment are for display to the user. 116 117 description ("") 118 Sample description, if available from the file. 119 width (inf millimetre) 120 Width of the sample in the primary direction. For fixed slits 121 the footprint of the beam on the sample decreases with angle 122 in this direction. 123 length (inf millimetre) 124 Width of the sample in the secondary direction. The footprint 125 is independent of angle in this direction. 126 thickness (inf millimetre) 127 Thickness of the sample. 128 substrate_sld (10^-6 Angstrom^-2) 129 To plot Fresnel reflectivity we need to know the substrate 130 scattering length density. The default is to assume silicon. 131 shape ('rectangular') 132 Shape is 'circular' or 'rectangular' 133 angle_x (n x 0 degree) 134 Angle between neutron beam and sample surface in the primary 135 direction. This may be constant or an array of length n for 136 the number of measurements. 137 angle_y (n x 0 degree) 138 Angle between the neutron beam and sample surface in the 139 secondary direction. This may be constant or an array of 140 length n for the number of measurements. This is known as 141 tilt on some instruments. 142 rotation (n x 0 degree) 143 For off-specular reflectivity the orientation of the patterned 144 array on the surface of the sample affects the computed theory. 145 This value is not needed for data reduction, but it should be 146 reported to the user during reduction and carried through to 147 the reduced file for correct analysis. 148 environment ({}) 149 Sample environment data. See Environment class for a list of 150 common environment data. 151 """ 152 properties = ['description','width','length','thickness','shape', 153 'angle_x','angle_y','rotation','substrate_sld'] 154 description = '' 155 width = inf # mm 156 length = inf # mm 157 thickness = inf # mm 158 shape = 'rectangular' # rectangular or circular or irregular 159 angle_x = 0 # degree 160 angle_y = 0 # degree 161 rotation = 0 # degree 162 substrate_sld = 2.07 # inv A (silicon substrate for neutrons) 163
164 - def __init__(self, **kw):
165 self.environment = {} 166 _set(self,kw)
167 - def __str__(self): return _str(self)
168 169 170
171 -class Environment(object):
172 """ 173 Define sample environment data for the measurements such as 174 temperature (kelvin) 175 pressure (pascal) 176 relative_humidity (%) 177 electric_field (V/m) 178 magnetic_field (tesla) 179 stress_field (pascal) 180 181 The data may be a constant, a series of values equal to 182 the number of scan points, or a series of values and times. 183 The average, max and min over all scan points, and the 184 value, max and min for a particular scan point may be 185 available. 186 187 Some measurements are directional, and will have a polar 188 and azimuthal angle associated with them. This may be 189 constant for the entire scan, or stored separately with 190 each magnitude measurement. 191 192 name 193 Name of environment variable 194 units 195 Units to report on graphs 196 average, minimum, maximum 197 Statistics on all measurements 198 value 199 Magnitude of the measurement 200 start 201 Start time for log 202 time (seconds) 203 Measurement time relative to start 204 polar_angle (degree) 205 azimuthal_angle (degree) 206 Provide orientation relative to the sample surface for 207 directional parameters: 208 * x is polar 0, azimuthal 0 209 * y is polar 90, azimuthal 0 210 * z is azimuthal 90 211 """ 212 pass
213 214
215 -class Beamstop(object):
216 """ 217 Define the geometry of the beamstop. This is used by the 218 detector class to compute the shadow of the beamstop on the 219 detector. The beamstop is assumed to be centered on the 220 direct beam regardless of the position of the detector. 221 222 distance (0 millimetre) 223 Distance from sample to beamstop. Note: this will need to 224 be subtracted from the distance from detector to beamstop. 225 shape ('rectangular') 226 Shape is 'circular' or 'rectangular' 227 width (0 millimetre) 228 Width of the beamstop in the primary direction. For circular 229 beamstops, this is the diameter. 230 length (0 millimetre) 231 Width of the beamstop in the secondary direction. For circular 232 beamstops, this is the diameter. 233 offset (2 x millimetre) 234 Offset of the beamstop from the center of the beam. 235 ispresent (False) 236 True if beamstop is present in the experiment. 237 """ 238 properties = ['distance','width','length','shape', 239 'x_offset','y_offset','ispresent'] 240 distance = 0 # mm 241 width = 0 # mm 242 length = 0 # mm 243 shape = 'rectangular' # rectangular or circular 244 offset = [0,0] # mm 245 ispresent = False 246
247 - def __init__(self, **kw): _set(self,kw)
248 - def __str__(self): return _str(self)
249
250 -class Detector(object):
251 """ 252 Define the detector properties. Note that this defines a virtual 253 detector. The real detector may have e.g., multiple beam paths 254 incident upon it, and be split into two virtual detectors when 255 the file is loaded. 256 257 Direction x refers to the primary direction, and direction y to 258 the secondary direction. For vertical geometry, the primary 259 direction is in the horizontal plane and the secondary direction 260 is in the vertical plane. For horizontal geometry these are 261 reversed. This allows the reduction software to be simpler, 262 but may complicate file loading from formats which store values 263 in absolute geometry. 264 265 Geometry 266 ======== 267 dims (2 x pixels) 268 Dimensions of the detector, [nx,ny]. For pencil detectors this 269 should be [1,1]. For position sensitive detectors, this should be 270 [nx,1]. For area detectors, this should be [nx,ny]. 271 distance (millimetre) 272 Distance from the sample to the detector. 273 size (2 x millimetre) 274 Detector size, [x,y]. Default is 1 mm x 1 mm. 275 solid_angle (2 x radian) 276 Detector solid angle [x,y], calculated from distance and size. 277 center (2 x millimetre) 278 Location of the center pixel [x,y] relative to the detector arm. 279 widths_x (nx x millimetre) 280 widths_y (ny x millimetre) 281 Pixel widths in x and y. We assume no space between the pixels. 282 angle_x (n x degree) 283 angle_y (n x degree) 284 Angle of the detector arm relative to the main beam in x and y. 285 This may be constant or an array of length n for the number of 286 measurements in the scan. 287 rotation (degree) 288 Angle of rotation of the detector relative to the beam. This 289 will affect how vertical integration in the region of interest 290 is calculated. Ideally the detector would not be rotated, though 291 misalignment can sometimes occur. 292 293 Efficiency 294 ========== 295 efficiency (nx x ny %) 296 Efficiency of the individual pixels; this is an array of the same 297 shape as the detector, giving the relative efficiency of each pixel, 298 or 1 if the efficiency is unknown. 299 TODO: do we need variance? 300 saturation (k [%, counts/second, uncertainty]) 301 Given a measurement of a given number of counts versus expected 302 number of counts on the detector (e.g., as estimated by scanning 303 a narrow slit across the detector to measure the beam profile, 304 then measuring increasingly large portions of the beam profile), 305 this can be converted to an efficiency correction per count rate 306 which can be applied to all data read with this detector. The 307 value for deadtime should be a tuple of three vectors: efficiency, 308 uncertainty and count rate. Below the lowest count rate the 309 detector is considered to be 100% efficient (any baseline 310 inefficiency will be normalized when comparing the measured 311 reflection to the measured beam). Beyond the highest count 312 rate, the detector is considered saturated. The uncertainty 313 is just the sqrt(counts)/time. 314 315 316 Note: Given the nature of the detectors (detecting ionization 317 caused by neutron capture) there is undoubtably a local 318 saturation level, but this is likely masked by the usual 319 detector electronics which can only detect one event at a 320 time regardless of where it occurs on the detector. 321 322 Measurement 323 =========== 324 wavelength (k nanometre) 325 Wavelength for each channel 326 wavelength_resolution (k %) 327 Wavelength resolution of the beam for each channel using 1-sigma 328 gaussian approximation dL, expressed as 100*dL/L. The actual 329 wavelength distribution is considerably more complicated, being 330 approximately square for multi-sheet monochromators and highly 331 skewed on TOF machines. 332 time_of_flight (k+1 millisecond) 333 Time boundaries for time-of-flight measurement 334 counts (nx x ny x k counts OR n x nx x ny counts) 335 nx x ny detector pixels 336 n number of measurements 337 k time/wavelength channels 338 339 Runtime Facilities 340 ================== 341 loadcounts (function returning counts) 342 Counts can be assigned using 343 data.detector.counts = weakref.ref(counts) 344 When the counts field is accessed, the reference will be resolved. 345 If it yields None, then loadcounts will be called and assigned to 346 counts as a weak reference. In this way large datasets can be 347 removed from memory when not in active use. 348 """ 349 properties=["dims",'distance','size','center','widths_x','widths_y', 350 'angle_x','angle_y','rotation','efficiency','saturation', 351 'wavelength','time_of_flight','counts'] 352 dims = [1,1] # i,j 353 distance = None # mm 354 size = [1,1] # mm 355 center = [0,0] # mm 356 widths_x = 1 # mm 357 widths_y = 1 # mm 358 angle_x = 0 # degree 359 angle_y = 0 # degree 360 rotation = 0 # degree 361 efficiency = 1 # proportion 362 saturation = inf # counts/sec 363 wavelength = 1 # angstrom 364 time_of_flight = None # ms 365
366 - def _solid_angle(self):
367 """Detector solid angle [x,y] (radians)""" 368 return 2*arctan2(numpy.asarray(self.size)/2.,self.distance)
369 solid_angle = property(_solid_angle,doc=_solid_angle.__doc__) 370 371 372 # Raw counts are cached in memory and loaded on demand. 373 # Rebinned and integrated counts for the region of interest 374 # are stored in memory. 375 #_pcounts = lambda:None
376 - def loadcounts(self):
377 """Load the data""" 378 raise NotImplementedError,\ 379 "Data format must set detector.counts or detector.loadcounts"
380 - def _pcounts(self):
381 """Simulated empty weak reference""" 382 return None
383 - def _getcounts(self):
384 counts = self._pcounts() 385 if counts is None: 386 counts = self.loadcounts() 387 self._pcounts = weakref.ref(counts) 388 return counts
389 - def _setcounts(self, value):
390 # File formats which are small do not need to use weak references, 391 # however, for convenience the should use the same interface, which 392 # is value() rather than value. 393 if isinstance(value,weakref.ref): 394 self._pcounts = value 395 else: 396 def static(): return value 397 self._pcounts = static
398 #self._pcounts = lambda:value
399 - def _delcounts(self):
400 _pcounts = lambda:None
401 counts = property(_getcounts,_setcounts,_delcounts) 402 403
404 - def __init__(self, **kw): _set(self,kw)
405 - def __str__(self): return _str(self)
406 407
408 -class ROI(object):
409 """ 410 Detector region of interest. 411 412 Defines a rectangular region of interest on the detector which 413 is used for defining frames. This can be used for example to 414 split a single detector with both polarization states (via 415 transmission and reflection off a supermirror) into two virtual 416 detectors. 417 418 xlo, xhi (pixels) 419 ylo, yhi (pixels) 420 """ 421 properties = ['xlo','xhi','ylo','yhi'] 422 xlo = None 423 xhi = None 424 ylo = None 425 yhi = None 426
427 - def __init__(self, **kw): _set(self,kw)
428 - def __str__(self): return _str(self)
429
430 -class Monitor(object):
431 """ 432 Define the monitor properties. 433 434 The monitor is essential to the normalization of reflectometry data. 435 Reflectometry is the number of neutrons detected divided by the 436 number of neutrons incident on the sample. To compute this ratio, 437 the incident and detected neutrons must be normalized to the neutron 438 rate, either counts per monitor count, counts per second or counts 439 per unit of source power (e.g., coulombs of protons incident on the 440 detector, or megawatt hours of reactor power). 441 442 counts (n x k counts) 443 Number of counts measured. For scanning instruments there is 444 a separate count for each of the n measurements. For TOF 445 instruments there is a separate count for each of k time 446 channels. Counts may be absent, in which case normalization 447 must be by time or by monitor. In some circumstances the 448 user may generate a counts vector, for example by estimating 449 the count rate by other means, in order to combine data 450 measured by time with data measured by monitor when the 451 monitor values are otherwise unreliable. Variance is assumed 452 to be the number of counts, after any necessary rebinning. 453 count_time (n seconds) 454 Duration of the measurement. For scanning instruments, there is 455 a separate duration for each measurement. For TOF, this is a 456 single value equal to the duration of the entire measurement. 457 source_power (n source_power_units) 458 The source power for each measurement. For situations when the 459 monitor cannot be trusted (which can happen from time to time on 460 some instruments), we can use the number of protons incident on 461 the target (proton charge) or the energy of the source (reactor 462 power integrated over the duration of each measurement) as a proxy 463 for the monitor. So long as the we normalize both the slit 464 measurement and the reflectivity measurement by the power, this 465 should give us a reasonable estimate of the reflectivity. If 466 the information is available, this will be a better proxy for 467 monitor than measurement duration. 468 base ('time' | 'counts' | 'power') 469 The measurement rate basis which should be used to normalize 470 the data. This is initialized by the file loader, but may 471 be overridden during reduction. 472 time_step (seconds) 473 The count_time timer has a reporting unit, e.g. second, or 474 millisecond, or in the case of NCNR ICP files, hundredths of 475 a minute. The measurement uncertainty for the count time 476 is assumed to be uniform over the time_step, centered on 477 the reported time, with a gaussian approximation of uncertainty 478 being sqrt(time_step/12). 479 start_time (n seconds) 480 For scanning instruments the start of each measurement relative 481 to start of the scan. Note that this is not simply sum of the 482 count times because there may be motor movement between 483 measurements. The start time is required to align the measurement 484 values with environment parameters, and for calculation of He3 485 polarization. For TOF, this should be zero. 486 distance (metre) 487 Distance from the sample. This is not used by reduction but 488 may be of interest to the user. 489 sampled_fraction ([0,1]) 490 Portion of the neutrons that are sampled by the monitor. If the 491 monitor is after the second slit, the monitor value can be used to 492 estimate the the counts on the detector, scaled by the sampled 493 fraction. Otherwise a full slit scan is required to normalize 494 the reflectivity. This is the inverse of the detector to monitor 495 ratio used to normalize data on some instruments. 496 time_of_flight (k+1 millisecond) 497 Time boundaries for the time-of-flight measurement 498 source_power_units ('coulombs' | 'megawatthours') 499 Units for source power. 500 source_power_variance (n source_power_units) 501 Variance in the measured source power 502 monitor_rate (counts/second) 503 For normalizing by counts when only count time is recorded, we 504 need an estimate of the monitor rate during the measurement. 505 """ 506 properties = ['distance','sampled_fraction','counts','start_time', 507 'count_time','time_step','time_of_flight','base', 508 'monitor_rate','source_power','source_power_units'] 509 distance = None 510 sampled_fraction = None 511 counts = None 512 start_time = None 513 count_time = None 514 time_step = 1 # Default to nearest second 515 time_of_flight = None 516 base = 'counts' 517 source_power = 1 # Default to 1 MW power 518 source_power_units = "MW" 519 source_power_variance = 0 520 monitor_rate = 0 # counts/sec 521
522 - def __init__(self, **kw): _set(self,kw)
523 - def __str__(self): return _str(self)
524
525 -class Moderator(object):
526 """ 527 Time of flight calculations require information about the moderator. 528 Primarily this is the length of the flight path from moderator to 529 monitor or detector required to compute wavelength. 530 531 Moderator temperature is also recorded. The user should probably 532 be warned when working with datasets with different moderator 533 temperatures since this is likely to affect the wavelength 534 spectrum of the beam. 535 536 distance (metre) 537 Distance from moderator to sample. This is negative since the 538 monitor is certainly before the sample. 539 temperature (kelvin) 540 Temperature of the moderator 541 type (string) 542 For information only at this point. 543 """ 544 properties = ['distance','temperature','type'] 545 distance = None 546 temperature = None 547 type = 'Unknown' 548
549 - def __init__(self, **kw): _set(self, kw)
550 - def __str__(self): return _str(self)
551
552 -class Warning(object):
553 """ 554 A warning is an information message and a possible set of actions to 555 take in response to the warning. 556 557 The user interface can query the message and the action list, generate 558 a dialog on the basis of the information. Actions may have associated 559 attributes that need to be set for the action to complete. 560 """ 561 pass
562
563 -class WarningWavelength(Warning):
564 """ 565 Unexpected wavelength warning. 566 567 This warning is attached to any dataset which has an unexpected 568 wavelength stored in the file (more than 1% different from the 569 default wavelength for the instrument). 570 571 Various actions can be done in response to the warning, including 572 always taking the default value for this instrument, overriding for 573 every value in the dataset 574 """ 575 pass
576
577 -class ReflData(object):
578 """ 579 slit1,slit2 (Slit) 580 Presample slits 581 slit3,slit4 (Slit) 582 Post sample slits 583 sample 584 Sample geometry 585 detector 586 Detector geometry, efficiency and counts 587 monitor 588 Counts and/or durations 589 polarization 590 '' unpolarized 591 '+' spin up 592 '-' spin down 593 '++','--' non-spin-flip 594 '-+','+-' spin flip 595 points 596 For scanning instruments, the number of measurements. 597 channels 598 For time of flight, the number of time channels. For white 599 beam instruments, the number of analysers. 600 reversed (False) 601 True if the measurement is reversed, in which case sample 602 and detector angles need to be negated and pixel directions 603 reversed when computing pixel coordinates. 604 roi 605 Region of interest on the detector. 606 display_monitor (counts) 607 Default monitor to use when displaying data from this dataset 608 609 File details 610 ============ 611 instrument (string) 612 Name of a particular instrument 613 probe ('neutron' or 'xray') 614 Type of radiation used to probe the sample. 615 path (string) 616 Location of the datafile 617 entry (string) 618 Entry identifier if more than one entry per file 619 name (string) 620 Name of the dataset. This may be a combination of filename and 621 entry number. 622 description (string) 623 Description of the entry. 624 date (timestamp) 625 Starting date and time of the measurement. 626 duration (second) 627 Duration of the measurement. 628 warnings 629 List of warnings generated when the file was loaded 630 intent (string) 631 Purpose of the measurement. 632 intensity: Normalization scan for computing absolute reflection 633 specular: Specular intensity measurement 634 q_offset: Background measurement, offset from Qx=0 in Q 635 sample_offset: Background measurement, sample rotated 636 detector_offset: Background measurement, detector moved 637 slice: Slice through Qx-Qz 638 area: Measurement of a region of Qx-Qz plane 639 alignment: Sample alignment measurement 640 other: Some other kind of measurement 641 642 Format specific fields (ignored by reduction software) 643 ====================== 644 file (handle) 645 Format specific file handle, for actions like showing the summary, 646 updating the data and reading the frames. 647 """ 648 properties = ['instrument','geometry','probe','points','channels', 649 'name','description','date','duration','attenuator', 650 'polarization','reversed','warnings','path','entry'] 651 geometry = "vertical" 652 probe = "unknown" 653 format = "unknown" 654 path = "unknown" 655 entry = "" 656 points = 1 657 channels = 1 658 name = "" 659 description = "" 660 date = datetime.datetime(1970,1,1) 661 duration = 0 662 file = None 663 attenuator = 1. 664 polarization = '' 665 reversed = False 666 warnings = None 667 messages = None 668
669 - def _getdR(self): return sqrt(self.varR)
670 - def _setdR(self, dR): self.varR = dR**2
671 dR = property(_getdR,_setdR) 672 673 # Data representation for generic plotter as (x,y,z,v) 674 # TODO: subclass Data so we get pixel edges calculations
675 - def _getx(self): return self.Qz
676 - def _gety(self): return self.Qx
677 - def _getz(self): return self.Qy
678 - def _getv(self): return self.R
679 - def _getdv(self): return sqrt(self.varR)
680 x,xlabel,xunits = property(_getx),"Qx","inv A" 681 y,ylabel,yunits = property(_gety),"Qy","inv A" 682 z,zlabel,zunits = property(_getz),"Qz","inv A" 683 v,dv = property(_getv),property(_getdv) 684 # vlabel and vunits depend on monitor normalization 685
686 - def __init__(self, **kw):
687 # Note: because _set is ahead of the following, the caller will not 688 # be able to specify sample, slit, detector or monitor on creation, 689 # but will instead have to use those items provided by the class. 690 _set(self,kw) 691 self.sample = Sample() 692 self.slit1 = Slit() 693 self.slit2 = Slit() 694 self.slit3 = Slit() 695 self.slit4 = Slit() 696 self.detector = Detector() 697 self.monitor = Monitor() 698 self.moderator = Moderator() 699 self.warnings = [] 700 self.roi = ROI() 701 self.messages = []
702
703 - def __str__(self):
704 base = [_str(self)] 705 others = [str(s) for s in [self.slit1,self.slit2,self.slit3,self.slit4, 706 self.sample,self.detector,self.monitor, 707 self.roi]] 708 return "\n".join(base+others)
709
710 - def warn(self,msg):
711 """Record a warning that should be displayed to the user""" 712 self.warnings.append(msg)
713
714 - def log(self,msg):
715 """Record corrections that have been applied to the data""" 716 self.messages.append(msg)
717
718 - def apply(self,correction):
719 """Allow alternative syntax: data.apply(correction)""" 720 self.log(str(correction)) 721 correction.apply(self)
722
723 - def resetQ(self):
724 """Recompute Qx,Qz from geometry and wavelength""" 725 A,B = self.sample.angle_x,self.detector.angle_x 726 L = self.detector.wavelength 727 Qx,Qz = ABL_to_QxQz(A,B,L) 728 self.Qx,self.Qz = Qx,Qz
729
730 -def _str(object):
731 """ 732 Helper function: document data object by convert attributes listed in 733 properties into a string. 734 """ 735 cls = object.__class__.__name__ 736 props = [a+"="+str(getattr(object,a)) for a in object.properties] 737 return "%s %s"%(cls,"\n ".join(props))
738 739
740 -def _set(object,kw):
741 ''' 742 Helper function: distribute the __init__ keyward paramters to 743 individual attributes of an object, raising AttributeError if 744 the class does not define the given attribute. 745 746 Example: 747 748 def __init__(self, **kw): _set(self,kw) 749 ''' 750 for k,v in kw.iteritems(): 751 if hasattr(self,k): 752 setattr(object,k,v) 753 else: 754 raise AttributeError, "Unknown attribute %s"%(k) 755 756 # Ignore the remainder of this file --- I don't yet have the computational 757 # interface set up. 758 759 """ 760 Computed values 761 =============== 762 edges_x (metric=['pixel'|'mm'|'degrees'|'radians'],frame=0) 763 Returns the nx+1 pixel edges of the detector in the given units. 764 In distance units, this is the distance relative to the center 765 of the detector arm. 766 edges_y (metric=['pixel'|'mm'|'degrees'|'radians'],frame=0) 767 Returns the ny+1 pixel edges of the detector in the given units. 768 769 def resolution(self): 770 return 771 """
772 773 # === Interaction with individual frames ===
774 -class Reader(ReflData):
775 - def numframes(self):
776 """ 777 Return the number of detector frames available. 778 """ 779 return self.channels*self.points
780
781 - def loadframes(self):
782 """ 783 Convert raw frames into a form suitable for display. 784 """ 785 # Hold a reference to the counts so that they are not purged 786 # from memory during the load operation. 787 xlo,xhi,ylo,yhi = self.roi 788 counts = self.detector.counts 789 nq = zhi-zlo+1 790 nx = self.detector.shape[0] 791 ny = self.detector.shape[1] 792 if ny == 1: 793 self.zx = counts[zlo:zhi+1,:] 794 else: 795 xy = numpy.zeros((nx,ny),dtype='float32') 796 zx = numpy.zeros((nq,nx),dtype='float32') 797 self.framerange = Limits() # Keep track of total range 798 for i in range(zlo,zhi): 799 v = self.frame(i) 800 self.framerange.add(v,dv=sqrt(v)) 801 xy += v 802 zx[i-zlo,:] = numpy.sum(v[:,ylo:yhi],axis=1) 803 self.xy = xy 804 self.zx = zx
805
806 - def frame(self,index):
807 """ 808 Return the 2-D detector frame for the given index k. For 809 multichannel instruments, index is the index for the channel 810 otherwise index is the measurement number. 811 812 The result is undefined if the detector is not a 2-D detector. 813 """ 814 if self.channels > 1: 815 return self.detector.counts[:,index] 816 else: 817 return self.detector.counts[index,:]
818 819 820
821 -def shadow(f, beamstop, frame):
822 """ 823 Construct a mask for the detector frame indicating which pixels 824 are outside the shadow of the beamstop. This pixels should not 825 be used when estimating sample background. Note that this becomes 826 considerably more tricky when angular divergence and gravity 827 are taken into account. The mask should include enough of the 828 penumbra that these effects can be ignored. 829 830 Currently this function returns no shadow. 831 """ 832 mask = numpy.ones(self.detector.shape,'int8') 833 if beamstop.ispresent: 834 # calculate location of the beamstop centre relative to 835 # the detector. 836 pass 837 return mask
838