source: sasview/DataLoader/invariant.py @ edd166b

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 edd166b was d23528ee, checked in by Gervaise Alina <gervyh@…>, 15 years ago

working on invariant, must the test are not passing yet.need more testing.incompleted

  • Property mode set to 100644
File size: 19.0 KB
Line 
1"""
2    This module is responsible to compute invariant related computation.
3    @author: Gervaise B. Alina/UTK
4"""
5import math 
6import numpy
7from DataLoader.data_info import Data1D as LoaderData1D
8from sans.fit.Fitting import Fit
9INFINITY = 10
10MIN = 0
11STEP= 1000
12
13class InvariantCalculator(object):
14    """
15        Compute invariant if data is given.
16        Can provide volume fraction and surface area if the user provides
17        Porod constant  and contrast values.
18        @precondition:  the user must send a data of type DataLoader.Data1D
19        @note: The data boundaries are assumed as infinite range.
20    """
21    def __init__(self, data):
22        """
23            Initialize variables
24            @param data: data must be of type DataLoader.Data1D
25        """
26        #Initialization of variables containing data's info
27        self.data = data
28        #Initialization  of the model to use to extrapolate a low q
29        self.model_min = None
30        #Initialization  of the model to use to extrapolate a high q
31        self.model_max = None
32        #Initialization of variable that contains invariant extrapolate to low q
33        self.q_star_min = 0
34        #Initialization of variable that contains invariant extrapolate to low q
35        self.q_star_max = 0
36        #Initialization of variables containing invariant's info
37        self.q_star = self._get_qstar(data= data)
38        # Initialization of the invariant total
39        self.q_star_total = self.q_star_min + self.q_star + self.q_star_max
40
41
42    def get_qstar_error(self, data):
43        """
44            @param data: data of type Data1D
45            @return invariant error
46        """
47        if not issubclass(data.__class__, LoaderData1D):
48            #Process only data that inherited from DataLoader.Data_info.Data1D
49            raise ValueError,"Data must be of type DataLoader.Data1D"
50       
51        if data.is_slit_smeared():
52            return self._getQStarUnsmearError(data= data)
53        else:
54            return self._getQStarSmearError(data= data)
55         
56         
57    def get_qstar_min(self, data, model, npts):
58        """
59            Set parameters of the model send by the user to value return by the fit
60            engine. Store that model in self.model_min for further use such as plotting.
61           
62            Extrapolate data to low q with npts number of point.
63            This function find the parameters that fit a data  with a given model.
64            Then create a new data with its q vector between 0 and the previous data
65            first q value.
66            where  the step between the points is the distance data.x[1] - data.x[0]
67            The new data has a y defines by model.evalDistrution(new data.x)
68            Finally compute the invariant for this new created data.
69           
70            @param data: data of type Data1D
71            @param model: the model uses to extrapolate the data
72            @param npts: the number of points used to extrapolate. npts number
73            of points will be selected from the last of points of q data to the
74            npts th points of q data.
75             
76            @return invariant value extrapolated to low q
77        """ 
78        if model == None or npts== 0:
79            return 0
80        npts = int(npts)
81        x = data.x[0: npts]
82        qmin = 0
83        qmax = x[len(x)-1]
84        dx = None
85        dy = None
86        dxl = None
87        dxw = None
88        if data.dx !=None:
89            dx = data.dx[0:int(npts)]
90        if data.dy !=None:
91            dxl = data.dy[0:int(npts)]
92        if data.dxl !=None:
93            dx = data.dxl[0:int(npts)]
94        if data.dxw !=None:
95            dxw = data.dxw[0:int(npts)]
96           
97        # fit the data with a model to get the appropriate parameters
98        self.model_min = self._fit( data, model, qmin=qmin, qmax= qmax)
99        #Get x between 0 and data.x[0] with step define such as
100        step = math.fabs(x[1]- x[0])
101        #create new Data1D to compute the invariant
102        new_x = numpy.linspace(start= MIN,
103                           stop= x[0],
104                           num= npts,
105                           endpoint=True
106                           )
107        new_y = model.evalDistribution(new_x)
108        min_data = LoaderData1D(x= new_x,y= new_y)
109        min_data.dxl = dxl
110        min_data.dxw = dxw
111        data.clone_without_data( clone= min_data)
112       
113        return self._get_qstar(data= min_data)
114         
115         
116    def get_qstar_max(self, data, model, npts):
117        """
118            Set parameters of the model send by the user to value return by the fit
119            engine. Store that model in self.model_max for further use such as plotting.
120           
121            Extrapolate data to low q with npts number of point
122            @param data: data of type Data1D
123            @param model: the model uses to extrapolate the data
124            @param npts: the number of points used to extrapolate
125            @return invariant value extrapolated to low q
126        """ 
127        if model == None or npts== 0:
128            return 0
129       
130        index_max = len(data.x) -1
131        index_min = index_max -int(npts)
132        x = data.x[index_min:index_max]
133        qmin = x[0]
134        qmax = x[len(x)-1]
135        dx = None
136        dy = None
137        dxl = None
138        dxw = None
139        if data.dx !=None:
140            dx = data.dx[qmin:qmax]
141        if data.dy !=None:
142            dxl = data.dy[qmin:qmax]
143        if data.dxl !=None:
144            dx = data.dxl[qmin:qmax]
145        if data.dxw !=None:
146            dxw = data.dxw[0:int(npts)]
147           
148        # fit the data with a model to get the appropriate parameters
149        self.model_max = self._fit( data, model, qmin= qmin, qmax= qmax)
150        #Create new Data1D
151        new_x = numpy.linspace(start= data.x[qmax],
152                           stop= INFINITY,
153                           num= npts,
154                           endpoint=True
155                           )
156        new_y = model.evalDistribution(new_x)
157        #create a Data1D to compute the invariant
158        max_data = LoaderData1D(x= new_x,y= new_y)
159        max_data.dxl = dxl
160        max_data.dxw = dxw
161        data.clone_without_data( clone= max_data)
162       
163        return self._get_qstar(data= max_data)
164         
165         
166   
167    def get_volume_fraction(self, contrast):
168        """
169            Compute volume fraction is given by:
170           
171                q_star= 2*(pi*contrast)**2* volume( 1- volume)
172                for k = 10^(8)*q_star/(2*(pi*|contrast|)**2)
173                we get 2 values of volume:
174                     volume1 = (1- sqrt(1- 4*k))/2
175                     volume2 = (1+ sqrt(1- 4*k))/2
176                contrast unit is 1/A^(2)= 10^(16)cm^(2)
177                q_star unit  1/A^(3)*1/cm
178               
179            the result returned will be 0<= volume <= 1
180           
181            @param contrast: contrast value provides by the user of type float
182            @return: volume fraction
183            @note: volume fraction must have no unit
184        """
185        if contrast < 0:
186            raise ValueError, "contrast must be greater than zero" 
187        if self.q_star ==None:
188            raise RuntimeError, "Q_star is not defined"
189       
190        #Get the total invariant with our without extrapolation
191        self.q_star_total = self.q_star + self.q_star_min + self.q_star_max
192       
193        if self.q_star_total < 0:
194            raise ValueError, "invariant must be greater than zero"
195       
196        #compute intermediate constant
197        k =  1.e-8*self.q_star_total /(2*(math.pi* math.fabs(float(contrast)))**2)
198        #check discriminant value
199        discrim= 1 - 4*k
200        if discrim < 0:
201            raise RuntimeError, "could not compute the volume fraction: negative discriminant"
202        elif discrim ==0:
203            volume = 1/2
204            return volume
205        else:
206            # compute the volume
207            volume1 = 0.5 *(1 - math.sqrt(discrim))
208            volume2 = 0.5 *(1 + math.sqrt(discrim))
209           
210            if 0<= volume1 and volume1 <= 1:
211                return volume1
212            elif 0<= volume2 and volume2<= 1: 
213                return volume2
214            raise RuntimeError, "could not compute the volume fraction: inconsistent results"
215   
216   
217    def get_surface(self, contrast, porod_const):
218        """
219            Compute the surface given by:
220                surface = (2*pi *volume(1- volume)*pConst)/ q_star
221               
222            @param contrast: contrast value provides by the user of type float
223            @param porod_const: Porod constant
224            @return: specific surface
225        """
226        # Compute the volume
227        volume = self.get_volume_fraction(contrast)
228       
229        #Check whether we have Q star
230        if self.q_star ==None:
231            raise RuntimeError, "Q_star is not defined"
232       
233        #Get the total invariant with our without extrapolation
234        self.q_star_total = self.q_star + self.q_star_min + self.q_star_max
235       
236        if self.q_star_total == 0:
237            raise ValueError, "invariant must be greater than zero. Q_star=%g" % self.q_star
238       
239        return 2*math.pi* volume*(1- volume)*float(porod_const)/self.q_star_total
240       
241       
242       
243    def _get_qstar_unsmeared(self, data):
244        """
245            @param data: data of type Data1D
246            Compute invariant given by
247            q_star= x0**2 *y0 *dx0 +x1**2 *y1 *dx1 + ..+ xn**2 *yn *dxn
248            where n= infinity
249            dxi = 1/2*(xi+1 - xi) + (xi - xi-1)
250            dx0 = x0+ (x1 - x0)/2
251            dxn = xn - xn-1
252        """
253        if len(data.x)<=1 or len(data.y)<=1 or len(data.x)!=len(data.y):
254            msg=  "Length x and y must be equal"
255            msg +=" and greater than 1; got x=%s, y=%s"%(len(data.x), len(data.y))
256            raise ValueError, msg
257       
258        elif len(data.x)==1 and len(data.y)==1:
259            return 0
260   
261        else:
262            n = len(data.x)-1
263            #compute the first delta
264            dx0 = data.x[0] + (data.x[1]- data.x[0])/2
265            #compute the last delta
266            dxn= data.x[n]- data.x[n-1]
267            sum = 0
268            sum += data.x[0]* data.x[0]* data.y[0]*dx0
269            sum += data.x[n]* data.x[n]* data.y[n]*dxn
270            if len(data.x)==2:
271                return sum
272            else:
273                #iterate between for element different from the first and the last
274                for i in xrange(1, n-1):
275                    dxi = (data.x[i+1] - data.x[i-1])/2
276                    sum += data.x[i]*data.x[i]* data.y[i]* dxi
277                return sum
278           
279         
280   
281    def _get_qstar(self, data):
282        """
283            @param data: data of type Data1D
284            @return invariant value
285        """
286        if not issubclass(data.__class__, LoaderData1D):
287            #Process only data that inherited from DataLoader.Data_info.Data1D
288            raise ValueError,"Data must be of type DataLoader.Data1D"
289       
290        # Check whether we have slit smearing information
291        if data.is_slit_smeared():
292            return self._get_qstar_smeared(data= data)
293        else:
294            return self._get_qstar_unsmeared(data= data)
295       
296       
297    def _getQStarUnsmearError(self, data):
298        """
299            @param data: data of type Data1D
300            Compute invariant uncertainty on y given by
301            q_star = math.sqrt[(x0**2*(dy0)*dx0)**2 +
302                    (x1**2 *(dy1)*dx1)**2 + ..+ (xn**2 *(dyn)*dxn)**2 ]
303            where n = infinity
304            dxi = 1/2*(xi+1 - xi) + (xi - xi-1)
305            dx0 = x0+ (x1 - x0)/2
306            dxn = xn - xn-1
307            dyn: error on dy
308            note: if data doesn't contains dy assume dy= math.sqrt(data.y)
309        """
310        if len(data.x)<=1 or len(data.y)<=1 or len(data.x)!=len(data.y):
311            msg=  "Length x and y must be equal"
312            msg +=" and greater than 1; got x=%s, y=%s"%(len(data.x),
313                                                         len(data.y))
314            raise ValueError,msg
315        elif len(data.x)==1 and len(data.y)==1:
316            return 0
317   
318        else:
319            #Create error for data without dy error
320            if data.dy ==None or data.dy==[]:
321                dy=[math.sqrt(y) for y in data.y]
322            else:
323                dy= data.dy
324               
325            n = len(data.x)-1
326            #compute the first delta
327            dx0 = data.x[0] + (data.x[1]- data.x[0])/2
328            #compute the last delta
329            dxn= data.x[n]- data.x[n-1]
330            sum = 0
331            sum += (data.x[0]* data.x[0]* dy[0]*dx0)**2
332            sum += (data.x[n]* data.x[n]* dy[n]*dxn)**2
333            if len(data.x)==2:
334                return math.sqrt(sum)
335            else:
336                #iterate between for element different from the first and the last
337                for i in xrange(1, n-1):
338                    dxi = (data.x[i+1] - data.x[i-1])/2
339                    sum += (data.x[i]*data.x[i]* dy[i]* dxi)**2
340                return math.sqrt(sum)
341           
342               
343    def _get_qstar_smeared(self, data):
344        """
345            @param data: data of type Data1D
346            Compute invariant with slit-smearing info
347            q_star= x0*dxl *y0 *dx0 + x1*dxl *y1 *dx1 + ..+ xn*dxl *yn *dxn
348            where n= infinity
349            dxi = 1/2*(xi+1 - xi) + (xi - xi-1)
350            dx0 = x0+ (x1 - x0)/2
351            dxn = xn - xn-1
352            dxl: slit smearing value
353        """
354       
355        if data.dxl ==None:
356            msg = "Cannot compute slit Smear invariant dxl "
357            msg +="must be a list, got dxl= %s , dxw= %s"%(str(data.dxl), str(data.dxw))
358            raise ValueError,msg
359
360        if len(data.x)<=1 or len(data.y)<=1 or len(data.x)!=len(data.y)\
361                or len(data.x)!= len(data.dxl):
362           
363            msg = "x, dxl, and y must be have the same length and greater than 1"
364            raise ValueError,msg
365        else:
366            n= len(data.x)-1
367            #compute the first delta
368            dx0= data.x[0] +(data.x[1]- data.x[0])/2
369            #compute the last delta
370            dxn= data.x[n]- data.x[n-1]
371            sum = 0
372            sum += data.x[0]* data.dxl[0]* data.y[0]*dx0
373            sum += data.x[n]* data.dxl[n]* data.y[n]*dxn
374            if len(data.x)==2:
375                return sum
376            else:
377                #iterate between for element different from the first and the last
378                for i in xrange(1, n-1):
379                    dxi = (data.x[i+1] - data.x[i-1])/2
380                    sum += data.x[i]* data.dxl[i]* data.y[i]* dxi
381                return sum
382       
383    def _getQStarSmearError(self, data):
384        """
385            @param data: data of type Data1D
386            Compute invariant with slit smearing info
387            q_star= x0*dxl *dy0 *dx0 + x1*dxl *dy1 *dx1 + ..+ xn*dxl *dyn *dxn
388            where n= infinity
389            dxi = 1/2*(xi+1 - xi) + (xi - xi-1)
390            dx0 = x0+ (x1 - x0)/2
391            dxn = xn - xn-1
392            dxl: slit smearing value
393            dyn : error on dy
394            note: if data doesn't contains dy assume dy= math.sqrt(data.y)
395        """
396        if data.dxl ==None:
397            msg = "Cannot compute Smear invariant dxl "
398            msg +="must be a list, got dx= %s"%str(data.dxl)
399            raise ValueError,msg
400
401        if len(data.x)<=1 or len(data.y)<=1 or len(data.x)!=len(data.y)\
402                or len(data.x)!= len(data.dxl):
403           
404            msg = "x, dxl, and y must be have the same length and greater than 1"
405            raise ValueError,msg
406        else:
407            #Create error for data without dy error
408            if data.dy ==None or data.dy==[]:
409                dy= [math.sqrt(y) for y in data.y]
410            else:
411                dy= data.dy
412               
413            n= len(data.x)-1
414            #compute the first delta
415            dx0= data.x[0] +(data.x[1]- data.x[0])/2
416            #compute the last delta
417            dxn= data.x[n]- data.x[n-1]
418            sum = 0
419            sum += (data.x[0]* data.dxl[0]* dy[0]*dx0)**2
420            sum += (data.x[n]* data.dxl[n]* dy[n]*dxn)**2
421            if len(data.x)==2:
422                return math.sqrt(sum)
423            else:
424                #iterate between for element different from the first and the last
425                for i in xrange(1, n-1):
426                    dxi = (data.x[i+1] - data.x[i-1])/2
427                    sum += (data.x[i]* data.dxl[i]* dy[i]* dxi)**2
428                return math.sqrt(sum)
429       
430    def _getVolFracError(self, contrast):
431        """
432            Compute the error on volume fraction uncertainty where
433            uncertainty is delta volume = 1/2 * (4*k* uncertainty on q_star)
434                                 /(2* math.sqrt(1-k* q_star))
435                                 
436            for k = 10^(8)*q_star/(2*(pi*|contrast|)**2)
437            @param contrast: contrast value provides by the user of type float
438        """
439        if self.q_star_total == None or self.q_star_err == None:
440            return 
441       
442        if self.q_star_total < 0:
443            raise ValueError, "invariant must be greater than zero"
444       
445        k =  1.e-8*self.q_star_total /(2*(math.pi* math.fabs(float(contrast)))**2)
446        #check discriminant value
447        discrim = 1 - 4*k
448        if  1- k*self.q_star <=0:
449            raise ValueError, "Cannot compute incertainty on volume"
450       
451        uncertainty = (0.5 *4*k* self.q_star_err)/(2*math.sqrt(1- k*self.q_star))
452        return math.fabs(uncertainty)
453   
454   
455    def _fit(self, data, model, qmin=None, qmax=None):
456        """
457            perform fit
458            @param data: data to fit
459            @param model: model to fit
460            @return: model with the parameters computed by the fitting engine
461        """
462        id = 1
463        fitter = Fit('scipy')
464        fitter.set_data(data,id)
465        pars=[]
466       
467        for param  in model.getParamList() :
468            # TODO: Remove the background from computation before fitting?
469            if param not in model.getDispParamList():
470                pars.append(param)
471        fitter.set_model(model,id,pars)
472        fitter.select_problem_for_fit(Uid=id,value=1)
473        result =  fitter.fit()
474        out = result.pvec
475        # Set the new parameter back to the model
476        if out== None:
477            #The fit engine didn't find parameters , the model is returned in
478            # the same state
479            return model
480        # Assigned new parameters values to the model
481        if out.__class__== numpy.float64:
482            #Only one parameter was fitted
483            model.setParam(pars[0], out)
484        else:
485            for index in xrange(len(pars)):
486                model.setParam(pars[index], out[index])
487        return model
488       
489       
490       
Note: See TracBrowser for help on using the repository browser.