source: sasview/DataLoader/qsmearing.py @ 978967c

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 978967c was 4fe4394, checked in by Mathieu Doucet <doucetm@…>, 16 years ago

implemented smearer selection

  • Property mode set to 100644
File size: 10.9 KB
RevLine 
[d00f8ff]1"""
2This software was developed by the University of Tennessee as part of the
3Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
4project funded by the US National Science Foundation.
5
6See the license text in license.txt
7
8copyright 2008, University of Tennessee
9"""
[4fe4394]10
11#TODO: improvement: allow for varying dQ as a function of Q
12
[d00f8ff]13import numpy
14import math
15import scipy.special
16
17def smear_selection(data1D):
18    """
19        Creates the right type of smearer according
[4fe4394]20        to the data.
21   
22        The canSAS format has a rule that either
23        slit smearing data OR resolution smearing data
24        is available.
25       
26        For the present purpose, we choose the one that
27        has none-zero data. If both slit and resolution
28        smearing arrays are filled with good data
29        (which should not happen), then we choose the
30        resolution smearing data.
31       
32        @param data1D: Data1D object
[d00f8ff]33    """
[4fe4394]34    # Sanity check. If we are not dealing with a SANS Data1D
35    # object, just return None
36    if data1D.__class__.__name__ != 'Data1D' \
37        or not hasattr(data1D, "dxl") or not hasattr(data1D, "dxw"):
38        return None
39   
40    # Look for resolution smearing data
41    _found_resolution = False
42    if data1D.dx is not None and len(data1D.dx)==len(data1D.x):
43       
44        # Check that we have non-zero data
45        if data1D.dx[0]>0.0:
46            _found_resolution = True
47       
48    # If we found resolution smearing data, return a QSmearer
49    if _found_resolution == True:
50        return QSmearer(data1D)
51
52    # Look for slit smearing data
53    _found_slit = False
54    if data1D.dxl is not None and len(data1D.dxl)==len(data1D.x) \
55        and data1D.dxw is not None and len(data1D.dxw)==len(data1D.x):
56       
57        # Check that we have non-zero data
58        if data1D.dxl[0]>0.0 or data1D.dxw[0]>0.0:
59            _found_slit = True
60       
61        # Sanity check: all data should be the same as a function of Q
62        for item in data1D.dxl:
63            if data1D.dxl[0] != item:
64                _found_resolution = False
65                break
66           
67        for item in data1D.dxw:
68            if data1D.dxw[0] != item:
69                _found_resolution = False
70                break
71           
72    # If we found slit smearing data, return a slit smearer
73    if _found_slit == True:
74        return SlitSmearer(data1D)
75   
76    return None
77           
[d00f8ff]78
79class _BaseSmearer(object):
80   
81    def __init__(self):
82        self.nbins = 0
83        self._weights = None
84       
85    def _compute_matrix(self): return NotImplemented
86
87    def __call__(self, iq):
88        """
89            Return the smeared I(q) value at the given q.
90            The smeared I(q) is computed using a predetermined
91            smearing matrix for a particular binning.
92       
93            @param q: I(q) array
94            @return: smeared I(q)
95        """
96        # Sanity check
97        if len(iq) != self.nbins:
98            raise RuntimeError, "Invalid I(q) vector: inconsistent array length %s <> %s" % (len(iq), self.nbins)
99       
100        if self._weights == None:
101            self._compute_matrix()
102           
103        iq_smeared = numpy.zeros(self.nbins)
104        # Loop over q-values
105        for q_i in range(self.nbins):
106            sum = 0.0
107            counts = 0.0   
108           
109            for i in range(self.nbins):
110                sum += iq[i] * self._weights[q_i][i]
111                counts += self._weights[q_i][i]
112               
113            iq_smeared[q_i] = sum/counts
114               
115        return iq_smeared   
116   
117class _SlitSmearer(_BaseSmearer):
118    """
119        Slit smearing for I(q) array
120    """
121   
122    def __init__(self, nbins=None, width=None, height=None, min=None, max=None):
123        """
124            Initialization
125           
126            @param iq: I(q) array [cm-1]
127            @param width: slit width [A-1]
128            @param height: slit height [A-1]
129            @param min: Q_min [A-1]
130            @param max: Q_max [A-1]
131        """
132        ## Slit width in Q units
133        self.width  = width
134        ## Slit height in Q units
135        self.height = height
136        ## Q_min (Min Q-value for I(q))
137        self.min    = min
138        ## Q_max (Max Q_value for I(q))
139        self.max    = max
140        ## Number of Q bins
141        self.nbins  = nbins
142        ## Number of points used in the smearing computation
143        self.npts   = 1000
144        ## Smearing matrix
145        self._weights = None
146       
147    def _compute_matrix(self):
148        """
149            Compute the smearing matrix for the current I(q) array
150        """
151        weights = numpy.zeros([self.nbins, self.nbins])
152       
153        # Loop over all q-values
154        for i in range(self.nbins):
155            q = self.min + i*(self.max-self.min)/float(self.nbins-1.0)
156           
157            # For each q-value, compute the weight of each other q-bin
158            # in the I(q) array
159            npts_h = self.npts if self.height>0 else 1
160            npts_w = self.npts if self.width>0 else 1
161           
162            # If both height and width are great than zero,
163            # modify the number of points in each direction so
164            # that the total number of points is still what
165            # the user would expect (downgrade resolution)
166            if npts_h>1 and npts_w>1:
167                npts_h = int(math.ceil(math.sqrt(self.npts)))
168                npts_w = npts_h
169               
170            for k in range(npts_h):
171                if npts_h==1:
172                    shift_h = 0
173                else:
174                    shift_h = self.height/(float(npts_h-1.0)) * k
175                for j in range(npts_w):
176                    if npts_w==1:
177                        shift_w = 0
178                    else:
179                        shift_w = self.width/(float(npts_w-1.0)) * j
180                    q_shifted = math.sqrt( ((q-shift_w)*(q-shift_w) + shift_h*shift_h) )
181                    q_i = int(math.floor( (q_shifted-self.min)/((self.max-self.min)/(self.nbins -1.0)) ))
182                   
183                    # Skip the entries outside our I(q) range
184                    #TODO: be careful with edge effect
185                    if q_i<self.nbins:
186                        weights[i][q_i] = weights[i][q_i]+1.0
187                               
188        self._weights = weights
189        return self._weights
190
191class SlitSmearer(_SlitSmearer):
192    """
193        Adaptor for slit smearing class and SANS data
194    """
195    def __init__(self, data1D):
196        """
197            Assumption: equally spaced bins of increasing q-values.
198           
199            @param data1D: data used to set the smearing parameters
200        """
201        # Initialization from parent class
202        super(SlitSmearer, self).__init__()
203       
204        ## Slit width
205        self.width = 0
206        if data1D.dxw is not None and len(data1D.dxw)==len(data1D.x):
207            self.width = data1D.dxw[0]
208            # Sanity check
209            for value in data1D.dxw:
210                if value != self.width:
211                    raise RuntimeError, "Slit smearing parameters must be the same for all data"
212               
213        ## Slit height
214        self.height = 0
215        if data1D.dxl is not None and len(data1D.dxl)==len(data1D.x):
216            self.height = data1D.dxl[0]
217            # Sanity check
218            for value in data1D.dxl:
219                if value != self.height:
220                    raise RuntimeError, "Slit smearing parameters must be the same for all data"
221       
222        ## Number of Q bins
223        self.nbins = len(data1D.x)
224        ## Minimum Q
225        self.min = data1D.x[0]
226        ## Maximum
227        self.max = data1D.x[len(data1D.x)-1]       
228
229
230class _QSmearer(_BaseSmearer):
231    """
232        Perform Gaussian Q smearing
233    """
234       
235    def __init__(self, nbins=None, width=None, min=None, max=None):
236        """
237            Initialization
238           
239            @param nbins: number of Q bins
240            @param width: standard deviation in Q [A-1]
241            @param min: Q_min [A-1]
242            @param max: Q_max [A-1]
243        """
244        ## Standard deviation in Q [A-1]
245        self.width  = width
246        ## Q_min (Min Q-value for I(q))
247        self.min    = min
248        ## Q_max (Max Q_value for I(q))
249        self.max    = max
250        ## Number of Q bins
251        self.nbins  = nbins
252        ## Smearing matrix
253        self._weights = None
254       
255    def _compute_matrix(self):
256        """
257            Compute the smearing matrix for the current I(q) array
258        """
259        weights = numpy.zeros([self.nbins, self.nbins])
260       
261        # Loop over all q-values
262        step = (self.max-self.min)/float(self.nbins-1.0)
263        for i in range(self.nbins):
264            q = self.min + i*step
265            q_min = q - 0.5*step
266            q_max = q + 0.5*step
267           
268            for j in range(self.nbins):
269                q_j = self.min + j*step
270               
271                # Compute the fraction of the Gaussian contributing
272                # to the q bin between q_min and q_max
[4fe4394]273                value =  scipy.special.erf( (q_max-q_j)/(math.sqrt(2.0)*self.width[j]) ) 
274                value -=scipy.special.erf( (q_min-q_j)/(math.sqrt(2.0)*self.width[j]) ) 
[d00f8ff]275
276                weights[i][j] += value
277                               
278        self._weights = weights
279        return self._weights
280       
281class QSmearer(_QSmearer):
282    """
283        Adaptor for Gaussian Q smearing class and SANS data
284    """
285    def __init__(self, data1D):
286        """
287            Assumption: equally spaced bins of increasing q-values.
288           
289            @param data1D: data used to set the smearing parameters
290        """
291        # Initialization from parent class
292        super(QSmearer, self).__init__()
293       
294        ## Slit width
[4fe4394]295        self.width = numpy.zeros(len(data1D.x))
[d00f8ff]296        if data1D.dx is not None and len(data1D.dx)==len(data1D.x):
[4fe4394]297            self.width = data1D.dx
[d00f8ff]298       
299        ## Number of Q bins
300        self.nbins = len(data1D.x)
301        ## Minimum Q
302        self.min = data1D.x[0]
303        ## Maximum
304        self.max = data1D.x[len(data1D.x)-1]       
305
306
307if __name__ == '__main__':
308    x = 0.001*numpy.arange(1,11)
309    y = 12.0-numpy.arange(1,11)
310    print x
311    #for i in range(10): print i, 0.001 + i*0.008/9.0
312    #for i in range(100): print i, int(math.floor( (i/ (100/9.0)) ))
313
314   
315    s = _SlitSmearer(nbins=10, width=0.0, height=0.005, min=0.001, max=0.010)
316    #s = _QSmearer(nbins=10, width=0.001, min=0.001, max=0.010)
317    s._compute_matrix()
318
319    sy = s(y)
320    print sy
321   
322    if True:
323        for i in range(10):
324            print x[i], sy[i]
325            #print q, ' : ', s.weight(q), s._compute_iq(q)
326            #print q, ' : ', s(q), s._compute_iq(q)
327            #s._compute_iq(q)
328
329
330
331
Note: See TracBrowser for help on using the repository browser.