source: sasview/DataLoader/qsmearing.py @ 65883cf

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 65883cf was 65883cf, checked in by Mathieu Doucet <doucetm@…>, 15 years ago

dataloader: fixed get_bin_range method for smearer objects

  • Property mode set to 100644
File size: 12.2 KB
Line 
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 2009, University of Tennessee
9"""
10
11import DataLoader.extensions.smearer as smearer
12import numpy
13import math
14import logging, sys
15import scipy.special
16
17def smear_selection(data1D):
18    """
19        Creates the right type of smearer according
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
33    """
34    # Sanity check. If we are not dealing with a SANS Data1D
35    # object, just return None
36    if  data1D.__class__.__name__ != 'Data1D':
37        return None
38   
39    if  not hasattr(data1D, "dx") and not hasattr(data1D, "dxl") and not hasattr(data1D, "dxw"):
40        return None
41   
42    # Look for resolution smearing data
43    _found_resolution = False
44    if data1D.dx is not None and len(data1D.dx)==len(data1D.x):
45       
46        # Check that we have non-zero data
47        if data1D.dx[0]>0.0:
48            _found_resolution = True
49            #print "_found_resolution",_found_resolution
50            #print "data1D.dx[0]",data1D.dx[0],data1D.dxl[0]
51    # If we found resolution smearing data, return a QSmearer
52    if _found_resolution == True:
53        return QSmearer(data1D)
54
55    # Look for slit smearing data
56    _found_slit = False
57    if data1D.dxl is not None and len(data1D.dxl)==len(data1D.x) \
58        and data1D.dxw is not None and len(data1D.dxw)==len(data1D.x):
59       
60        # Check that we have non-zero data
61        if data1D.dxl[0]>0.0 or data1D.dxw[0]>0.0:
62            _found_slit = True
63       
64        # Sanity check: all data should be the same as a function of Q
65        for item in data1D.dxl:
66            if data1D.dxl[0] != item:
67                _found_resolution = False
68                break
69           
70        for item in data1D.dxw:
71            if data1D.dxw[0] != item:
72                _found_resolution = False
73                break
74    # If we found slit smearing data, return a slit smearer
75    if _found_slit == True:
76        return SlitSmearer(data1D)
77   
78    return None
79           
80
81class _BaseSmearer(object):
82   
83    def __init__(self):
84        self.nbins = 0
85        self._weights = None
86        ## Internal flag to keep track of C++ smearer initialization
87        self._init_complete = False
88        self._smearer = None
89       
90    def __deepcopy__(self, memo={}):
91        """
92            Return a valid copy of self.
93            Avoid copying the _smearer C object and force a matrix recompute
94            when the copy is used. 
95        """
96        result = _BaseSmearer()
97        result.nbins = self.nbins
98        return result
99
100       
101    def _compute_matrix(self): return NotImplemented
102
103    def get_bin_range(self, q_min=None, q_max=None):
104        """
105            @param q_min: minimum q-value to smear
106            @param q_max: maximum q-value to smear
107        """
108        # If this is the first time we call for smearing,
109        # initialize the C++ smearer object first
110        if not self._init_complete:
111            self._initialize_smearer()
112           
113        if q_min == None:
114            q_min = self.min
115       
116        if q_max == None:
117            q_max = self.max
118       
119        _qmin_unsmeared, _qmax_unsmeared = self.get_unsmeared_range(q_min, q_max)
120       
121        _first_bin = None
122        _last_bin  = None
123
124        step = (self.max-self.min)/(self.nbins-1.0)
125        try:
126            for i in range(self.nbins):
127                q_i = smearer.get_q(self._smearer, i)
128                if (q_i >= _qmin_unsmeared) and (q_i <= _qmax_unsmeared):
129                    # Identify first and last bin
130                    if _first_bin is None:
131                        _first_bin = i
132                    else:
133                        _last_bin  = i
134        except:
135            raise RuntimeError, "_BaseSmearer.get_bin_range: error getting range\n  %s" % sys.exc_value
136               
137        return _first_bin, _last_bin
138
139    def __call__(self, iq_in, first_bin=0, last_bin=None):
140        """
141            Perform smearing
142        """
143        # If this is the first time we call for smearing,
144        # initialize the C++ smearer object first
145        if not self._init_complete:
146            self._initialize_smearer()
147             
148        # Get the max value for the last bin
149        if last_bin is None or last_bin>=len(iq_in):
150            last_bin = len(iq_in)-1
151        # Check that the first bin is positive
152        if first_bin<0:
153            first_bin = 0
154           
155        # Sanity check
156        if len(iq_in) != self.nbins:
157            raise RuntimeError, "Invalid I(q) vector: inconsistent array length %d != %s" % (len(iq_in), str(self.nbins))
158             
159        # Storage for smeared I(q)   
160        iq_out = numpy.zeros(self.nbins)
161        smear_output = smearer.smear(self._smearer, iq_in, iq_out, first_bin, last_bin)
162        if smear_output < 0:
163            raise RuntimeError, "_BaseSmearer: could not smear, code = %g" % smear_output
164        return iq_out
165   
166class _SlitSmearer(_BaseSmearer):
167    """
168        Slit smearing for I(q) array
169    """
170   
171    def __init__(self, nbins=None, width=None, height=None, min=None, max=None):
172        """
173            Initialization
174           
175            @param iq: I(q) array [cm-1]
176            @param width: slit width [A-1]
177            @param height: slit height [A-1]
178            @param min: Q_min [A-1]
179            @param max: Q_max [A-1]
180        """
181        _BaseSmearer.__init__(self)
182        ## Slit width in Q units
183        self.width  = width
184        ## Slit height in Q units
185        self.height = height
186        ## Q_min (Min Q-value for I(q))
187        self.min    = min
188        ## Q_max (Max Q_value for I(q))
189        self.max    = max
190        ## Number of Q bins
191        self.nbins  = nbins
192        ## Number of points used in the smearing computation
193        self.npts   = 10000
194        ## Smearing matrix
195        self._weights = None
196        self.qvalues  = None
197       
198    def _initialize_smearer(self):
199        """
200            Initialize the C++ smearer object.
201            This method HAS to be called before smearing
202        """
203        #self._smearer = smearer.new_slit_smearer(self.width, self.height, self.min, self.max, self.nbins)
204        self._smearer = smearer.new_slit_smearer_with_q(self.width, self.height, self.qvalues)
205        self._init_complete = True
206
207    def get_unsmeared_range(self, q_min, q_max):
208        """
209            Determine the range needed in unsmeared-Q to cover
210            the smeared Q range
211        """
212        # Range used for input to smearing
213        _qmin_unsmeared = q_min
214        _qmax_unsmeared = q_max
215        try:
216            _qmin_unsmeared = self.min
217            _qmax_unsmeared = self.max
218        except:
219            logging.error("_SlitSmearer.get_bin_range: %s" % sys.exc_value)
220        return _qmin_unsmeared, _qmax_unsmeared
221
222class SlitSmearer(_SlitSmearer):
223    """
224        Adaptor for slit smearing class and SANS data
225    """
226    def __init__(self, data1D):
227        """
228            Assumption: equally spaced bins of increasing q-values.
229           
230            @param data1D: data used to set the smearing parameters
231        """
232        # Initialization from parent class
233        super(SlitSmearer, self).__init__()
234       
235        ## Slit width
236        self.width = 0
237        if data1D.dxw is not None and len(data1D.dxw)==len(data1D.x):
238            self.width = data1D.dxw[0]
239            # Sanity check
240            for value in data1D.dxw:
241                if value != self.width:
242                    raise RuntimeError, "Slit smearing parameters must be the same for all data"
243               
244        ## Slit height
245        self.height = 0
246        if data1D.dxl is not None and len(data1D.dxl)==len(data1D.x):
247            self.height = data1D.dxl[0]
248            # Sanity check
249            for value in data1D.dxl:
250                if value != self.height:
251                    raise RuntimeError, "Slit smearing parameters must be the same for all data"
252       
253        ## Number of Q bins
254        self.nbins = len(data1D.x)
255        ## Minimum Q
256        self.min = min(data1D.x)
257        ## Maximum
258        self.max = max(data1D.x)
259        ## Q-values
260        self.qvalues = data1D.x
261       
262
263class _QSmearer(_BaseSmearer):
264    """
265        Perform Gaussian Q smearing
266    """
267       
268    def __init__(self, nbins=None, width=None, min=None, max=None):
269        """
270            Initialization
271           
272            @param nbins: number of Q bins
273            @param width: array standard deviation in Q [A-1]
274            @param min: Q_min [A-1]
275            @param max: Q_max [A-1]
276        """
277        _BaseSmearer.__init__(self)
278        ## Standard deviation in Q [A-1]
279        self.width  = width
280        ## Q_min (Min Q-value for I(q))
281        self.min    = min
282        ## Q_max (Max Q_value for I(q))
283        self.max    = max
284        ## Number of Q bins
285        self.nbins  = nbins
286        ## Smearing matrix
287        self._weights = None
288        self.qvalues  = None
289       
290    def _initialize_smearer(self):
291        """
292            Initialize the C++ smearer object.
293            This method HAS to be called before smearing
294        """
295        #self._smearer = smearer.new_q_smearer(numpy.asarray(self.width), self.min, self.max, self.nbins)
296        self._smearer = smearer.new_q_smearer_with_q(numpy.asarray(self.width), self.qvalues)
297        self._init_complete = True
298       
299    def get_unsmeared_range(self, q_min, q_max):
300        """
301            Determine the range needed in unsmeared-Q to cover
302            the smeared Q range
303            Take 3 sigmas as the offset between smeared and unsmeared space
304        """
305        # Range used for input to smearing
306        _qmin_unsmeared = q_min
307        _qmax_unsmeared = q_max
308        try:
309            offset = 3.0*max(self.width)
310            _qmin_unsmeared = max([self.min, q_min-offset])
311            _qmax_unsmeared = min([self.max, q_max+offset])
312        except:
313            logging.error("_QSmearer.get_bin_range: %s" % sys.exc_value)
314        return _qmin_unsmeared, _qmax_unsmeared
315       
316       
317       
318class QSmearer(_QSmearer):
319    """
320        Adaptor for Gaussian Q smearing class and SANS data
321    """
322    def __init__(self, data1D):
323        """
324            Assumption: equally spaced bins of increasing q-values.
325           
326            @param data1D: data used to set the smearing parameters
327        """
328        # Initialization from parent class
329        super(QSmearer, self).__init__()
330       
331        ## Resolution
332        self.width = numpy.zeros(len(data1D.x))
333        if data1D.dx is not None and len(data1D.dx)==len(data1D.x):
334            self.width = data1D.dx
335       
336        ## Number of Q bins
337        self.nbins = len(data1D.x)
338        ## Minimum Q
339        self.min = min(data1D.x)
340        ## Maximum
341        self.max = max(data1D.x)
342        ## Q-values
343        self.qvalues = data1D.x
344
345
346if __name__ == '__main__':
347    x = 0.001*numpy.arange(1,11)
348    y = 12.0-numpy.arange(1,11)
349    print x
350    #for i in range(10): print i, 0.001 + i*0.008/9.0
351    #for i in range(100): print i, int(math.floor( (i/ (100/9.0)) ))
352
353   
354    s = _SlitSmearer(nbins=10, width=0.0, height=0.005, min=0.001, max=0.010)
355    #s = _QSmearer(nbins=10, width=0.001, min=0.001, max=0.010)
356    s._compute_matrix()
357
358    sy = s(y)
359    print sy
360   
361    if True:
362        for i in range(10):
363            print x[i],y[i], sy[i]
364            #print q, ' : ', s.weight(q), s._compute_iq(q)
365            #print q, ' : ', s(q), s._compute_iq(q)
366            #s._compute_iq(q)
367
368
369
370
Note: See TracBrowser for help on using the repository browser.