source: sasview/sanscalculator/src/sans/calculator/resolution_calculator.py @ 440f020

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 440f020 was bcfc969, checked in by Jae Cho <jhjcho@…>, 13 years ago

more accurate way to cal averaging sigmas

  • Property mode set to 100644
File size: 39.7 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 TOF as 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        self.image_lam = []
39        # resolutions
40        # lamda in r-direction
41        self.sigma_lamda = 0
42        # x-dir (no lamda)
43        self.sigma_1 = 0
44        #y-dir (no lamda)
45        self.sigma_2 = 0
46        # 1D total
47        self.sigma_1d = 0
48        # q min and max
49        self.qx_min = -0.3
50        self.qx_max = 0.3
51        self.qy_min = -0.3
52        self.qy_max = 0.3
53        # q min and max of the detector
54        self.detector_qx_min = -0.3
55        self.detector_qx_max = 0.3
56        self.detector_qy_min = -0.3
57        self.detector_qy_max = 0.3
58        # possible max qrange
59        self.qxmin_limit = 0
60        self.qxmax_limit = 0
61        self.qymin_limit = 0
62        self.qymax_limit = 0
63        # set sigmas
64        self.sigma_1 = 0
65        self.sigma_lamd = 0
66        self.sigma_2 = 0
67        self.sigma_1d = 0
68        # plots
69        self.plot = None
70        # instrumental params defaults
71        self.mass = 0
72        self.intensity = 0
73        self.wavelength = 0
74        self.wavelength_spread = 0
75        self.source_aperture_size = []
76        self.source2sample_distance = []
77        self.sample2sample_distance = []
78        self.sample_aperture_size = []
79        self.sample2detector_distance = []
80        self.detector_pix_size = []
81        self.detector_size = []
82        # get all the values of the instrumental parameters
83        #self.intensity = self.get_intensity()
84        #self.wavelength = self.get_wavelength()
85        #self.wavelength_spread = self.get_wavelength_spread()
86        self.get_all_instrument_params()
87        # max q range for all lambdas
88        self.qxrange = []
89        self.qyrange = []
90       
91    def compute_and_plot(self, qx_value, qy_value, qx_min, qx_max, 
92                          qy_min, qy_max, coord = 'cartesian'):
93        """
94        Compute the resolution
95        : qx_value: x component of q
96        : qy_value: y component of q
97        """
98        # make sure to update all the variables need.
99        # except lambda, dlambda, and intensity
100        self.get_all_instrument_params()
101        # wavelength etc.
102        lamda_list, dlamb_list = self.get_wave_list()
103        intens_list = []#self.get_intensity_list()
104
105        sig1_list = []
106        sig2_list = []
107        sigr_list = []
108        sigma1d_list = []
109        num_lamda = len(lamda_list)
110        for num in range(num_lamda):
111            lam = lamda_list[num]
112            # wavelength spread
113            dlam = dlamb_list[num]
114            intens = self.setup_tof(lam, dlam)
115            intens_list.append(intens)
116            # save q min max
117            #qx_min = 0
118            #qx_max = 0
119            #qy_min = 0
120            #qy_max = 0
121            # compute 2d resolution
122            _, _, sigma_1, sigma_2, sigma_r, sigma1d = \
123                            self.compute(lam, dlam, qx_value, qy_value, coord)
124            # make image
125            image = self.get_image(qx_value, qy_value, sigma_1, sigma_2, 
126                            sigma_r, qx_min, qx_max, qy_min, qy_max,
127                            coord, False)
128           
129            # Non tof mode to be speed up
130            #if num_lamda < 2:
131            #    return self.plot_image(image)
132           
133            if qx_min > self.qx_min:
134                qx_min = self.qx_min
135            if qx_max < self.qx_max:
136                qx_max = self.qx_max
137            if qy_min > self.qy_min:
138                qy_min = self.qy_min
139            if qy_max < self.qy_max:
140                qy_max = self.qy_max
141               
142            # set max qranges
143            self.qxrange = [qx_min, qx_max]
144            self.qyrange = [qy_min, qy_max]
145           
146            sig1_list.append(sigma_1)
147            sig2_list.append(sigma_2)
148            sigr_list.append(sigma_r)
149            sigma1d_list.append(sigma1d)
150        # redraw image in global 2d q-space.   
151        self.image_lam = []
152        total_intensity = 0
153        sigma_1 = 0
154        sigma_r = 0
155        sigma_2 = 0
156        sigma1d = 0
157        for ind in range(num_lamda):
158            lam = lamda_list[ind]
159            dlam = dlamb_list[ind]
160            intens = self.setup_tof(lam, dlam)
161            out = self.get_image(qx_value, qy_value, sig1_list[ind], 
162                                   sig2_list[ind], sigr_list[ind], 
163                                   qx_min, qx_max, qy_min, qy_max, coord)
164            # this is the case of q being outside the detector
165            #if numpy.all(out==0.0):
166            #    continue
167            image = out
168            # set variance as sigmas
169            sigma_1 += sig1_list[ind] *  sig1_list[ind] * self.intensity
170            sigma_r += sigr_list[ind] * sigr_list[ind] * self.intensity
171            sigma_2 += sig2_list[ind] * sig2_list[ind] * self.intensity
172            sigma1d += sigma1d_list[ind] * sigma1d_list[ind] * self.intensity
173            total_intensity += self.intensity
174       
175        if total_intensity != 0:
176            # average variance
177            image_out = image / total_intensity
178            sigma_1 = sigma_1 / total_intensity
179            sigma_r = sigma_r / total_intensity
180            sigma_2 = sigma_2 / total_intensity
181            sigma1d = sigma1d / total_intensity
182            # set sigmas
183            self.sigma_1 = sqrt(sigma_1)
184            self.sigma_lamd = sqrt(sigma_r)
185            self.sigma_2 = sqrt(sigma_2)
186            self.sigma_1d = sqrt(sigma1d)
187            # rescale
188            max_im_val = 1 #image_out.max()
189            if max_im_val > 0:
190                image_out /= max_im_val
191        else:
192            image_out = image * 0.0
193            # Don't calculate sigmas nor set self.sigmas!
194            sigma_1 = 0
195            sigma_r = 0
196            sigma_2 = 0
197            sigma1d = 0
198        if len(self.image) > 0:
199            self.image += image_out
200        else:
201            self.image = image_out
202       
203        # plot image
204        return self.plot_image(self.image) 
205   
206    def setup_tof(self, wavelength, wavelength_spread):
207        """
208        Setup all parameters in instrument
209       
210        : param ind: index of lambda, etc
211        """
212
213        # set wave.wavelength
214        self.set_wavelength(wavelength)
215        self.set_wavelength_spread(wavelength_spread)
216        self.intensity = self.wave.get_intensity()
217       
218        if wavelength == 0:
219            msg = "Can't compute the resolution: the wavelength is zero..." 
220            raise RuntimeError, msg
221        return self.intensity
222       
223    def compute(self, wavelength, wavelength_spread, qx_value, qy_value, 
224                coord = 'cartesian'):
225        """
226        Compute the Q resoltuion in || and + direction of 2D
227        : qx_value: x component of q
228        : qy_value: y component of q
229        """
230        coord = 'cartesian'
231        lamb = wavelength
232        lamb_spread = wavelength_spread
233       
234        # Find polar values
235        qr_value, phi = self._get_polar_value(qx_value, qy_value)
236        # vacuum wave transfer
237        knot = 2*pi/lamb
238        # scattering angle theta; always true for plane detector
239        # aligned vertically to the ko direction
240        if qr_value > knot:
241            theta = pi/2
242        else:
243            theta = math.asin(qr_value/knot)
244        # source aperture size
245        rone = self.source_aperture_size
246        # sample aperture size
247        rtwo = self.sample_aperture_size
248        # detector pixel size
249        rthree = self.detector_pix_size
250        # source to sample(aperture) distance
251        l_ssa = self.source2sample_distance[0]
252        # sample(aperture) to detector distance
253        l_sad = self.sample2detector_distance[0]
254        # sample (aperture) to sample distance
255        l_sas = self.sample2sample_distance[0]
256        # source to sample distance
257        l_one = l_ssa + l_sas
258        # sample to detector distance
259        l_two = l_sad - l_sas
260       
261        # Sample offset correction for l_one and Lp on variance calculation
262        l1_cor = (l_ssa * l_two) / (l_sas + l_two)
263        lp_cor = (l_ssa * l_two) / (l_one + l_two)
264        # the radial distance to the pixel from the center of the detector
265        radius = math.tan(theta) * l_two
266        #Lp = l_one*l_two/(l_one+l_two)
267        # default polar coordinate
268        comp1 = 'radial'
269        comp2 = 'phi'
270        # in the case of the cartesian coordinate
271        if coord == 'cartesian':
272            comp1 = 'x'
273            comp2 = 'y'
274
275        # sigma in the radial/x direction
276        # for source aperture
277        sigma_1  = self.get_variance(rone, l1_cor, phi, comp1)
278        # for sample apperture
279        sigma_1 += self.get_variance(rtwo, lp_cor, phi, comp1)
280        # for detector pix
281        sigma_1 += self.get_variance(rthree, l_two, phi, comp1)
282        # for gravity term
283        sigma_1 +=  self.get_variance_gravity(l_ssa, l_sad, lamb, lamb_spread, 
284                             phi, comp1, 'on') 
285        # for wavelength spread
286        # reserve for 1d calculation
287        sigma_wave_1 = self.get_variance_wave(radius, l_two, lamb_spread, 
288                                          phi, 'radial', 'on')
289        # for 1d
290        variance_1d_1 = sigma_1/2 + sigma_wave_1
291        # normalize
292        variance_1d_1 = knot * knot * variance_1d_1 / 12
293       
294        # for 2d
295        #sigma_1 += sigma_wave_1
296        # normalize
297        sigma_1 = knot*sqrt(sigma_1 / 12)
298        sigma_r = knot*sqrt(sigma_wave_1 / 12)
299        # sigma in the phi/y direction
300        # for source apperture
301        sigma_2  = self.get_variance(rone, l1_cor, phi, comp2)
302
303        # for sample apperture
304        sigma_2 += self.get_variance(rtwo, lp_cor, phi, comp2)
305
306        # for detector pix
307        sigma_2 += self.get_variance(rthree, l_two, phi, comp2)
308
309        # for gravity term
310        sigma_2 +=  self.get_variance_gravity(l_ssa, l_sad, lamb, lamb_spread, 
311                             phi, comp2, 'on')
312
313       
314        # for wavelength spread
315        # reserve for 1d calculation
316        sigma_wave_2 = self.get_variance_wave(radius, l_two, lamb_spread, 
317                                          phi, 'phi', 'on') 
318        # for 1d
319        variance_1d_2 = sigma_2/2 +sigma_wave_2
320        # normalize
321        variance_1d_2 = knot*knot*variance_1d_2 / 12
322       
323        # for 2d
324        #sigma_2 =  knot*sqrt(sigma_2/12)
325        #sigma_2 += sigma_wave_2
326        # normalize
327        sigma_2 =  knot * sqrt(sigma_2 / 12)
328        sigma1d = sqrt(variance_1d_1 + variance_1d_2)
329        # set sigmas
330        self.sigma_1 = sigma_1
331        self.sigma_lamd = sigma_r
332        self.sigma_2 = sigma_2
333        self.sigma_1d = sigma1d
334        return qr_value, phi, sigma_1, sigma_2, sigma_r, sigma1d
335   
336    def _within_detector_range(self,qx_value, qy_value):
337        """
338        check if qvalues are within detector range
339        """
340        # detector range
341        detector_qx_min = self.detector_qx_min
342        detector_qx_max = self.detector_qx_max
343        detector_qy_min = self.detector_qy_min
344        detector_qy_max = self.detector_qy_max
345        if self.qxmin_limit > detector_qx_min:
346            self.qxmin_limit = detector_qx_min
347        if self.qxmax_limit < detector_qx_max:
348            self.qxmax_limit = detector_qx_max
349        if self.qymin_limit > detector_qy_min:
350            self.qymin_limit = detector_qy_min
351        if self.qymax_limit < detector_qy_max:
352            self.qymax_limit = detector_qy_max
353        if qx_value < detector_qx_min or qx_value > detector_qx_max:
354            return False
355        if qy_value < detector_qy_min or qy_value > detector_qy_max:
356            return False
357        return True
358   
359    def get_image(self, qx_value, qy_value, sigma_1, sigma_2, sigma_r,
360                  qx_min, qx_max, qy_min, qy_max, 
361                  coord = 'cartesian', full_cal=True): 
362        """
363        Get the resolution in polar coordinate ready to plot
364        : qx_value: qx_value value
365        : qy_value: qy_value value
366        : sigma_1: variance in r direction
367        : sigma_2: variance in phi direction
368        : coord: coordinate system of image, 'polar' or 'cartesian'
369        """
370        # Get  qx_max and qy_max...
371        output = self._get_detector_qxqy_pixels()
372       
373        qr_value, phi = self._get_polar_value(qx_value, qy_value)
374
375        # Check whether the q value is within the detector range
376        msg = "Invalid input: Q value out of the detector range..."
377        if qx_min < self.qx_min:
378            self.qx_min = qx_min
379            #raise ValueError, msg
380        if qx_max > self.qx_max:
381            self.qx_max = qx_max
382            #raise ValueError, msg
383        if qy_min < self.qy_min:
384            self.qy_min = qy_min
385            #raise ValueError, msg
386        if qy_max > self.qy_max:
387            self.qy_max = qy_max
388            #raise ValueError, msg
389        if not full_cal:
390            return None
391 
392        # Make an empty graph in the detector scale
393        dx_size = (self.qx_max - self.qx_min) / (1000 - 1)
394        dy_size = (self.qy_max - self.qy_min) / (1000 - 1)
395        x_val = numpy.arange(self.qx_min, self.qx_max, dx_size)
396        y_val = numpy.arange(self.qy_max, self.qy_min, -dy_size)
397        q_1, q_2 = numpy.meshgrid(x_val, y_val)
398        #q_phi = numpy.arctan(q_1,q_2)
399        # check whether polar or cartesian
400        if coord == 'polar':
401            # Find polar values
402            qr_value, phi = self._get_polar_value(qx_value, qy_value)
403            q_1, q_2 = self._rotate_z(q_1, q_2, phi)
404            qc_1 = qr_value
405            qc_2 = 0.0
406            # Calculate the 2D Gaussian distribution image
407            image = self._gaussian2d_polar(q_1, q_2, qc_1, qc_2, 
408                                 sigma_1, sigma_2, sigma_r)
409        else:
410            # catesian coordinate
411            # qx_center
412            qc_1 = qx_value
413            # qy_center
414            qc_2 = qy_value
415           
416            # Calculate the 2D Gaussian distribution image
417            image = self._gaussian2d(q_1, q_2, qc_1, qc_2, 
418                                     sigma_1, sigma_2, sigma_r)
419        # out side of detector
420        if not self._within_detector_range(qx_value, qy_value):
421            image *= 0.0
422            self.intensity = 0.0
423            #return self.image
424
425        # Add it if there are more than one inputs.
426        if len(self.image_lam) > 0:
427            self.image_lam += image * self.intensity
428        else:
429            self.image_lam = image * self.intensity
430       
431        return self.image_lam
432   
433    def plot_image(self, image):
434        """
435        Plot image using pyplot
436        : image: 2d resolution image
437       
438        : return plt: pylab object
439        """
440        import matplotlib.pyplot as plt
441
442        self.plot = plt
443        plt.xlabel('$\\rm{Q}_{x} [A^{-1}]$')
444        plt.ylabel('$\\rm{Q}_{y} [A^{-1}]$')
445        # Max value of the image
446        max = numpy.max(image)
447        qx_min, qx_max, qy_min, qy_max = self.get_detector_qrange()
448
449        # Image
450        im = plt.imshow(image, 
451                extent = [qx_min, qx_max, qy_min, qy_max])
452
453        # bilinear interpolation to make it smoother
454        im.set_interpolation('bilinear')
455
456        return plt
457   
458    def reset_image(self):
459        """
460        Reset image to default (=[])
461        """
462        self.image = []
463       
464    def get_variance(self, size = [], distance = 0, phi = 0, comp = 'radial'):
465        """
466        Get the variance when the slit/pinhole size is given
467        : size: list that can be one(diameter for circular)
468                or two components(lengths for rectangular)
469        : distance: [z, x] where z along the incident beam, x // qx_value
470        : comp: direction of the sigma; can be 'phi', 'y', 'x', and 'radial'
471       
472        : return variance: sigma^2
473        """   
474        # check the length of size (list)
475        len_size = len(size)
476       
477        # define sigma component direction
478        if comp == 'radial':
479            phi_x = math.cos(phi)
480            phi_y = math.sin(phi)
481        elif comp == 'phi':
482            phi_x = math.sin(phi)
483            phi_y = math.cos(phi)
484        elif comp == 'x':
485            phi_x = 1
486            phi_y = 0
487        elif comp == 'y':
488            phi_x = 0
489            phi_y = 1
490        else:
491            phi_x = 0
492            phi_y = 0
493        # calculate each component
494        # for pinhole w/ radius = size[0]/2
495        if len_size == 1:
496            x_comp = (0.5 * size[0]) * sqrt(3)
497            y_comp = 0
498        # for rectangular slit
499        elif len_size == 2:
500            x_comp = size[0] * phi_x
501            y_comp = size[1] * phi_y
502        # otherwise
503        else:
504            raise ValueError, " Improper input..."
505        # get them squared 
506        sigma  = x_comp * x_comp
507        sigma += y_comp * y_comp
508        # normalize by distance
509        sigma /= (distance * distance)
510
511        return sigma
512
513    def get_variance_wave(self, radius, distance, spread, phi, 
514                          comp = 'radial', switch = 'on'):
515        """
516        Get the variance when the wavelength spread is given
517       
518        : radius: the radial distance from the beam center to the pix of q
519        : distance: sample to detector distance
520        : spread: wavelength spread (ratio)
521        : comp: direction of the sigma; can be 'phi', 'y', 'x', and 'radial'
522       
523        : return variance: sigma^2
524        """
525        if switch.lower() == 'off':
526            return 0
527        # check the singular point
528        if distance == 0 or comp == 'phi':
529            return 0
530        else:
531            # calculate sigma^2
532            sigma = 2 * math.pow(radius/distance*spread, 2)
533            if comp == 'x':
534                sigma *= (math.cos(phi)*math.cos(phi))
535            elif comp == 'y':
536                sigma *= (math.sin(phi)*math.sin(phi))
537            else:
538                sigma *= 1         
539               
540            return sigma
541
542    def get_variance_gravity(self, s_distance, d_distance, wavelength, spread, 
543                             phi, comp = 'radial', switch = 'on'):
544        """
545        Get the variance from gravity when the wavelength spread is given
546       
547        : s_distance: source to sample distance
548        : d_distance: sample to detector distance
549        : wavelength: wavelength
550        : spread: wavelength spread (ratio)
551        : comp: direction of the sigma; can be 'phi', 'y', 'x', and 'radial'
552       
553        : return variance: sigma^2
554        """
555        if switch.lower() == 'off':
556            return 0
557        if self.mass == 0.0:
558            return 0
559        # check the singular point
560        if d_distance == 0 or comp == 'x':
561            return 0
562        else:
563            # neutron mass in cgs unit
564            self.mass = self.get_neutron_mass()
565            # plank constant in cgs unit
566            h_constant = _PLANK_H
567            # gravity in cgs unit
568            gravy = _GRAVITY
569            # m/h
570            m_over_h = self.mass /h_constant
571            # A value
572            a_value = d_distance * (s_distance + d_distance)
573            a_value *= math.pow(m_over_h / 2, 2)
574            a_value *= gravy
575            # unit correction (1/cm to 1/A) for A and d_distance below
576            a_value *= 1.0E-16
577           
578            # calculate sigma^2
579            sigma = math.pow(a_value / d_distance, 2)
580            sigma *= math.pow(wavelength, 4)
581            sigma *= math.pow(spread, 2)
582            sigma *= 8
583           
584            # only for the polar coordinate
585            #if comp == 'radial':
586            #    sigma *= (math.sin(phi) * math.sin(phi))
587            #elif comp == 'phi':
588            #    sigma *= (math.cos(phi) * math.cos(phi))
589           
590            return sigma
591       
592    def get_intensity(self):
593        """
594        Get intensity
595        """
596        return self.wave.intensity
597
598    def get_wavelength(self):
599        """
600        Get wavelength
601        """
602        return self.wave.wavelength
603   
604    def get_spectrum(self):
605        """
606        Get spectrum
607        """
608        return self.wave.spectrum   
609   
610    def get_default_spectrum(self):
611        """
612        Get default_spectrum
613        """
614        return self.wave.get_default_spectrum()   
615   
616    def get_spectrum(self):
617        """
618        Get _spectrum
619        """
620        return self.wave.get_spectrum() 
621         
622    def get_wavelength_spread(self):
623        """
624        Get wavelength spread
625        """
626        return self.wave.wavelength_spread
627   
628    def get_neutron_mass(self):
629        """
630        Get Neutron mass
631        """
632        return self.wave.mass
633   
634    def get_source_aperture_size(self):
635        """
636        Get source aperture size
637        """
638        return self.aperture.source_size
639   
640    def get_sample_aperture_size(self):
641        """
642        Get sample aperture size
643        """
644        return self.aperture.sample_size
645   
646    def get_detector_pix_size(self):
647        """
648        Get detector pixel size
649        """
650        return self.detector.pix_size
651       
652    def get_detector_size(self):
653        """
654        Get detector size
655        """
656        return self.detector.size   
657     
658    def get_source2sample_distance(self):
659        """
660        Get detector source2sample_distance
661        """
662        return self.aperture.sample_distance
663   
664    def get_sample2sample_distance(self):
665        """
666        Get detector sampleslitsample_distance
667        """
668        return self.sample.distance
669   
670    def get_sample2detector_distance(self):
671        """
672        Get detector sample2detector_distance
673        """
674        return self.detector.distance
675   
676    def set_intensity(self, intensity):
677        """
678        Set intensity
679        """
680        self.wave.set_intensity(intensity)
681       
682    def set_wave(self, wavelength):
683        """
684        Set wavelength list or wavelength
685        """
686        if wavelength.__class__.__name__ == 'list':
687            self.wave.set_wave_list(wavelength)
688        elif wavelength.__class__.__name__ == 'float':
689            self.wave.set_wave_list([wavelength])
690            #self.set_wavelength(wavelength)
691        else:
692            raise
693   
694    def set_wave_spread(self, wavelength_spread):
695        """
696        Set wavelength spread  or wavelength spread
697        """
698        if wavelength_spread.__class__.__name__ == 'list':
699            self.wave.set_wave_spread_list(wavelength_spread)
700        elif wavelength_spread.__class__.__name__ == 'float':
701            self.wave.set_wave_spread_list([wavelength_spread])
702            #self.set_wavelength_spread(wavelength_spread)
703        else:
704            raise
705       
706    def set_wavelength(self, wavelength):
707        """
708        Set wavelength
709        """
710        self.wavelength = wavelength
711        self.wave.set_wavelength(wavelength)
712       
713    def set_spectrum(self, spectrum):
714        """
715        Set spectrum
716        """
717        self.spectrum = spectrum
718        self.wave.set_spectrum(spectrum) 
719         
720    def set_wavelength_spread(self, wavelength_spread):
721        """
722        Set wavelength spread
723        """
724        self.wavelength_spread = wavelength_spread
725        self.wave.set_wavelength_spread(wavelength_spread)
726       
727    def set_wave_list(self, wavelength_list, wavelengthspread_list):
728        """
729        Set wavelength and its spread list
730        """
731        self.wave.set_wave_list(wavelength_list, wavelengthspread_list)
732   
733    def get_wave_list(self):
734        """
735        Set wavelength spread
736        """
737        return self.wave.get_wave_list()
738   
739    def get_intensity_list(self):
740        """
741        Set wavelength spread
742        """
743        return self.wave.get_intensity_list()
744           
745    def set_source_aperture_size(self, size):
746        """
747        Set source aperture size
748       
749        : param size: [dia_value] or [x_value, y_value]
750        """       
751        if len(size) < 1 or len(size) > 2:
752            raise RuntimeError, "The length of the size must be one or two."
753        self.aperture.set_source_size(size)
754       
755    def set_neutron_mass(self, mass):
756        """
757        Set Neutron mass
758        """
759        self.wave.set_mass(mass)
760        self.mass = mass
761       
762    def set_sample_aperture_size(self, size):
763        """
764        Set sample aperture size
765       
766        : param size: [dia_value] or [xheight_value, yheight_value]
767        """
768        if len(size) < 1 or len(size) > 2:
769            raise RuntimeError, "The length of the size must be one or two."
770        self.aperture.set_sample_size(size)
771   
772    def set_detector_pix_size(self, size):
773        """
774        Set detector pixel size
775        """
776        self.detector.set_pix_size(size)
777       
778    def set_detector_size(self, size):
779        """
780        Set detector size in number of pixels
781        : param size: [pixel_nums] or [x_pix_num, yx_pix_num]
782        """
783        self.detector.set_size(size)
784       
785    def set_source2sample_distance(self, distance):
786        """
787        Set detector source2sample_distance
788       
789        : param distance: [distance, x_offset]
790        """
791        if len(distance) < 1 or len(distance) > 2:
792            raise RuntimeError, "The length of the size must be one or two."
793        self.aperture.set_sample_distance(distance)
794
795    def set_sample2sample_distance(self, distance):
796        """
797        Set detector sample_slit2sample_distance
798       
799        : param distance: [distance, x_offset]
800        """
801        if len(distance) < 1 or len(distance) > 2:
802            raise RuntimeError, "The length of the size must be one or two."
803        self.sample.set_distance(distance)
804   
805    def set_sample2detector_distance(self, distance):
806        """
807        Set detector sample2detector_distance
808       
809        : param distance: [distance, x_offset]
810        """
811        if len(distance) < 1 or len(distance) > 2:
812            raise RuntimeError, "The length of the size must be one or two."
813        self.detector.set_distance(distance)
814       
815    def get_all_instrument_params(self):
816        """
817        Get all instrumental parameters
818        """
819        #self.intensity = self.get_intensity()
820        #self.wavelength = self.get_wavelength()
821        #self.wavelength_spread = self.get_wavelength_spread()
822        self.mass = self.get_neutron_mass()
823        self.spectrum = self.get_spectrum()
824        self.source_aperture_size = self.get_source_aperture_size()
825        self.sample_aperture_size = self.get_sample_aperture_size()
826        self.detector_pix_size = self.get_detector_pix_size()
827        self.detector_size = self.get_detector_size()
828        self.source2sample_distance = self.get_source2sample_distance()
829        self.sample2sample_distance = self.get_sample2sample_distance()
830        self.sample2detector_distance = self.get_sample2detector_distance()
831   
832    def get_detector_qrange(self):
833        """
834        get max detector q ranges
835       
836        : return: qx_min, qx_max, qy_min, qy_max tuple
837        """
838        if len(self.qxrange) != 2 or len(self.qyrange) != 2:
839            return None
840        qx_min = self.qxrange[0]
841        qx_max = self.qxrange[1]
842        qy_min = self.qyrange[0]
843        qy_max = self.qyrange[1]
844       
845        return qx_min, qx_max, qy_min, qy_max
846   
847    def _rotate_z(self, x_value, y_value, theta= 0.0):
848        """
849        Rotate x-y cordinate around z-axis by theta
850        : x_value: numpy array of x values
851        : y_value: numpy array of y values
852        : theta: angle to rotate by in rad
853       
854        :return: x_prime, y-prime
855        """       
856        # rotate by theta
857        x_prime = x_value * math.cos(theta) + y_value * math.sin(theta)
858        y_prime =  -x_value * math.sin(theta) + y_value * math.cos(theta)
859   
860        return x_prime, y_prime
861   
862    def _gaussian2d(self, x_val, y_val, x0_val, y0_val, 
863                    sigma_x, sigma_y, sigma_r):
864        """
865        Calculate 2D Gaussian distribution
866        : x_val: x value
867        : y_val: y value
868        : x0_val: mean value in x-axis
869        : y0_val: mean value in y-axis
870        : sigma_x: variance in x-direction
871        : sigma_y: variance in y-direction
872       
873        : return: gaussian (value)
874        """
875        # phi values at each points (not at the center)
876        x_value = x_val - x0_val
877        y_value = y_val - y0_val
878        phi_i = numpy.arctan2(y_val, x_val)
879
880        sin_phi = numpy.sin(phi_i)
881        cos_phi = numpy.cos(phi_i)
882       
883        x_p = x_value * cos_phi + y_value * sin_phi
884        y_p = -x_value * sin_phi + y_value * cos_phi
885       
886        new_sig_x = sqrt(sigma_r * sigma_r / (sigma_x * sigma_x ) + 1)
887        new_sig_y = sqrt(sigma_r * sigma_r / (sigma_y * sigma_y ) + 1)
888        new_x = x_p * cos_phi / new_sig_x - y_p * sin_phi
889        new_x /= sigma_x
890        new_y = x_p * sin_phi / new_sig_y + y_p * cos_phi
891        new_y /= sigma_y
892
893        nu_value = -0.5 *(new_x * new_x + new_y * new_y)
894
895        gaussian = numpy.exp(nu_value)
896        # normalizing factor correction
897        gaussian /= gaussian.sum()
898
899        return gaussian
900
901    def _gaussian2d_polar(self, x_val, y_val, x0_val, y0_val, 
902                        sigma_x, sigma_y, sigma_r):
903        """
904        Calculate 2D Gaussian distribution for polar coodinate
905        : x_val: x value
906        : y_val: y value
907        : x0_val: mean value in x-axis
908        : y0_val: mean value in y-axis
909        : sigma_x: variance in r-direction
910        : sigma_y: variance in phi-direction
911        : sigma_r: wavelength variance in r-direction
912       
913        : return: gaussian (value)
914        """
915        sigma_x = sqrt(sigma_x * sigma_x + sigma_r * sigma_r)
916        # call gaussian1d
917        gaussian  = self._gaussian1d(x_val, x0_val, sigma_x)
918        gaussian *= self._gaussian1d(y_val, y0_val, sigma_y)
919 
920        # normalizing factor correction
921        if sigma_x != 0 and sigma_y != 0:
922            gaussian *= sqrt(2 * pi)
923        return gaussian
924   
925    def _gaussian1d(self, value, mean, sigma):
926        """
927        Calculate 1D Gaussian distribution
928        : value: value
929        : mean: mean value
930        : sigma: variance
931       
932        : return: gaussian (value)
933        """
934        # default
935        gaussian = 1.0
936        if sigma != 0:
937            # get exponent
938            nu_value = (value - mean) / sigma
939            nu_value *= nu_value
940            nu_value *= -0.5
941            gaussian *= numpy.exp(nu_value)
942            gaussian /= sigma
943            # normalize
944            gaussian /= sqrt(2 * pi)
945           
946        return gaussian
947   
948    def _atan_phi(self, qy_value, qx_value):
949        """
950        Find the angle phi of q on the detector plane for qx_value, qy_value given
951        : qx_value: x component of q
952        : qy_value: y component of q
953       
954        : return phi: the azimuthal angle of q on x-y plane
955        """
956        phi = math.atan2(qy_value, qx_value)
957        return phi
958        # default
959        phi = 0
960        # ToDo: This is misterious - sign???
961        #qy_value = -qy_value
962        # Take care of the singular point
963        if qx_value == 0:
964            if qy_value > 0:
965                phi = pi / 2
966            elif qy_value < 0:
967                phi = -pi / 2
968            else:
969                phi = 0
970        else:
971            # the angle
972            phi = math.atan2(qy_value, qx_value)
973
974        return phi
975
976    def _get_detector_qxqy_pixels(self):
977        """
978        Get the pixel positions of the detector in the qx_value-qy_value space
979        """
980       
981        # update all param values
982        self.get_all_instrument_params()
983       
984        # wavelength
985        wavelength = self.wave.wavelength
986        # Gavity correction
987        delta_y = self._get_beamcenter_drop() # in cm
988       
989        # detector_pix size
990        detector_pix_size = self.detector_pix_size
991        # Square or circular pixel
992        if len(detector_pix_size) == 1:
993            pix_x_size = detector_pix_size[0]
994            pix_y_size = detector_pix_size[0]
995        # rectangular pixel pixel
996        elif len(detector_pix_size) == 2:
997            pix_x_size = detector_pix_size[0]
998            pix_y_size = detector_pix_size[1]
999        else:
1000            raise ValueError, " Input value format error..."
1001        # Sample to detector distance = sample slit to detector
1002        # minus sample offset
1003        sample2detector_distance = self.sample2detector_distance[0] - \
1004                                    self.sample2sample_distance[0]
1005        # detector offset in x-direction
1006        detector_offset = 0
1007        try:
1008            detector_offset = self.sample2detector_distance[1]
1009        except:
1010            pass
1011       
1012        # detector size in [no of pix_x,no of pix_y]
1013        detector_pix_nums_x = self.detector_size[0]
1014       
1015        # get pix_y if it exists, otherwse take it from [0]
1016        try:
1017            detector_pix_nums_y = self.detector_size[1]
1018        except:
1019            detector_pix_nums_y = self.detector_size[0]
1020       
1021        # detector offset in pix number
1022        offset_x = detector_offset / pix_x_size
1023        offset_y = delta_y / pix_y_size
1024       
1025        # beam center position in pix number (start from 0)
1026        center_x, center_y = self._get_beamcenter_position(detector_pix_nums_x,
1027                                    detector_pix_nums_y, offset_x, offset_y)
1028        # distance [cm] from the beam center on detector plane
1029        detector_ind_x = numpy.arange(detector_pix_nums_x)
1030        detector_ind_y = numpy.arange(detector_pix_nums_y)
1031
1032        # shif 0.5 pixel so that pix position is at the center of the pixel
1033        detector_ind_x = detector_ind_x + 0.5
1034        detector_ind_y = detector_ind_y + 0.5
1035
1036        # the relative postion from the beam center
1037        detector_ind_x = detector_ind_x - center_x
1038        detector_ind_y = detector_ind_y - center_y
1039       
1040        # unit correction in cm
1041        detector_ind_x = detector_ind_x * pix_x_size
1042        detector_ind_y = detector_ind_y * pix_y_size
1043       
1044        qx_value = numpy.zeros(len(detector_ind_x))
1045        qy_value = numpy.zeros(len(detector_ind_y))
1046        i = 0
1047
1048        for indx in detector_ind_x:
1049            qx_value[i] = self._get_qx(indx, sample2detector_distance, wavelength)
1050            i += 1
1051        i = 0
1052        for indy in detector_ind_y:
1053            qy_value[i] = self._get_qx(indy, sample2detector_distance, wavelength)
1054            i += 1
1055           
1056        # qx_value and qy_value values in array
1057        qx_value = qx_value.repeat(detector_pix_nums_y) 
1058        qx_value = qx_value.reshape(detector_pix_nums_x, detector_pix_nums_y)
1059        qy_value = qy_value.repeat(detector_pix_nums_x) 
1060        qy_value = qy_value.reshape(detector_pix_nums_y, detector_pix_nums_x)
1061        qy_value = qy_value.transpose()
1062
1063        # p min and max values among the center of pixels
1064        self.qx_min = numpy.min(qx_value)
1065        self.qx_max = numpy.max(qx_value)
1066        self.qy_min = numpy.min(qy_value)
1067        self.qy_max = numpy.max(qy_value)
1068               
1069        # Appr. min and max values of the detector display limits
1070        # i.e., edges of the last pixels.
1071        self.qy_min += self._get_qx(-0.5 * pix_y_size, 
1072                                sample2detector_distance, wavelength)
1073        self.qy_max += self._get_qx(0.5 * pix_y_size, 
1074                                sample2detector_distance, wavelength)
1075        #if self.qx_min == self.qx_max:
1076        self.qx_min += self._get_qx(-0.5 * pix_x_size, 
1077                                sample2detector_distance, wavelength)
1078        self.qx_max += self._get_qx(0.5 * pix_x_size, 
1079                                    sample2detector_distance, wavelength)
1080       
1081        # min and max values of detecter
1082        self.detector_qx_min = self.qx_min
1083        self.detector_qx_max = self.qx_max
1084        self.detector_qy_min = self.qy_min
1085        self.detector_qy_max = self.qy_max
1086       
1087       
1088
1089        # try to set it as a Data2D otherwise pass (not required for now)
1090        try:
1091            from sans.dataloader.data_info import Data2D
1092            output = Data2D()
1093            inten = numpy.zeros_like(qx_value)
1094            output.data     = inten
1095            output.qx_data  = qx_value
1096            output.qy_data  = qy_value           
1097        except:
1098            pass
1099       
1100        return output#qx_value,qy_value
1101       
1102    def _get_qx(self, dx_size, det_dist, wavelength):
1103        """
1104        :param dx_size: x-distance from beam center [cm]
1105        :param det_dist: sample to detector distance [cm]
1106       
1107        :return: q-value at the given position
1108        """
1109        # Distance from beam center in the plane of detector
1110        plane_dist = dx_size
1111        # full scattering angle on the x-axis
1112        theta  = math.atan(plane_dist / det_dist)
1113        qx_value     = (2.0 * pi / wavelength) * math.sin(theta)
1114        return qx_value 
1115   
1116    def _get_polar_value(self, qx_value, qy_value):
1117        """
1118        Find qr_value and phi from qx_value and qy_value values
1119       
1120        : return qr_value, phi
1121        """
1122        # find |q| on detector plane
1123        qr_value = sqrt(qx_value*qx_value + qy_value*qy_value)
1124        # find angle phi
1125        phi = self._atan_phi(qy_value, qx_value)
1126       
1127        return qr_value, phi
1128   
1129    def _get_beamcenter_position(self, num_x, num_y, offset_x, offset_y):
1130        """
1131        :param num_x: number of pixel in x-direction
1132        :param num_y: number of pixel in y-direction
1133        :param offset: detector offset in x-direction in pix number
1134       
1135        :return: pix number; pos_x, pos_y in pix index
1136        """
1137        # beam center position
1138        pos_x = num_x / 2
1139        pos_y = num_y / 2
1140
1141        # correction for offset
1142        pos_x += offset_x
1143        # correction for gravity that is always negative
1144        pos_y -= offset_y
1145
1146        return pos_x, pos_y     
1147
1148    def _get_beamcenter_drop(self):
1149        """
1150        Get the beam center drop (delta y) in y diection due to gravity
1151       
1152        :return delta y: the beam center drop in cm
1153        """
1154        # Check if mass == 0 (X-ray).
1155        if self.mass == 0:
1156            return 0
1157        # Covert unit from A to cm
1158        unit_cm = 1e-08
1159        # Velocity of neutron in horizontal direction (~ actual velocity)
1160        velocity = _PLANK_H / (self.mass * self.wave.wavelength * unit_cm)
1161        # Compute delta y
1162        delta_y = 0.5
1163        delta_y *= _GRAVITY
1164        delta_y *= self.sample2detector_distance[0] 
1165        delta_y *= (self.source2sample_distance[0] + self.sample2detector_distance[0])
1166        delta_y /= (velocity * velocity)
1167
1168        return delta_y     
1169
1170   
Note: See TracBrowser for help on using the repository browser.