source: sasview/calculator/resolution_calculator.py @ aad74b3

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since aad74b3 was be6e99a, checked in by Jae Cho <jhjcho@…>, 13 years ago

fixed problems in the estimator

  • Property mode set to 100644
File size: 31.3 KB
Line 
1"""
2This object is a small tool to allow user to quickly
3determine the variance in q  from the
4instrumental parameters.
5"""
6from instrument import Sample
7from instrument import Detector
8from instrument import Neutron
9from instrument import Aperture
10# import math stuffs
11from math import pi
12from math import sqrt
13import math 
14import scipy
15import numpy
16
17#Plank's constant in cgs unit
18_PLANK_H = 6.62606896E-27
19#Gravitational acc. in cgs unit
20_GRAVITY = 981.0
21
22class ResolutionCalculator(object):
23    """
24    compute resolution in 2D
25    """
26    def __init__(self):
27       
28        # wavelength
29        self.wave = Neutron()
30        # sample
31        self.sample = Sample()
32        # aperture
33        self.aperture = Aperture()
34        # detector
35        self.detector = Detector()
36        # 2d image of the resolution
37        self.image = []
38        # resolutions
39        # lamda in r-direction
40        self.sigma_lamda = 0
41        # x-dir (no lamda)
42        self.sigma_1 = 0
43        #y-dir (no lamda)
44        self.sigma_2 = 0
45        # 1D total
46        self.sigma_1d = 0
47        # q min and max
48        self.qx_min = -0.3
49        self.qx_max = 0.3
50        self.qy_min = -0.3
51        self.qy_max = 0.3
52        # q min and max of the detector
53        self.detector_qx_min = -0.3
54        self.detector_qx_max = 0.3
55        self.detector_qy_min = -0.3
56        self.detector_qy_max = 0.3
57        # plots
58        self.plot = None
59        # instrumental params defaults
60        self.mass = 0
61        self.intensity = 0
62        self.wavelength = 0
63        self.wavelength_spread = 0
64        self.source_aperture_size = []
65        self.source2sample_distance = []
66        self.sample2sample_distance = []
67        self.sample_aperture_size = []
68        self.sample2detector_distance = []
69        self.detector_pix_size = []
70        self.detector_size = []
71        # get all the values of the instrumental parameters
72        self.get_all_instrument_params()
73       
74    def compute_and_plot(self, qx_value, qy_value, qx_min, qx_max, 
75                          qy_min, qy_max, coord = 'cartesian'):
76        """
77        Compute the resolution
78        : qx_value: x component of q
79        : qy_value: y component of q
80        """
81        # compute 2d resolution
82        _, _, sigma_1, sigma_2, sigma_r = \
83                            self.compute(qx_value, qy_value, coord)
84        # make image
85        image = self.get_image(qx_value, qy_value, sigma_1, sigma_2, sigma_r, 
86                               qx_min, qx_max, qy_min, qy_max, coord)
87        # plot image
88        return self.plot_image(image) 
89
90    def compute(self, qx_value, qy_value, coord = 'cartesian'):
91        """
92        Compute the Q resoltuion in || and + direction of 2D
93        : qx_value: x component of q
94        : qy_value: y component of q
95        """
96        coord = 'cartesian'
97        # make sure to update all the variables need.
98        self.get_all_instrument_params()
99        # wavelength
100        lamb = self.wavelength
101   
102        if lamb == 0:
103            msg = "Can't compute the resolution: the wavelength is zero..." 
104            raise RuntimeError, msg
105        # wavelength spread
106        lamb_spread = self.wavelength_spread
107        # Find polar values
108        qr_value, phi = self._get_polar_value(qx_value, qy_value)
109        # vacuum wave transfer
110        knot = 2*pi/lamb
111        # scattering angle theta; always true for plane detector
112        # aligned vertically to the ko direction
113        if qr_value > knot:
114            theta = pi/2
115        else:
116            theta = math.asin(qr_value/knot)
117        # source aperture size
118        rone = self.source_aperture_size
119        # sample aperture size
120        rtwo = self.sample_aperture_size
121        # detector pixel size
122        rthree = self.detector_pix_size
123        # source to sample(aperture) distance
124        l_ssa = self.source2sample_distance[0]
125        # sample(aperture) to detector distance
126        l_sad = self.sample2detector_distance[0]
127        # sample (aperture) to sample distance
128        l_sas = self.sample2sample_distance[0]
129        # source to sample distance
130        l_one = l_ssa + l_sas
131        # sample to detector distance
132        l_two = l_sad - l_sas
133       
134        # Sample offset correction for l_one and Lp on variance calculation
135        l1_cor = (l_ssa * l_two) / (l_sas + l_two)
136        lp_cor = (l_ssa * l_two) / (l_one + l_two)
137        # the radial distance to the pixel from the center of the detector
138        radius = math.tan(theta)*l_two
139        #Lp = l_one*l_two/(l_one+l_two)
140        # default polar coordinate
141        comp1 = 'radial'
142        comp2 = 'phi'
143        # in the case of the cartesian coordinate
144        if coord == 'cartesian':
145            comp1 = 'x'
146            comp2 = 'y'
147
148        # sigma in the radial/x direction
149        # for source aperture
150        sigma_1  = self.get_variance(rone, l1_cor, phi, comp1)
151        # for sample apperture
152        sigma_1 += self.get_variance(rtwo, lp_cor, phi, comp1)
153        # for detector pix
154        sigma_1 += self.get_variance(rthree, l_two, phi, comp1)
155        # for gravity term
156        sigma_1 +=  self.get_variance_gravity(l_ssa, l_sad, lamb, lamb_spread, 
157                             phi, comp1, 'on') 
158        # for wavelength spread
159        # reserve for 1d calculation
160        sigma_wave_1 = self.get_variance_wave(radius, l_two, lamb_spread, 
161                                          phi, 'radial', 'on')
162        # for 1d
163        variance_1d_1 = sigma_1/2 +sigma_wave_1
164        # normalize
165        variance_1d_1 = knot*knot*variance_1d_1/12
166       
167        # for 2d
168        #sigma_1 += sigma_wave_1
169        # normalize
170        sigma_1 = knot*sqrt(sigma_1/12)
171        sigma_r = knot*sqrt(sigma_wave_1/12)
172        # sigma in the phi/y direction
173        # for source apperture
174        sigma_2  = self.get_variance(rone, l1_cor, phi, comp2)
175
176        # for sample apperture
177        sigma_2 += self.get_variance(rtwo, lp_cor, phi, comp2)
178
179        # for detector pix
180        sigma_2 += self.get_variance(rthree, l_two, phi, comp2)
181
182        # for gravity term
183        sigma_2 +=  self.get_variance_gravity(l_ssa, l_sad, lamb, lamb_spread, 
184                             phi, comp2, 'on')
185
186       
187        # for wavelength spread
188        # reserve for 1d calculation
189        sigma_wave_2 = self.get_variance_wave(radius, l_two, lamb_spread, 
190                                          phi, 'phi', 'on') 
191        # for 1d
192        variance_1d_2 = sigma_2/2 +sigma_wave_2
193        # normalize
194        variance_1d_2 = knot*knot*variance_1d_2/12
195       
196        # for 2d
197        #sigma_2 =  knot*sqrt(sigma_2/12)
198        #sigma_2 += sigma_wave_2
199        # normalize
200        sigma_2 =  knot*sqrt(sigma_2/12)
201
202        # set sigmas
203        self.sigma_1 = sigma_1
204        self.sigma_lamd = sigma_r
205        self.sigma_2 = sigma_2
206       
207        self.sigma_1d = sqrt(variance_1d_1 + variance_1d_2)
208        return qr_value, phi, sigma_1, sigma_2, sigma_r
209   
210    def get_image(self, qx_value, qy_value, sigma_1, sigma_2, sigma_r,
211                  qx_min, qx_max, qy_min, qy_max, coord = 'cartesian'): 
212        """
213        Get the resolution in polar coordinate ready to plot
214        : qx_value: qx_value value
215        : qy_value: qy_value value
216        : sigma_1: variance in r direction
217        : sigma_2: variance in phi direction
218        : coord: coordinate system of image, 'polar' or 'cartesian'
219        """
220        # Get  qx_max and qy_max...
221        output = self._get_detector_qxqy_pixels()
222        # Set qx_value/qy_value min/max
223        #qx_min = self.qx_min
224        #qx_max = self.qx_max
225        #qy_min = self.qy_min
226        #qy_max = self.qy_max
227        qr_value, phi = self._get_polar_value(qx_value, qy_value)
228
229        # Check whether the q value is within the detector range
230        msg = "Invalid input: Q value out of the detector range..."
231        if qx_min < self.qx_min:
232            self.qx_min = qx_min
233            #raise ValueError, msg
234        if qx_max > self.qx_max:
235            self.qx_max = qx_max
236            #raise ValueError, msg
237        if qy_min < self.qy_min:
238            self.qy_min = qy_min
239            #raise ValueError, msg
240        if qy_max > self.qy_max:
241            self.qy_max = qy_max
242            #raise ValueError, msg
243
244        # Make an empty graph in the detector scale
245        dx_size = (self.qx_max - self.qx_min) / (1000 - 1)
246        dy_size = (self.qy_max - self.qy_min) / (1000 - 1)
247        x_val = numpy.arange(self.qx_min, self.qx_max, dx_size)
248        y_val = numpy.arange(self.qy_max, self.qy_min, -dy_size)
249        q_1, q_2 = numpy.meshgrid(x_val, y_val)
250        #q_phi = numpy.arctan(q_1,q_2)
251        # check whether polar or cartesian
252        if coord == 'polar':
253            # Find polar values
254            qr_value, phi = self._get_polar_value(qx_value, qy_value)
255            q_1, q_2 = self._rotate_z(q_1, q_2, phi)
256            qc_1 = qr_value
257            qc_2 = 0.0
258            # Calculate the 2D Gaussian distribution image
259            image = self._gaussian2d_polar(q_1, q_2, qc_1, qc_2, 
260                                 sigma_1, sigma_2, sigma_r)
261        else:
262            # catesian coordinate
263            # qx_center
264            qc_1 = qx_value
265            # qy_center
266            qc_2 = qy_value
267           
268            # Calculate the 2D Gaussian distribution image
269            image = self._gaussian2d(q_1, q_2, qc_1, qc_2, 
270                                     sigma_1, sigma_2, sigma_r)
271        # Add it if there are more than one inputs.
272        if len(self.image) > 0:
273            self.image += image
274        else:
275            self.image = image
276
277        return self.image
278   
279    def plot_image(self, image):
280        """
281        Plot image using pyplot
282        : image: 2d resolution image
283       
284        : return plt: pylab object
285        """
286        import matplotlib.pyplot as plt
287
288        self.plot = plt
289        plt.xlabel('$\\rm{Q}_{x} [A^{-1}]$')
290        plt.ylabel('$\\rm{Q}_{y} [A^{-1}]$')
291        # Max value of the image
292        max = numpy.max(image)
293        # Image
294        im = plt.imshow(image, 
295                extent = [self.qx_min, self.qx_max, self.qy_min, self.qy_max])
296
297        # bilinear interpolation to make it smoother
298        im.set_interpolation('bilinear')
299
300        return plt
301   
302    def reset_image(self):
303        """
304        Reset image to default (=[])
305        """
306        self.image = []
307       
308    def get_variance(self, size = [], distance = 0, phi = 0, comp = 'radial'):
309        """
310        Get the variance when the slit/pinhole size is given
311        : size: list that can be one(diameter for circular)
312                or two components(lengths for rectangular)
313        : distance: [z, x] where z along the incident beam, x // qx_value
314        : comp: direction of the sigma; can be 'phi', 'y', 'x', and 'radial'
315       
316        : return variance: sigma^2
317        """   
318        # check the length of size (list)
319        len_size = len(size)
320       
321        # define sigma component direction
322        if comp == 'radial':
323            phi_x = math.cos(phi)
324            phi_y = math.sin(phi)
325        elif comp == 'phi':
326            phi_x = math.sin(phi)
327            phi_y = math.cos(phi)
328        elif comp == 'x':
329            phi_x = 1
330            phi_y = 0
331        elif comp == 'y':
332            phi_x = 0
333            phi_y = 1
334        else:
335            phi_x = 0
336            phi_y = 0
337        # calculate each component
338        # for pinhole w/ radius = size[0]/2
339        if len_size == 1:
340            x_comp = (0.5 * size[0]) * sqrt(3)
341            y_comp = 0
342        # for rectangular slit
343        elif len_size == 2:
344            x_comp = size[0] * phi_x
345            y_comp = size[1] * phi_y
346        # otherwise
347        else:
348            raise ValueError, " Improper input..."
349        # get them squared 
350        sigma  = x_comp * x_comp
351        sigma += y_comp * y_comp
352        # normalize by distance
353        sigma /= (distance * distance)
354
355        return sigma
356
357    def get_variance_wave(self, radius, distance, spread, phi, 
358                          comp = 'radial', switch = 'on'):
359        """
360        Get the variance when the wavelength spread is given
361       
362        : radius: the radial distance from the beam center to the pix of q
363        : distance: sample to detector distance
364        : spread: wavelength spread (ratio)
365        : comp: direction of the sigma; can be 'phi', 'y', 'x', and 'radial'
366       
367        : return variance: sigma^2
368        """
369        if switch.lower() == 'off':
370            return 0
371        # check the singular point
372        if distance == 0 or comp == 'phi':
373            return 0
374        else:
375            # calculate sigma^2
376            sigma = 2 * math.pow(radius/distance*spread, 2)
377            if comp == 'x':
378                sigma *= (math.cos(phi)*math.cos(phi))
379            elif comp == 'y':
380                sigma *= (math.sin(phi)*math.sin(phi))
381            else:
382                sigma *= 1         
383               
384            return sigma
385
386    def get_variance_gravity(self, s_distance, d_distance, wavelength, spread, 
387                             phi, comp = 'radial', switch = 'on'):
388        """
389        Get the variance from gravity when the wavelength spread is given
390       
391        : s_distance: source to sample distance
392        : d_distance: sample to detector distance
393        : wavelength: wavelength
394        : spread: wavelength spread (ratio)
395        : comp: direction of the sigma; can be 'phi', 'y', 'x', and 'radial'
396       
397        : return variance: sigma^2
398        """
399        if switch.lower() == 'off':
400            return 0
401        if self.mass == 0.0:
402            return 0
403        # check the singular point
404        if d_distance == 0 or comp == 'x':
405            return 0
406        else:
407            # neutron mass in cgs unit
408            self.mass = self.get_neutron_mass()
409            # plank constant in cgs unit
410            h_constant = _PLANK_H
411            # gravity in cgs unit
412            gravy = _GRAVITY
413            # m/h
414            m_over_h = self.mass /h_constant
415            # A value
416            a_value = d_distance * (s_distance + d_distance)
417            a_value *= math.pow(m_over_h / 2, 2)
418            a_value *= gravy
419            # unit correction (1/cm to 1/A) for A and d_distance below
420            a_value *= 1.0E-16
421           
422            # calculate sigma^2
423            sigma = math.pow(a_value / d_distance, 2)
424            sigma *= math.pow(wavelength, 4)
425            sigma *= math.pow(spread, 2)
426            sigma *= 8
427           
428            # only for the polar coordinate
429            #if comp == 'radial':
430            #    sigma *= (math.sin(phi) * math.sin(phi))
431            #elif comp == 'phi':
432            #    sigma *= (math.cos(phi) * math.cos(phi))
433           
434            return sigma
435       
436    def get_intensity(self):
437        """
438        Get intensity
439        """
440        return self.wave.intensity
441
442    def get_wavelength(self):
443        """
444        Get wavelength
445        """
446        return self.wave.wavelength
447   
448    def get_wavelength_spread(self):
449        """
450        Get wavelength spread
451        """
452        return self.wave.wavelength_spread
453   
454    def get_neutron_mass(self):
455        """
456        Get Neutron mass
457        """
458        return self.wave.mass
459   
460    def get_source_aperture_size(self):
461        """
462        Get source aperture size
463        """
464        return self.aperture.source_size
465   
466    def get_sample_aperture_size(self):
467        """
468        Get sample aperture size
469        """
470        return self.aperture.sample_size
471   
472    def get_detector_pix_size(self):
473        """
474        Get detector pixel size
475        """
476        return self.detector.pix_size
477   
478    def get_detector_size(self):
479        """
480        Get detector size
481        """
482        return self.detector.size   
483     
484    def get_source2sample_distance(self):
485        """
486        Get detector source2sample_distance
487        """
488        return self.aperture.sample_distance
489   
490    def get_sample2sample_distance(self):
491        """
492        Get detector sampleslitsample_distance
493        """
494        return self.sample.distance
495   
496    def get_sample2detector_distance(self):
497        """
498        Get detector sample2detector_distance
499        """
500        return self.detector.distance
501   
502    def set_intensity(self, intensity):
503        """
504        Set intensity
505        """
506        self.wave.set_intensity(intensity)
507   
508    def set_wavelength(self, wavelength):
509        """
510        Set wavelength
511        """
512        self.wave.set_wavelength(wavelength)
513   
514    def set_wavelength_spread(self, wavelength_spread):
515        """
516        Set wavelength spread
517        """
518        self.wave.set_wavelength_spread(wavelength_spread)
519   
520    def set_source_aperture_size(self, size):
521        """
522        Set source aperture size
523       
524        : param size: [dia_value] or [x_value, y_value]
525        """       
526        if len(size) < 1 or len(size) > 2:
527            raise RuntimeError, "The length of the size must be one or two."
528        self.aperture.set_source_size(size)
529       
530    def set_neutron_mass(self, mass):
531        """
532        Set Neutron mass
533        """
534        self.wave.set_mass(mass)
535        self.mass = mass
536       
537    def set_sample_aperture_size(self, size):
538        """
539        Set sample aperture size
540       
541        : param size: [dia_value] or [xheight_value, yheight_value]
542        """
543        if len(size) < 1 or len(size) > 2:
544            raise RuntimeError, "The length of the size must be one or two."
545        self.aperture.set_sample_size(size)
546   
547    def set_detector_pix_size(self, size):
548        """
549        Set detector pixel size
550        """
551        self.detector.set_pix_size(size)
552       
553    def set_detector_size(self, size):
554        """
555        Set detector size in number of pixels
556        : param size: [pixel_nums] or [x_pix_num, yx_pix_num]
557        """
558        self.detector.set_size(size)
559       
560    def set_source2sample_distance(self, distance):
561        """
562        Set detector source2sample_distance
563       
564        : param distance: [distance, x_offset]
565        """
566        if len(distance) < 1 or len(distance) > 2:
567            raise RuntimeError, "The length of the size must be one or two."
568        self.aperture.set_sample_distance(distance)
569
570    def set_sample2sample_distance(self, distance):
571        """
572        Set detector sample_slit2sample_distance
573       
574        : param distance: [distance, x_offset]
575        """
576        if len(distance) < 1 or len(distance) > 2:
577            raise RuntimeError, "The length of the size must be one or two."
578        self.sample.set_distance(distance)
579   
580    def set_sample2detector_distance(self, distance):
581        """
582        Set detector sample2detector_distance
583       
584        : param distance: [distance, x_offset]
585        """
586        if len(distance) < 1 or len(distance) > 2:
587            raise RuntimeError, "The length of the size must be one or two."
588        self.detector.set_distance(distance)
589       
590    def get_all_instrument_params(self):
591        """
592        Get all instrumental parameters
593        """
594        self.intensity = self.get_intensity()
595        self.wavelength = self.get_wavelength()
596        self.wavelength_spread = self.get_wavelength_spread()
597        self.mass = self.get_neutron_mass()
598        self.source_aperture_size = self.get_source_aperture_size()
599        self.sample_aperture_size = self.get_sample_aperture_size()
600        self.detector_pix_size = self.get_detector_pix_size()
601        self.detector_size = self.get_detector_size()
602        self.source2sample_distance = self.get_source2sample_distance()
603        self.sample2sample_distance = self.get_sample2sample_distance()
604        self.sample2detector_distance = self.get_sample2detector_distance()
605
606
607         
608    def _rotate_z(self, x_value, y_value, theta= 0.0):
609        """
610        Rotate x-y cordinate around z-axis by theta
611        : x_value: numpy array of x values
612        : y_value: numpy array of y values
613        : theta: angle to rotate by in rad
614       
615        :return: x_prime, y-prime
616        """       
617        # rotate by theta
618        x_prime = x_value * math.cos(theta) + y_value * math.sin(theta)
619        y_prime =  -x_value * math.sin(theta) + y_value * math.cos(theta)
620   
621        return x_prime, y_prime
622   
623    def _gaussian2d(self, x_val, y_val, x0_val, y0_val, 
624                    sigma_x, sigma_y, sigma_r):
625        """
626        Calculate 2D Gaussian distribution
627        : x_val: x value
628        : y_val: y value
629        : x0_val: mean value in x-axis
630        : y0_val: mean value in y-axis
631        : sigma_x: variance in x-direction
632        : sigma_y: variance in y-direction
633       
634        : return: gaussian (value)
635        """
636        # phi values at each points (not at the center)
637        x_value = x_val - x0_val
638        y_value = y_val - y0_val
639        phi_i = numpy.arctan2(y_val, x_val)
640
641        sin_phi = numpy.sin(phi_i)
642        cos_phi = numpy.cos(phi_i)
643       
644        x_p = x_value * cos_phi + y_value * sin_phi
645        y_p = -x_value * sin_phi + y_value * cos_phi
646       
647        new_x = x_p * cos_phi / (sigma_r / sigma_x + 1) - y_p * sin_phi
648        new_x /= sigma_x
649        new_y = x_p * sin_phi / (sigma_r / sigma_y + 1) + y_p * cos_phi
650        new_y /= sigma_y
651
652        nu_value = -0.5 *(new_x * new_x + new_y * new_y)
653
654        gaussian = numpy.exp(nu_value)
655        # normalizing factor correction
656        gaussian /= gaussian.sum()
657
658        return gaussian
659
660    def _gaussian2d_polar(self, x_val, y_val, x0_val, y0_val, 
661                        sigma_x, sigma_y, sigma_r):
662        """
663        Calculate 2D Gaussian distribution for polar coodinate
664        : x_val: x value
665        : y_val: y value
666        : x0_val: mean value in x-axis
667        : y0_val: mean value in y-axis
668        : sigma_x: variance in r-direction
669        : sigma_y: variance in phi-direction
670        : sigma_r: wavelength variance in r-direction
671       
672        : return: gaussian (value)
673        """
674        sigma_x = sqrt(sigma_x * sigma_x + sigma_r * sigma_r)
675        # call gaussian1d
676        gaussian  = self._gaussian1d(x_val, x0_val, sigma_x)
677        gaussian *= self._gaussian1d(y_val, y0_val, sigma_y)
678 
679        # normalizing factor correction
680        if sigma_x != 0 and sigma_y != 0:
681            gaussian *= sqrt(2 * pi)
682        return gaussian
683   
684    def _gaussian1d(self, value, mean, sigma):
685        """
686        Calculate 1D Gaussian distribution
687        : value: value
688        : mean: mean value
689        : sigma: variance
690       
691        : return: gaussian (value)
692        """
693        # default
694        gaussian = 1.0
695        if sigma != 0:
696            # get exponent
697            nu_value = (value - mean) / sigma
698            nu_value *= nu_value
699            nu_value *= -0.5
700            gaussian *= numpy.exp(nu_value)
701            gaussian /= sigma
702            # normalize
703            gaussian /= sqrt(2 * pi)
704           
705        return gaussian
706   
707    def _atan_phi(self, qy_value, qx_value):
708        """
709        Find the angle phi of q on the detector plane for qx_value, qy_value given
710        : qx_value: x component of q
711        : qy_value: y component of q
712       
713        : return phi: the azimuthal angle of q on x-y plane
714        """
715        phi = math.atan2(qy_value, qx_value)
716        return phi
717        # default
718        phi = 0
719        # ToDo: This is misterious - sign???
720        #qy_value = -qy_value
721        # Take care of the singular point
722        if qx_value == 0:
723            if qy_value > 0:
724                phi = pi / 2
725            elif qy_value < 0:
726                phi = -pi / 2
727            else:
728                phi = 0
729        else:
730            # the angle
731            phi = math.atan2(qy_value, qx_value)
732
733        return phi
734
735    def _get_detector_qxqy_pixels(self):
736        """
737        Get the pixel positions of the detector in the qx_value-qy_value space
738        """
739       
740        # update all param values
741        self.get_all_instrument_params()
742       
743        # wavelength
744        wavelength = self.wavelength
745        # Gavity correction
746        delta_y = self._get_beamcenter_drop() # in cm
747       
748        # detector_pix size
749        detector_pix_size = self.detector_pix_size
750        # Square or circular pixel
751        if len(detector_pix_size) == 1:
752            pix_x_size = detector_pix_size[0]
753            pix_y_size = detector_pix_size[0]
754        # rectangular pixel pixel
755        elif len(detector_pix_size) == 2:
756            pix_x_size = detector_pix_size[0]
757            pix_y_size = detector_pix_size[1]
758        else:
759            raise ValueError, " Input value format error..."
760        # Sample to detector distance = sample slit to detector
761        # minus sample offset
762        sample2detector_distance = self.sample2detector_distance[0] - \
763                                    self.sample2sample_distance[0]
764        # detector offset in x-direction
765        detector_offset = 0
766        try:
767            detector_offset = self.sample2detector_distance[1]
768        except:
769            pass
770       
771        # detector size in [no of pix_x,no of pix_y]
772        detector_pix_nums_x = self.detector_size[0]
773       
774        # get pix_y if it exists, otherwse take it from [0]
775        try:
776            detector_pix_nums_y = self.detector_size[1]
777        except:
778            detector_pix_nums_y = self.detector_size[0]
779       
780        # detector offset in pix number
781        offset_x = detector_offset / pix_x_size
782        offset_y = delta_y / pix_y_size
783       
784        # beam center position in pix number (start from 0)
785        center_x, center_y = self._get_beamcenter_position(detector_pix_nums_x,
786                                    detector_pix_nums_y, offset_x, offset_y)
787        # distance [cm] from the beam center on detector plane
788        detector_ind_x = numpy.arange(detector_pix_nums_x)
789        detector_ind_y = numpy.arange(detector_pix_nums_y)
790
791        # shif 0.5 pixel so that pix position is at the center of the pixel
792        detector_ind_x = detector_ind_x + 0.5
793        detector_ind_y = detector_ind_y + 0.5
794
795        # the relative postion from the beam center
796        detector_ind_x = detector_ind_x - center_x
797        detector_ind_y = detector_ind_y - center_y
798       
799        # unit correction in cm
800        detector_ind_x = detector_ind_x * pix_x_size
801        detector_ind_y = detector_ind_y * pix_y_size
802       
803        qx_value = numpy.zeros(len(detector_ind_x))
804        qy_value = numpy.zeros(len(detector_ind_y))
805        i = 0
806
807        for indx in detector_ind_x:
808            qx_value[i] = self._get_qx(indx, sample2detector_distance, wavelength)
809            i += 1
810        i = 0
811        for indy in detector_ind_y:
812            qy_value[i] = self._get_qx(indy, sample2detector_distance, wavelength)
813            i += 1
814           
815        # qx_value and qy_value values in array
816        qx_value = qx_value.repeat(detector_pix_nums_y) 
817        qx_value = qx_value.reshape(detector_pix_nums_x, detector_pix_nums_y)
818        qy_value = qy_value.repeat(detector_pix_nums_x) 
819        qy_value = qy_value.reshape(detector_pix_nums_y, detector_pix_nums_x)
820        qy_value = qy_value.transpose()
821
822        # p min and max values among the center of pixels
823        self.qx_min = numpy.min(qx_value)
824        self.qx_max = numpy.max(qx_value)
825        self.qy_min = numpy.min(qy_value)
826        self.qy_max = numpy.max(qy_value)
827               
828        # Appr. min and max values of the detector display limits
829        # i.e., edges of the last pixels.
830        self.qy_min += self._get_qx(-0.5 * pix_y_size, 
831                                sample2detector_distance, wavelength)
832        self.qy_max += self._get_qx(0.5 * pix_y_size, 
833                                sample2detector_distance, wavelength)
834        #if self.qx_min == self.qx_max:
835        self.qx_min += self._get_qx(-0.5 * pix_x_size, 
836                                sample2detector_distance, wavelength)
837        self.qx_max += self._get_qx(0.5 * pix_x_size, 
838                                    sample2detector_distance, wavelength)
839        # min and max values of detecter
840        self.detector_qx_min = self.qx_min
841        self.detector_qx_max = self.qx_max
842        self.detector_qy_min = self.qy_min
843        self.detector_qy_max = self.qy_max
844
845        # try to set it as a Data2D otherwise pass (not required for now)
846        try:
847            from DataLoader.data_info import Data2D
848            output = Data2D()
849            inten = numpy.zeros_like(qx_value)
850            output.data     = inten
851            output.qx_data  = qx_value
852            output.qy_data  = qy_value           
853        except:
854            pass
855       
856        return output#qx_value,qy_value
857       
858    def _get_qx(self, dx_size, det_dist, wavelength):
859        """
860        :param dx_size: x-distance from beam center [cm]
861        :param det_dist: sample to detector distance [cm]
862       
863        :return: q-value at the given position
864        """
865        # Distance from beam center in the plane of detector
866        plane_dist = dx_size
867        # full scattering angle on the x-axis
868        theta  = math.atan(plane_dist / det_dist)
869        qx_value     = (2.0 * pi / wavelength) * math.sin(theta)
870        return qx_value 
871   
872    def _get_polar_value(self, qx_value, qy_value):
873        """
874        Find qr_value and phi from qx_value and qy_value values
875       
876        : return qr_value, phi
877        """
878        # find |q| on detector plane
879        qr_value = sqrt(qx_value*qx_value + qy_value*qy_value)
880        # find angle phi
881        phi = self._atan_phi(qy_value, qx_value)
882       
883        return qr_value, phi
884   
885    def _get_beamcenter_position(self, num_x, num_y, offset_x, offset_y):
886        """
887        :param num_x: number of pixel in x-direction
888        :param num_y: number of pixel in y-direction
889        :param offset: detector offset in x-direction in pix number
890       
891        :return: pix number; pos_x, pos_y in pix index
892        """
893        # beam center position
894        pos_x = num_x / 2
895        pos_y = num_y / 2
896
897        # correction for offset
898        pos_x += offset_x
899        # correction for gravity that is always negative
900        pos_y -= offset_y
901
902        return pos_x, pos_y     
903
904    def _get_beamcenter_drop(self):
905        """
906        Get the beam center drop (delta y) in y diection due to gravity
907       
908        :return delta y: the beam center drop in cm
909        """
910        # Check if mass == 0 (X-ray).
911        if self.mass == 0:
912            return 0
913        # Covert unit from A to cm
914        unit_cm = 1e-08
915        # Velocity of neutron in horizontal direction (~ actual velocity)
916        velocity = _PLANK_H / (self.mass * self.wavelength * unit_cm)
917        # Compute delta y
918        delta_y = 0.5
919        delta_y *= _GRAVITY
920        delta_y *= self.sample2detector_distance[0] 
921        delta_y *= (self.source2sample_distance[0] + self.sample2detector_distance[0])
922        delta_y /= (velocity * velocity)
923
924        return delta_y     
925
926   
Note: See TracBrowser for help on using the repository browser.