source: sasview/park_integration/src/sans/fit/AbstractFitEngine.py @ 0766d6d

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

fixed 2d qrange set

  • Property mode set to 100644
File size: 28.1 KB
RevLine 
[aa36f96]1
[89f3b66]2import  copy
[c4d6900]3#import logging
[444c900e]4import sys
[89f3b66]5import numpy
6import math
7import park
[41517a6]8from sans.dataloader.data_info import Data1D
9from sans.dataloader.data_info import Data2D
[4b5bd73]10import time
11_SMALLVALUE = 1.0e-10   
[b2f25dc5]12   
[48882d1]13class SansParameter(park.Parameter):
14    """
[aa36f96]15    SANS model parameters for use in the PARK fitting service.
16    The parameter attribute value is redirected to the underlying
17    parameter value in the SANS model.
[48882d1]18    """
[1cff677]19    def __init__(self, name, model, data):
[ca6d914]20        """
[aa36f96]21        :param name: the name of the model parameter
22        :param model: the sans model to wrap as a park model
23       
[ca6d914]24        """
[c4d6900]25        park.Parameter.__init__(self, name)
[89f3b66]26        self._model, self._name = model, name
[1cff677]27        self.data = data
28        self.model = model
[ca6d914]29        #set the value for the parameter of the given name
30        self.set(model.getParam(name))
[48882d1]31         
[ca6d914]32    def _getvalue(self):
33        """
[aa36f96]34        override the _getvalue of park parameter
35       
36        :return value the parameter associates with self.name
37       
[ca6d914]38        """
39        return self._model.getParam(self.name)
[48882d1]40   
[89f3b66]41    def _setvalue(self, value):
[ca6d914]42        """
[aa36f96]43        override the _setvalue pf park parameter
44       
45        :param value: the value to set on a given parameter
46       
[ca6d914]47        """
[48882d1]48        self._model.setParam(self.name, value)
49       
[c4d6900]50    value = property(_getvalue, _setvalue)
[48882d1]51   
52    def _getrange(self):
[ca6d914]53        """
[aa36f96]54        Override _getrange of park parameter
55        return the range of parameter
[ca6d914]56        """
[920a6e5]57        #if not  self.name in self._model.getDispParamList():
[89f3b66]58        lo, hi = self._model.details[self.name][1:3]
[920a6e5]59        if lo is None: lo = -numpy.inf
60        if hi is None: hi = numpy.inf
61        #else:
62            #lo,hi = self._model.details[self.name][1:]
63            #if lo is None: lo = -numpy.inf
64            #if hi is None: hi = numpy.inf
[e0e22f2c]65        if lo > hi:
[05f14dd]66            raise ValueError,"wrong fit range for parameters"
67       
[89f3b66]68        return lo, hi
[48882d1]69   
[b2f25dc5]70    def get_name(self):
71        """
72        """
73        return self._getname()
74   
[89f3b66]75    def _setrange(self, r):
[ca6d914]76        """
[aa36f96]77        override _setrange of park parameter
78       
79        :param r: the value of the range to set
80       
[ca6d914]81        """
[12b76cf]82        self._model.details[self.name][1:3] = r
[89f3b66]83    range = property(_getrange, _setrange)
[a9e04aa]84   
85class Model(park.Model):
[48882d1]86    """
[aa36f96]87    PARK wrapper for SANS models.
[48882d1]88    """
[1cff677]89    def __init__(self, sans_model, sans_data=None, **kw):
[ca6d914]90        """
[aa36f96]91        :param sans_model: the sans model to wrap using park interface
92       
[ca6d914]93        """
[a9e04aa]94        park.Model.__init__(self, **kw)
[48882d1]95        self.model = sans_model
[ca6d914]96        self.name = sans_model.name
[1cff677]97        self.data = sans_data
[ca6d914]98        #list of parameters names
[48882d1]99        self.sansp = sans_model.getParamList()
[ca6d914]100        #list of park parameter
[1cff677]101        self.parkp = [SansParameter(p, sans_model, sans_data) for p in self.sansp]
[ca6d914]102        #list of parameterset
[89f3b66]103        self.parameterset = park.ParameterSet(sans_model.name, pars=self.parkp)
104        self.pars = []
[ca6d914]105 
[c4d6900]106    def get_params(self, fitparams):
[ca6d914]107        """
[aa36f96]108        return a list of value of paramter to fit
109       
110        :param fitparams: list of paramaters name to fit
111       
[ca6d914]112        """
[c4d6900]113        list_params = []
[89f3b66]114        self.pars = []
115        self.pars = fitparams
[48882d1]116        for item in fitparams:
117            for element in self.parkp:
[c4d6900]118                if element.name == str(item):
119                    list_params.append(element.value)
120        return list_params
[48882d1]121   
[c4d6900]122    def set_params(self, paramlist, params):
[ca6d914]123        """
[aa36f96]124        Set value for parameters to fit
125       
126        :param params: list of value for parameters to fit
127       
[ca6d914]128        """
[e71440c]129        try:
130            for i in range(len(self.parkp)):
131                for j in range(len(paramlist)):
[89f3b66]132                    if self.parkp[i].name == paramlist[j]:
[e71440c]133                        self.parkp[i].value = params[j]
[89f3b66]134                        self.model.setParam(self.parkp[i].name, params[j])
[e71440c]135        except:
136            raise
[ca6d914]137 
[89f3b66]138    def eval(self, x):
[ca6d914]139        """
[aa36f96]140        override eval method of park model.
141       
142        :param x: the x value used to compute a function
143       
[ca6d914]144        """
[d8a2e31]145        try:
[393f0f3]146            return self.model.evalDistribution(x)
[d8a2e31]147        except:
[393f0f3]148            raise
[c4d6900]149       
150    def eval_derivs(self, x, pars=[]):
151        """
152        Evaluate the model and derivatives wrt pars at x.
153
154        pars is a list of the names of the parameters for which derivatives
155        are desired.
156
157        This method needs to be specialized in the model to evaluate the
158        model function.  Alternatively, the model can implement is own
159        version of residuals which calculates the residuals directly
160        instead of calling eval.
161        """
162        return []
163
[a9e04aa]164
[b64fa56]165   
[1e3169c]166class FitData1D(Data1D):
167    """
[aa36f96]168    Wrapper class  for SANS data
169    FitData1D inherits from DataLoader.data_info.Data1D. Implements
170    a way to get residuals from data.
[1e3169c]171    """
[634ca14]172    def __init__(self, x, y, dx=None, dy=None, smearer=None, data=None):
[7d0c1a8]173        """
[aa36f96]174        :param smearer: is an object of class QSmearer or SlitSmearer
175           that will smear the theory data (slit smearing or resolution
176           smearing) when set.
177       
178        The proper way to set the smearing object would be to
179        do the following: ::
180       
[109e60ab]181            from DataLoader.qsmearing import smear_selection
[1e3169c]182            smearer = smear_selection(some_data)
183            fitdata1d = FitData1D( x= [1,3,..,],
184                                    y= [3,4,..,8],
185                                    dx=None,
186                                    dy=[1,2...], smearer= smearer)
[aa36f96]187       
188        :Note: that some_data _HAS_ to be of class DataLoader.data_info.Data1D
[109e60ab]189            Setting it back to None will turn smearing off.
190           
[7d0c1a8]191        """
[89f3b66]192        Data1D.__init__(self, x=x, y=y, dx=dx, dy=dy)
[634ca14]193        self.sans_data = data
[b461b6d7]194        self.smearer = smearer
[c4d6900]195        self._first_unsmeared_bin = None
196        self._last_unsmeared_bin = None
[189be4e]197        # Check error bar; if no error bar found, set it constant(=1)
[c4d6900]198        # TODO: Should provide an option for users to set it like percent,
199        # constant, or dy data
[89f3b66]200        if dy == None or dy == [] or dy.all() == 0:
201            self.dy = numpy.ones(len(y)) 
[189be4e]202        else:
[89f3b66]203            self.dy = numpy.asarray(dy).copy()
[189be4e]204
[109e60ab]205        ## Min Q-value
[4bd557d]206        #Skip the Q=0 point, especially when y(q=0)=None at x[0].
[89f3b66]207        if min (self.x) == 0.0 and self.x[0] == 0 and\
208                     not numpy.isfinite(self.y[0]):
[1e3169c]209            self.qmin = min(self.x[self.x!=0])
[773806e]210        else:                             
[89f3b66]211            self.qmin = min(self.x)
[109e60ab]212        ## Max Q-value
[89f3b66]213        self.qmax = max(self.x)
[058b2d7]214       
[72c7d31]215        # Range used for input to smearing
216        self._qmin_unsmeared = self.qmin
217        self._qmax_unsmeared = self.qmax
[fd0d30fd]218        # Identify the bin range for the unsmeared and smeared spaces
[89f3b66]219        self.idx = (self.x >= self.qmin) & (self.x <= self.qmax)
220        self.idx_unsmeared = (self.x >= self._qmin_unsmeared) \
221                            & (self.x <= self._qmax_unsmeared)
[fd0d30fd]222 
[c4d6900]223    def set_fit_range(self, qmin=None, qmax=None):
[7d0c1a8]224        """ to set the fit range"""
[09975cbb]225        # Skip Q=0 point, (especially for y(q=0)=None at x[0]).
[189be4e]226        # ToDo: Find better way to do it.
[89f3b66]227        if qmin == 0.0 and not numpy.isfinite(self.y[qmin]):
228            self.qmin = min(self.x[self.x != 0])
229        elif qmin != None:                       
[773806e]230            self.qmin = qmin           
[89f3b66]231        if qmax != None:
[eef2e0ed]232            self.qmax = qmax
[4bb2917]233        # Determine the range needed in unsmeared-Q to cover
234        # the smeared Q range
[72c7d31]235        self._qmin_unsmeared = self.qmin
236        self._qmax_unsmeared = self.qmax   
237       
[4bb2917]238        self._first_unsmeared_bin = 0
[89f3b66]239        self._last_unsmeared_bin  = len(self.x) - 1
[4bb2917]240       
[c4d6900]241        if self.smearer != None:
[89f3b66]242            self._first_unsmeared_bin, self._last_unsmeared_bin = \
243                    self.smearer.get_bin_range(self.qmin, self.qmax)
[1e3169c]244            self._qmin_unsmeared = self.x[self._first_unsmeared_bin]
245            self._qmax_unsmeared = self.x[self._last_unsmeared_bin]
[4bb2917]246           
[fd0d30fd]247        # Identify the bin range for the unsmeared and smeared spaces
[89f3b66]248        self.idx = (self.x >= self.qmin) & (self.x <= self.qmax)
249        ## zero error can not participate for fitting
250        self.idx = self.idx & (self.dy != 0) 
251        self.idx_unsmeared = (self.x >= self._qmin_unsmeared) \
252                            & (self.x <= self._qmax_unsmeared)
[0766d6d]253
[c4d6900]254    def get_fit_range(self):
[7d0c1a8]255        """
[aa36f96]256        return the range of data.x to fit
[7d0c1a8]257        """
258        return self.qmin, self.qmax
[72c7d31]259       
[7d0c1a8]260    def residuals(self, fn):
[72c7d31]261        """
[aa36f96]262        Compute residuals.
263       
264        If self.smearer has been set, use if to smear
265        the data before computing chi squared.
266       
267        :param fn: function that return model value
268       
269        :return: residuals
270       
[109e60ab]271        """
272        # Compute theory data f(x)
[89f3b66]273        fx = numpy.zeros(len(self.x))
[7e752fe]274        fx[self.idx_unsmeared] = fn(self.x[self.idx_unsmeared])
[fd0d30fd]275       
[d5b488b]276        ## Smear theory data
[109e60ab]277        if self.smearer is not None:
[89f3b66]278            fx = self.smearer(fx, self._first_unsmeared_bin, 
279                              self._last_unsmeared_bin)
[d5b488b]280        ## Sanity check
[89f3b66]281        if numpy.size(self.dy) != numpy.size(fx):
282            msg = "FitData1D: invalid error array "
283            msg += "%d <> %d" % (numpy.shape(self.dy), numpy.size(fx))                                                     
284            raise RuntimeError, msg 
[425e49ca]285        return (self.y[self.idx] - fx[self.idx]) / self.dy[self.idx], fx[self.idx]
[444c900e]286           
[72c7d31]287     
[7d0c1a8]288    def residuals_deriv(self, model, pars=[]):
289        """
[aa36f96]290        :return: residuals derivatives .
291       
292        :note: in this case just return empty array
293       
[7d0c1a8]294        """
295        return []
296   
[1e3169c]297class FitData2D(Data2D):
[7d0c1a8]298    """ Wrapper class  for SANS data """
[89f3b66]299    def __init__(self, sans_data2d, data=None, err_data=None):
[c4d6900]300        Data2D.__init__(self, data=data, err_data=err_data)
[7d0c1a8]301        """
[aa36f96]302        Data can be initital with a data (sans plottable)
303        or with vectors.
[7d0c1a8]304        """
[89f3b66]305        self.res_err_image = []
[444c900e]306        self.idx = []
[89f3b66]307        self.qmin = None
308        self.qmax = None
[f72333f]309        self.smearer = None
[c4d6900]310        self.radius = 0
311        self.res_err_data = []
[634ca14]312        self.sans_data = sans_data2d
[89f3b66]313        self.set_data(sans_data2d)
[f72333f]314
[89f3b66]315    def set_data(self, sans_data2d, qmin=None, qmax=None):
[1e3169c]316        """
[aa36f96]317        Determine the correct qx_data and qy_data within range to fit
[1e3169c]318        """
[89f3b66]319        self.data = sans_data2d.data
[83195f7]320        self.err_data = sans_data2d.err_data
321        self.qx_data = sans_data2d.qx_data
322        self.qy_data = sans_data2d.qy_data
[89f3b66]323        self.mask = sans_data2d.mask
[83195f7]324
325        x_max = max(math.fabs(sans_data2d.xmin), math.fabs(sans_data2d.xmax))
326        y_max = max(math.fabs(sans_data2d.ymin), math.fabs(sans_data2d.ymax))
[20d30e9]327       
328        ## fitting range
[027e8f2]329        if qmin == None:
330            self.qmin = 1e-16
331        if qmax == None:
[89f3b66]332            self.qmax = math.sqrt(x_max * x_max + y_max * y_max)
[70bf68c]333        ## new error image for fitting purpose
[89f3b66]334        if self.err_data == None or self.err_data == []:
335            self.res_err_data = numpy.ones(len(self.data))
[70bf68c]336        else:
[da58fcc]337            self.res_err_data = copy.deepcopy(self.err_data)
[9e8c150]338        #self.res_err_data[self.res_err_data==0]=1
[d8a2e31]339       
[89f3b66]340        self.radius = numpy.sqrt(self.qx_data**2 + self.qy_data**2)
[83195f7]341       
342        # Note: mask = True: for MASK while mask = False for NOT to mask
[444c900e]343        self.idx = ((self.qmin <= self.radius)&\
[89f3b66]344                            (self.radius <= self.qmax))
[444c900e]345        self.idx = (self.idx) & (self.mask)
346        self.idx = (self.idx) & (numpy.isfinite(self.data))
[0766d6d]347
[c4d6900]348    def set_smearer(self, smearer): 
[f72333f]349        """
[aa36f96]350        Set smearer
[f72333f]351        """
352        if smearer == None:
353            return
354        self.smearer = smearer
[444c900e]355        self.smearer.set_index(self.idx)
[f72333f]356        self.smearer.get_data()
357
[c4d6900]358    def set_fit_range(self, qmin=None, qmax=None):
[7d0c1a8]359        """ to set the fit range"""
[89f3b66]360        if qmin == 0.0:
[773806e]361            self.qmin = 1e-16
[89f3b66]362        elif qmin != None:                       
[773806e]363            self.qmin = qmin           
[89f3b66]364        if qmax != None:
365            self.qmax = qmax       
366        self.radius = numpy.sqrt(self.qx_data**2 + self.qy_data**2)
[0766d6d]367        self.idx = ((self.qmin <= self.radius)&\
[89f3b66]368                            (self.radius <= self.qmax))
[444c900e]369        self.idx = (self.idx) &(self.mask)
370        self.idx = (self.idx) & (numpy.isfinite(self.data))
371        self.idx = (self.idx) & (self.res_err_data != 0)
[0766d6d]372
[c4d6900]373    def get_fit_range(self):
[7d0c1a8]374        """
[aa36f96]375        return the range of data.x to fit
[7d0c1a8]376        """
[20d30e9]377        return self.qmin, self.qmax
[7d0c1a8]378     
[d8a2e31]379    def residuals(self, fn): 
[83195f7]380        """
[aa36f96]381        return the residuals
[f72333f]382        """ 
383        if self.smearer != None:
[444c900e]384            fn.set_index(self.idx)
[f72333f]385            # Get necessary data from self.data and set the data for smearing
386            fn.get_data()
387
388            gn = fn.get_value() 
389        else:
[444c900e]390            gn = fn([self.qx_data[self.idx],
391                     self.qy_data[self.idx]])
[83195f7]392        # use only the data point within ROI range
[d8661fb]393        res = (self.data[self.idx] - gn)/self.res_err_data[self.idx]
[0766d6d]394
[444c900e]395        return res, gn
[0e51519]396       
[7d0c1a8]397    def residuals_deriv(self, model, pars=[]):
398        """
[aa36f96]399        :return: residuals derivatives .
400       
401        :note: in this case just return empty array
402       
[7d0c1a8]403        """
404        return []
[48882d1]405   
[4bd557d]406class FitAbort(Exception):
407    """
[aa36f96]408    Exception raise to stop the fit
[4bd557d]409    """
[1ab9dc1]410    #pass
411    #print"Creating fit abort Exception"
[4bd557d]412
413
[70bf68c]414class SansAssembly:
[ca6d914]415    """
[aa36f96]416    Sans Assembly class a class wrapper to be call in optimizer.leastsq method
[ca6d914]417    """
[e0072082]418    def __init__(self, paramlist, model=None , data=None, fitresult=None,
[ba7dceb]419                 handler=None, curr_thread=None, msg_q=None):
[ca6d914]420        """
[aa36f96]421        :param Model: the model wrapper fro sans -model
422        :param Data: the data wrapper for sans data
423       
[ca6d914]424        """
[e0072082]425        self.model = model
426        self.data  = data
427        self.paramlist = paramlist
[ba7dceb]428        self.msg_q = msg_q
[e0072082]429        self.curr_thread = curr_thread
430        self.handler = handler
431        self.fitresult = fitresult
432        self.res = []
[4b5bd73]433        self.true_res = []
[e0072082]434        self.func_name = "Functor"
[425e49ca]435        self.theory = None
[e0072082]436       
[c4d6900]437    #def chisq(self, params):
438    def chisq(self):
[48882d1]439        """
[aa36f96]440        Calculates chi^2
441       
442        :param params: list of parameter values
443       
444        :return: chi^2
445       
[48882d1]446        """
447        sum = 0
[4b5bd73]448        for item in self.true_res:
[c4d6900]449            sum += item * item
[4b5bd73]450        if len(self.true_res) == 0:
[4bd557d]451            return None
[4b5bd73]452        return sum / len(self.true_res)
[20d30e9]453   
[c4d6900]454    def __call__(self, params):
[ca6d914]455        """
[aa36f96]456        Compute residuals
457       
458        :param params: value of parameters to fit
459       
[4b5bd73]460        """ 
461        #import thread
462        self.model.set_params(self.paramlist, params)
[0766d6d]463        #print "params", params
[5722d66]464        self.true_res, theory = self.data.residuals(self.model.eval)
465        self.theory = copy.deepcopy(theory)
[4b5bd73]466        # check parameters range
467        if self.check_param_range():
468            # if the param value is outside of the bound
469            # just silent return res = inf
470            return self.res
471        self.res = self.true_res       
[ba7dceb]472       
473        if self.fitresult is not None:
[e0072082]474            self.fitresult.set_model(model=self.model)
[444c900e]475            self.fitresult.residuals = self.true_res
476            self.fitresult.theory = theory
[ba7dceb]477           
[4b5bd73]478            #fitness = self.chisq(params=params)
[c4d6900]479            fitness = self.chisq()
[511c6810]480            self.fitresult.pvec = params
[90c9cdf]481            self.fitresult.set_fitness(fitness=fitness)
[ba7dceb]482            if self.msg_q is not None:
483                self.msg_q.put(self.fitresult)
484               
485            if self.handler is not None:
486                self.handler.set_result(result=self.fitresult)
487                self.handler.update_fit()
[4b5bd73]488
[511c6810]489            if self.curr_thread != None :
[d5f0f5e3]490                try:
[078f2f2]491                    self.curr_thread.isquit()
492                except:
[acfff8b]493                    msg = "Fitting: Terminated...       Note: Forcing to stop " 
494                    msg += "fitting may cause a 'Functor error message' "
495                    msg += "being recorded in the log file....."
[1ab9dc1]496                    self.handler.error(msg)
497                    raise
498                    #return
[12cd4ec]499         
[48882d1]500        return self.res
501   
[4b5bd73]502    def check_param_range(self):
503        """
504        Check the lower and upper bound of the parameter value
505        and set res to the inf if the value is outside of the
506        range
507        :limitation: the initial values must be within range.
508        """
509
[bdc25e2]510        #time.sleep(0.01)
[4b5bd73]511        is_outofbound = False
512        # loop through the fit parameters
513        for p in self.model.parameterset:
514            param_name = p.get_name()
515            if param_name in self.paramlist:
516               
517                # if the range was defined, check the range
518                if numpy.isfinite(p.range[0]):
519                    if p.value == 0:
520                        # This value works on Scipy
521                        # Do not change numbers below
522                        value = _SMALLVALUE
523                    else:
524                        value = p.value
525                    # For leastsq, it needs a bit step back from the boundary
526                    val = p.range[0] - value * _SMALLVALUE
527                    if p.value < val: 
528                        self.res *= 1e+6
529                       
530                        is_outofbound = True
531                        break
532                if numpy.isfinite(p.range[1]):
533                    # This value works on Scipy
534                    # Do not change numbers below
535                    if p.value == 0:
536                        value = _SMALLVALUE
537                    else:
538                        value = p.value
539                    # For leastsq, it needs a bit step back from the boundary
540                    val = p.range[1] + value * _SMALLVALUE
541                    if p.value > val:
542                        self.res *= 1e+6
543                        is_outofbound = True
544                        break
545
546        return is_outofbound
547   
548   
[4c718654]549class FitEngine:
[ee5b04c]550    def __init__(self):
[ca6d914]551        """
[aa36f96]552        Base class for scipy and park fit engine
[ca6d914]553        """
554        #List of parameter names to fit
[b2f25dc5]555        self.param_list = []
[ca6d914]556        #Dictionnary of fitArrange element (fit problems)
[b2f25dc5]557        self.fit_arrange_dict = {}
[7db52f1]558       
[1cff677]559    def set_model(self, model,  id,  pars=[], constraints=[], data=None):
[4c718654]560        """
[c4d6900]561        set a model on a given  in the fit engine.
[aa36f96]562       
563        :param model: sans.models type
[c4d6900]564        :param : is the key of the fitArrange dictionary where model is
[aa36f96]565                saved as a value
566        :param pars: the list of parameters to fit
567        :param constraints: list of
568            tuple (name of parameter, value of parameters)
569            the value of parameter must be a string to constraint 2 different
570            parameters.
571            Example: 
572            we want to fit 2 model M1 and M2 both have parameters A and B.
573            constraints can be:
574             constraints = [(M1.A, M2.B+2), (M1.B= M2.A *5),...,]
575           
576             
577        :note: pars must contains only name of existing model's parameters
578       
[ca6d914]579        """
[fd6b789]580        if model == None:
581            raise ValueError, "AbstractFitEngine: Need to set model to fit"
[393f0f3]582       
[89f3b66]583        new_model = model
[393f0f3]584        if not issubclass(model.__class__, Model):
[1cff677]585            new_model = Model(model, data)
[fd6b789]586       
[89f3b66]587        if len(constraints) > 0:
[fd6b789]588            for constraint in constraints:
589                name, value = constraint
590                try:
[89f3b66]591                    new_model.parameterset[str(name)].set(str(value))
[fd6b789]592                except:
[89f3b66]593                    msg = "Fit Engine: Error occurs when setting the constraint"
[c4d6900]594                    msg += " %s for parameter %s " % (value, name)
[fd6b789]595                    raise ValueError, msg
596               
[89f3b66]597        if len(pars) > 0:
598            temp = []
[fd6b789]599            for item in pars:
600                if item in new_model.model.getParamList():
601                    temp.append(item)
[b2f25dc5]602                    self.param_list.append(item)
[fd6b789]603                else:
604                   
[89f3b66]605                    msg = "wrong parameter %s used" % str(item)
606                    msg += "to set model %s. Choose" % str(new_model.model.name)
607                    msg += "parameter name within %s" % \
608                                str(new_model.model.getParamList())
609                    raise ValueError, msg
[fd6b789]610             
[c4d6900]611            #A fitArrange is already created but contains data_list only at id
612            if self.fit_arrange_dict.has_key(id):
613                self.fit_arrange_dict[id].set_model(new_model)
614                self.fit_arrange_dict[id].pars = pars
[6831a99]615            else:
[c4d6900]616            #no fitArrange object has been create with this id
[48882d1]617                fitproblem = FitArrange()
[fd6b789]618                fitproblem.set_model(new_model)
[89f3b66]619                fitproblem.pars = pars
[c4d6900]620                self.fit_arrange_dict[id] = fitproblem
[7db52f1]621                vals = []
622                for name in pars:
623                    vals.append(new_model.model.getParam(name))
624                self.fit_arrange_dict[id].vals = vals
[d4b0687]625        else:
[6831a99]626            raise ValueError, "park_integration:missing parameters"
[48882d1]627   
[c4d6900]628    def set_data(self, data, id, smearer=None, qmin=None, qmax=None):
[aa36f96]629        """
630        Receives plottable, creates a list of data to fit,set data
631        in a FitArrange object and adds that object in a dictionary
[c4d6900]632        with key id.
[aa36f96]633       
634        :param data: data added
[c4d6900]635        :param id: unique key corresponding to a fitArrange object with data
[aa36f96]636       
[ca6d914]637        """
[89f3b66]638        if data.__class__.__name__ == 'Data2D':
639            fitdata = FitData2D(sans_data2d=data, data=data.data,
640                                 err_data=data.err_data)
[f8ce013]641        else:
[89f3b66]642            fitdata = FitData1D(x=data.x, y=data.y ,
643                                 dx=data.dx, dy=data.dy, smearer=smearer)
[634ca14]644        fitdata.sans_data = data
[393f0f3]645       
[c4d6900]646        fitdata.set_fit_range(qmin=qmin, qmax=qmax)
647        #A fitArrange is already created but contains model only at id
648        if self.fit_arrange_dict.has_key(id):
649            self.fit_arrange_dict[id].add_data(fitdata)
[d4b0687]650        else:
[c4d6900]651        #no fitArrange object has been create with this id
[89f3b66]652            fitproblem = FitArrange()
[f8ce013]653            fitproblem.add_data(fitdata)
[c4d6900]654            self.fit_arrange_dict[id] = fitproblem   
[20d30e9]655   
[c4d6900]656    def get_model(self, id):
[d4b0687]657        """
[aa36f96]658       
[c4d6900]659        :param id: id is key in the dictionary containing the model to return
[aa36f96]660       
[c4d6900]661        :return:  a model at this id or None if no FitArrange element was
662            created with this id
[aa36f96]663           
[d4b0687]664        """
[c4d6900]665        if self.fit_arrange_dict.has_key(id):
666            return self.fit_arrange_dict[id].get_model()
[d4b0687]667        else:
668            return None
669   
[c4d6900]670    def remove_fit_problem(self, id):
671        """remove   fitarrange in id"""
672        if self.fit_arrange_dict.has_key(id):
673            del self.fit_arrange_dict[id]
[a9e04aa]674           
[c4d6900]675    def select_problem_for_fit(self, id, value):
[a9e04aa]676        """
[c4d6900]677        select a couple of model and data at the id position in dictionary
[aa36f96]678        and set in self.selected value to value
679       
680        :param value: the value to allow fitting.
681                can only have the value one or zero
682               
[a9e04aa]683        """
[c4d6900]684        if self.fit_arrange_dict.has_key(id):
685            self.fit_arrange_dict[id].set_to_fit(value)
[eef2e0ed]686             
[c4d6900]687    def get_problem_to_fit(self, id):
[a9e04aa]688        """
[c4d6900]689        return the self.selected value of the fit problem of id
[aa36f96]690       
[c4d6900]691        :param id: the id of the problem
[aa36f96]692       
[a9e04aa]693        """
[c4d6900]694        if self.fit_arrange_dict.has_key(id):
695            self.fit_arrange_dict[id].get_to_fit()
[4c718654]696   
[d4b0687]697class FitArrange:
698    def __init__(self):
699        """
[aa36f96]700        Class FitArrange contains a set of data for a given model
701        to perform the Fit.FitArrange must contain exactly one model
702        and at least one data for the fit to be performed.
703       
704        model: the model selected by the user
705        Ldata: a list of data what the user wants to fit
[d4b0687]706           
707        """
708        self.model = None
[c4d6900]709        self.data_list = []
[89f3b66]710        self.pars = []
[7db52f1]711        self.vals = []
[a9e04aa]712        #self.selected  is zero when this fit problem is not schedule to fit
713        #self.selected is 1 when schedule to fit
714        self.selected = 0
[d4b0687]715       
[89f3b66]716    def set_model(self, model):
[d4b0687]717        """
[aa36f96]718        set_model save a copy of the model
719       
720        :param model: the model being set
721       
[d4b0687]722        """
723        self.model = model
724       
[89f3b66]725    def add_data(self, data):
[d4b0687]726        """
[c4d6900]727        add_data fill a self.data_list with data to fit
[aa36f96]728       
729        :param data: Data to add in the list 
730       
[d4b0687]731        """
[c4d6900]732        if not data in self.data_list:
733            self.data_list.append(data)
[d4b0687]734           
735    def get_model(self):
[aa36f96]736        """
737       
738        :return: saved model
739       
740        """
[d4b0687]741        return self.model   
742     
743    def get_data(self):
[aa36f96]744        """
745       
[c4d6900]746        :return: list of data data_list
[aa36f96]747       
748        """
[c4d6900]749        #return self.data_list
750        return self.data_list[0] 
[d4b0687]751     
[89f3b66]752    def remove_data(self, data):
[d4b0687]753        """
[aa36f96]754        Remove one element from the list
755       
[c4d6900]756        :param data: Data to remove from data_list
[aa36f96]757       
[d4b0687]758        """
[c4d6900]759        if data in self.data_list:
760            self.data_list.remove(data)
[aa36f96]761           
[a9e04aa]762    def set_to_fit (self, value=0):
763        """
[aa36f96]764        set self.selected to 0 or 1  for other values raise an exception
765       
766        :param value: integer between 0 or 1
767       
[a9e04aa]768        """
[89f3b66]769        self.selected = value
[a9e04aa]770       
771    def get_to_fit(self):
772        """
[aa36f96]773        return self.selected value
[a9e04aa]774        """
775        return self.selected
[444c900e]776   
777   
778IS_MAC = True
779if sys.platform.count("win32") > 0:
780    IS_MAC = False
781   
782class FResult(object):
783    """
784    Storing fit result
785    """
786    def __init__(self, model=None, param_list=None, data=None):
787        self.calls = None
788        self.fitness = None
789        self.chisqr = None
790        self.pvec = []
791        self.cov = []
792        self.info = None
793        self.mesg = None
794        self.success = None
795        self.stderr = None
796        self.residuals = []
797        self.index = []
798        self.parameters = None
799        self.is_mac = IS_MAC
800        self.model = model
801        self.data = data
802        self.theory = []
803        self.param_list = param_list
804        self.iterations = 0
805        self.inputs = []
806        if self.model is not None and self.data is not None:
807            self.inputs = [(self.model, self.data)]
808     
809    def set_model(self, model):
810        """
811        """
812        self.model = model
813       
814    def set_fitness(self, fitness):
815        """
816        """
817        self.fitness = fitness
818       
819    def __str__(self):
820        """
821        """
822        if self.pvec == None and self.model is None and self.param_list is None:
823            return "No results"
824        n = len(self.model.parameterset)
825        self.iterations += 1
826        result_param = zip(xrange(n), self.model.parameterset)
827        msg1 = ["[Iteration #: %s ]" % self.iterations]
828        msg3 = ["=== goodness of fit: %s ===" % (str(self.fitness))]
829        if not self.is_mac:
830            msg2 = ["P%-3d  %s......|.....%s" % \
831                (p[0], p[1], p[1].value)\
832                  for p in result_param if p[1].name in self.param_list]
833            msg =  msg1 + msg3 + msg2
834        else:
835            msg = msg1 + msg3
836        msg = "\n".join(msg)
837        return msg
838   
839    def print_summary(self):
840        """
841        """
842        print self 
Note: See TracBrowser for help on using the repository browser.