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

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

deepcopy theory

  • Property mode set to 100644
File size: 25.9 KB
RevLine 
[aa36f96]1
[89f3b66]2import  copy
[c4d6900]3#import logging
4#import 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)
[fd0d30fd]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]
[72c7d31]286     
[7d0c1a8]287    def residuals_deriv(self, model, pars=[]):
288        """
[aa36f96]289        :return: residuals derivatives .
290       
291        :note: in this case just return empty array
292       
[7d0c1a8]293        """
294        return []
295   
[1e3169c]296class FitData2D(Data2D):
[7d0c1a8]297    """ Wrapper class  for SANS data """
[89f3b66]298    def __init__(self, sans_data2d, data=None, err_data=None):
[c4d6900]299        Data2D.__init__(self, data=data, err_data=err_data)
[7d0c1a8]300        """
[aa36f96]301        Data can be initital with a data (sans plottable)
302        or with vectors.
[7d0c1a8]303        """
[89f3b66]304        self.res_err_image = []
305        self.index_model = []
306        self.qmin = None
307        self.qmax = None
[f72333f]308        self.smearer = None
[c4d6900]309        self.radius = 0
310        self.res_err_data = []
[634ca14]311        self.sans_data = sans_data2d
[89f3b66]312        self.set_data(sans_data2d)
[f72333f]313
[89f3b66]314    def set_data(self, sans_data2d, qmin=None, qmax=None):
[1e3169c]315        """
[aa36f96]316        Determine the correct qx_data and qy_data within range to fit
[1e3169c]317        """
[89f3b66]318        self.data = sans_data2d.data
[83195f7]319        self.err_data = sans_data2d.err_data
320        self.qx_data = sans_data2d.qx_data
321        self.qy_data = sans_data2d.qy_data
[89f3b66]322        self.mask = sans_data2d.mask
[83195f7]323
324        x_max = max(math.fabs(sans_data2d.xmin), math.fabs(sans_data2d.xmax))
325        y_max = max(math.fabs(sans_data2d.ymin), math.fabs(sans_data2d.ymax))
[20d30e9]326       
327        ## fitting range
[027e8f2]328        if qmin == None:
329            self.qmin = 1e-16
330        if qmax == None:
[89f3b66]331            self.qmax = math.sqrt(x_max * x_max + y_max * y_max)
[70bf68c]332        ## new error image for fitting purpose
[89f3b66]333        if self.err_data == None or self.err_data == []:
334            self.res_err_data = numpy.ones(len(self.data))
[70bf68c]335        else:
[da58fcc]336            self.res_err_data = copy.deepcopy(self.err_data)
[9e8c150]337        #self.res_err_data[self.res_err_data==0]=1
[d8a2e31]338       
[89f3b66]339        self.radius = numpy.sqrt(self.qx_data**2 + self.qy_data**2)
[83195f7]340       
341        # Note: mask = True: for MASK while mask = False for NOT to mask
[89f3b66]342        self.index_model = ((self.qmin <= self.radius)&\
343                            (self.radius <= self.qmax))
[36bc34e]344        self.index_model = (self.index_model) & (self.mask)
345        self.index_model = (self.index_model) & (numpy.isfinite(self.data))
[f72333f]346       
[c4d6900]347    def set_smearer(self, smearer): 
[f72333f]348        """
[aa36f96]349        Set smearer
[f72333f]350        """
351        if smearer == None:
352            return
353        self.smearer = smearer
354        self.smearer.set_index(self.index_model)
355        self.smearer.get_data()
356
[c4d6900]357    def set_fit_range(self, qmin=None, qmax=None):
[7d0c1a8]358        """ to set the fit range"""
[89f3b66]359        if qmin == 0.0:
[773806e]360            self.qmin = 1e-16
[89f3b66]361        elif qmin != None:                       
[773806e]362            self.qmin = qmin           
[89f3b66]363        if qmax != None:
364            self.qmax = qmax       
365        self.radius = numpy.sqrt(self.qx_data**2 + self.qy_data**2)
366        self.index_model = ((self.qmin <= self.radius)&\
367                            (self.radius <= self.qmax))
[36bc34e]368        self.index_model = (self.index_model) &(self.mask)
369        self.index_model = (self.index_model) & (numpy.isfinite(self.data))
[c4d6900]370        self.index_model = (self.index_model) & (self.res_err_data != 0)
[aa36f96]371       
[c4d6900]372    def get_fit_range(self):
[7d0c1a8]373        """
[aa36f96]374        return the range of data.x to fit
[7d0c1a8]375        """
[20d30e9]376        return self.qmin, self.qmax
[7d0c1a8]377     
[d8a2e31]378    def residuals(self, fn): 
[83195f7]379        """
[aa36f96]380        return the residuals
[f72333f]381        """ 
382        if self.smearer != None:
383            fn.set_index(self.index_model)
384            # Get necessary data from self.data and set the data for smearing
385            fn.get_data()
386
387            gn = fn.get_value() 
388        else:
[89f3b66]389            gn = fn([self.qx_data[self.index_model],
390                     self.qy_data[self.index_model]])
[83195f7]391        # use only the data point within ROI range
[89f3b66]392        res = (self.data[self.index_model] - gn)/\
393                    self.res_err_data[self.index_model]
[3dcbe99]394        return res, gn
[0e51519]395       
[7d0c1a8]396    def residuals_deriv(self, model, pars=[]):
397        """
[aa36f96]398        :return: residuals derivatives .
399       
400        :note: in this case just return empty array
401       
[7d0c1a8]402        """
403        return []
[48882d1]404   
[4bd557d]405class FitAbort(Exception):
406    """
[aa36f96]407    Exception raise to stop the fit
[4bd557d]408    """
[1ab9dc1]409    #pass
410    #print"Creating fit abort Exception"
[4bd557d]411
412
[70bf68c]413class SansAssembly:
[ca6d914]414    """
[aa36f96]415    Sans Assembly class a class wrapper to be call in optimizer.leastsq method
[ca6d914]416    """
[e0072082]417    def __init__(self, paramlist, model=None , data=None, fitresult=None,
418                 handler=None, curr_thread=None):
[ca6d914]419        """
[aa36f96]420        :param Model: the model wrapper fro sans -model
421        :param Data: the data wrapper for sans data
422       
[ca6d914]423        """
[e0072082]424        self.model = model
425        self.data  = data
426        self.paramlist = paramlist
427        self.curr_thread = curr_thread
428        self.handler = handler
429        self.fitresult = fitresult
430        self.res = []
[4b5bd73]431        self.true_res = []
[e0072082]432        self.func_name = "Functor"
[425e49ca]433        self.theory = None
[e0072082]434       
[c4d6900]435    #def chisq(self, params):
436    def chisq(self):
[48882d1]437        """
[aa36f96]438        Calculates chi^2
439       
440        :param params: list of parameter values
441       
442        :return: chi^2
443       
[48882d1]444        """
445        sum = 0
[4b5bd73]446        for item in self.true_res:
[c4d6900]447            sum += item * item
[4b5bd73]448        if len(self.true_res) == 0:
[4bd557d]449            return None
[4b5bd73]450        return sum / len(self.true_res)
[20d30e9]451   
[c4d6900]452    def __call__(self, params):
[ca6d914]453        """
[aa36f96]454        Compute residuals
455       
456        :param params: value of parameters to fit
457       
[4b5bd73]458        """ 
459        #import thread
460        self.model.set_params(self.paramlist, params)
[425e49ca]461       
[5722d66]462        self.true_res, theory = self.data.residuals(self.model.eval)
463        self.theory = copy.deepcopy(theory)
[4b5bd73]464        # check parameters range
465        if self.check_param_range():
466            # if the param value is outside of the bound
467            # just silent return res = inf
468            return self.res
469        self.res = self.true_res       
[e0072082]470        if self.fitresult is not None and  self.handler is not None:
471            self.fitresult.set_model(model=self.model)
[4b5bd73]472            #fitness = self.chisq(params=params)
[c4d6900]473            fitness = self.chisq()
[511c6810]474            self.fitresult.pvec = params
[90c9cdf]475            self.fitresult.set_fitness(fitness=fitness)
[e0072082]476            self.handler.set_result(result=self.fitresult)
[4b5bd73]477            self.handler.update_fit()
478
[511c6810]479            if self.curr_thread != None :
[d5f0f5e3]480                try:
[078f2f2]481                    self.curr_thread.isquit()
482                except:
[acfff8b]483                    msg = "Fitting: Terminated...       Note: Forcing to stop " 
484                    msg += "fitting may cause a 'Functor error message' "
485                    msg += "being recorded in the log file....."
[1ab9dc1]486                    self.handler.error(msg)
487                    raise
488                    #return
[12cd4ec]489         
[48882d1]490        return self.res
491   
[4b5bd73]492    def check_param_range(self):
493        """
494        Check the lower and upper bound of the parameter value
495        and set res to the inf if the value is outside of the
496        range
497        :limitation: the initial values must be within range.
498        """
499
[bdc25e2]500        #time.sleep(0.01)
[4b5bd73]501        is_outofbound = False
502        # loop through the fit parameters
503        for p in self.model.parameterset:
504            param_name = p.get_name()
505            if param_name in self.paramlist:
506               
507                # if the range was defined, check the range
508                if numpy.isfinite(p.range[0]):
509                    if p.value == 0:
510                        # This value works on Scipy
511                        # Do not change numbers below
512                        value = _SMALLVALUE
513                    else:
514                        value = p.value
515                    # For leastsq, it needs a bit step back from the boundary
516                    val = p.range[0] - value * _SMALLVALUE
517                    if p.value < val: 
518                        self.res *= 1e+6
519                       
520                        is_outofbound = True
521                        break
522                if numpy.isfinite(p.range[1]):
523                    # This value works on Scipy
524                    # Do not change numbers below
525                    if p.value == 0:
526                        value = _SMALLVALUE
527                    else:
528                        value = p.value
529                    # For leastsq, it needs a bit step back from the boundary
530                    val = p.range[1] + value * _SMALLVALUE
531                    if p.value > val:
532                        self.res *= 1e+6
533                        is_outofbound = True
534                        break
535
536        return is_outofbound
537   
538   
[4c718654]539class FitEngine:
[ee5b04c]540    def __init__(self):
[ca6d914]541        """
[aa36f96]542        Base class for scipy and park fit engine
[ca6d914]543        """
544        #List of parameter names to fit
[b2f25dc5]545        self.param_list = []
[ca6d914]546        #Dictionnary of fitArrange element (fit problems)
[b2f25dc5]547        self.fit_arrange_dict = {}
[c4d6900]548 
[1cff677]549    def set_model(self, model,  id,  pars=[], constraints=[], data=None):
[4c718654]550        """
[c4d6900]551        set a model on a given  in the fit engine.
[aa36f96]552       
553        :param model: sans.models type
[c4d6900]554        :param : is the key of the fitArrange dictionary where model is
[aa36f96]555                saved as a value
556        :param pars: the list of parameters to fit
557        :param constraints: list of
558            tuple (name of parameter, value of parameters)
559            the value of parameter must be a string to constraint 2 different
560            parameters.
561            Example: 
562            we want to fit 2 model M1 and M2 both have parameters A and B.
563            constraints can be:
564             constraints = [(M1.A, M2.B+2), (M1.B= M2.A *5),...,]
565           
566             
567        :note: pars must contains only name of existing model's parameters
568       
[ca6d914]569        """
[fd6b789]570        if model == None:
571            raise ValueError, "AbstractFitEngine: Need to set model to fit"
[393f0f3]572       
[89f3b66]573        new_model = model
[393f0f3]574        if not issubclass(model.__class__, Model):
[1cff677]575            new_model = Model(model, data)
[fd6b789]576       
[89f3b66]577        if len(constraints) > 0:
[fd6b789]578            for constraint in constraints:
579                name, value = constraint
580                try:
[89f3b66]581                    new_model.parameterset[str(name)].set(str(value))
[fd6b789]582                except:
[89f3b66]583                    msg = "Fit Engine: Error occurs when setting the constraint"
[c4d6900]584                    msg += " %s for parameter %s " % (value, name)
[fd6b789]585                    raise ValueError, msg
586               
[89f3b66]587        if len(pars) > 0:
588            temp = []
[fd6b789]589            for item in pars:
590                if item in new_model.model.getParamList():
591                    temp.append(item)
[b2f25dc5]592                    self.param_list.append(item)
[fd6b789]593                else:
594                   
[89f3b66]595                    msg = "wrong parameter %s used" % str(item)
596                    msg += "to set model %s. Choose" % str(new_model.model.name)
597                    msg += "parameter name within %s" % \
598                                str(new_model.model.getParamList())
599                    raise ValueError, msg
[fd6b789]600             
[c4d6900]601            #A fitArrange is already created but contains data_list only at id
602            if self.fit_arrange_dict.has_key(id):
603                self.fit_arrange_dict[id].set_model(new_model)
604                self.fit_arrange_dict[id].pars = pars
[6831a99]605            else:
[c4d6900]606            #no fitArrange object has been create with this id
[48882d1]607                fitproblem = FitArrange()
[fd6b789]608                fitproblem.set_model(new_model)
[89f3b66]609                fitproblem.pars = pars
[c4d6900]610                self.fit_arrange_dict[id] = fitproblem
[aed7c57]611               
[d4b0687]612        else:
[6831a99]613            raise ValueError, "park_integration:missing parameters"
[48882d1]614   
[c4d6900]615    def set_data(self, data, id, smearer=None, qmin=None, qmax=None):
[aa36f96]616        """
617        Receives plottable, creates a list of data to fit,set data
618        in a FitArrange object and adds that object in a dictionary
[c4d6900]619        with key id.
[aa36f96]620       
621        :param data: data added
[c4d6900]622        :param id: unique key corresponding to a fitArrange object with data
[aa36f96]623       
[ca6d914]624        """
[89f3b66]625        if data.__class__.__name__ == 'Data2D':
626            fitdata = FitData2D(sans_data2d=data, data=data.data,
627                                 err_data=data.err_data)
[f8ce013]628        else:
[89f3b66]629            fitdata = FitData1D(x=data.x, y=data.y ,
630                                 dx=data.dx, dy=data.dy, smearer=smearer)
[634ca14]631        fitdata.sans_data = data
[393f0f3]632       
[c4d6900]633        fitdata.set_fit_range(qmin=qmin, qmax=qmax)
634        #A fitArrange is already created but contains model only at id
635        if self.fit_arrange_dict.has_key(id):
636            self.fit_arrange_dict[id].add_data(fitdata)
[d4b0687]637        else:
[c4d6900]638        #no fitArrange object has been create with this id
[89f3b66]639            fitproblem = FitArrange()
[f8ce013]640            fitproblem.add_data(fitdata)
[c4d6900]641            self.fit_arrange_dict[id] = fitproblem   
[20d30e9]642   
[c4d6900]643    def get_model(self, id):
[d4b0687]644        """
[aa36f96]645       
[c4d6900]646        :param id: id is key in the dictionary containing the model to return
[aa36f96]647       
[c4d6900]648        :return:  a model at this id or None if no FitArrange element was
649            created with this id
[aa36f96]650           
[d4b0687]651        """
[c4d6900]652        if self.fit_arrange_dict.has_key(id):
653            return self.fit_arrange_dict[id].get_model()
[d4b0687]654        else:
655            return None
656   
[c4d6900]657    def remove_fit_problem(self, id):
658        """remove   fitarrange in id"""
659        if self.fit_arrange_dict.has_key(id):
660            del self.fit_arrange_dict[id]
[a9e04aa]661           
[c4d6900]662    def select_problem_for_fit(self, id, value):
[a9e04aa]663        """
[c4d6900]664        select a couple of model and data at the id position in dictionary
[aa36f96]665        and set in self.selected value to value
666       
667        :param value: the value to allow fitting.
668                can only have the value one or zero
669               
[a9e04aa]670        """
[c4d6900]671        if self.fit_arrange_dict.has_key(id):
672            self.fit_arrange_dict[id].set_to_fit(value)
[eef2e0ed]673             
[c4d6900]674    def get_problem_to_fit(self, id):
[a9e04aa]675        """
[c4d6900]676        return the self.selected value of the fit problem of id
[aa36f96]677       
[c4d6900]678        :param id: the id of the problem
[aa36f96]679       
[a9e04aa]680        """
[c4d6900]681        if self.fit_arrange_dict.has_key(id):
682            self.fit_arrange_dict[id].get_to_fit()
[4c718654]683   
[d4b0687]684class FitArrange:
685    def __init__(self):
686        """
[aa36f96]687        Class FitArrange contains a set of data for a given model
688        to perform the Fit.FitArrange must contain exactly one model
689        and at least one data for the fit to be performed.
690       
691        model: the model selected by the user
692        Ldata: a list of data what the user wants to fit
[d4b0687]693           
694        """
695        self.model = None
[c4d6900]696        self.data_list = []
[89f3b66]697        self.pars = []
[a9e04aa]698        #self.selected  is zero when this fit problem is not schedule to fit
699        #self.selected is 1 when schedule to fit
700        self.selected = 0
[d4b0687]701       
[89f3b66]702    def set_model(self, model):
[d4b0687]703        """
[aa36f96]704        set_model save a copy of the model
705       
706        :param model: the model being set
707       
[d4b0687]708        """
709        self.model = model
710       
[89f3b66]711    def add_data(self, data):
[d4b0687]712        """
[c4d6900]713        add_data fill a self.data_list with data to fit
[aa36f96]714       
715        :param data: Data to add in the list 
716       
[d4b0687]717        """
[c4d6900]718        if not data in self.data_list:
719            self.data_list.append(data)
[d4b0687]720           
721    def get_model(self):
[aa36f96]722        """
723       
724        :return: saved model
725       
726        """
[d4b0687]727        return self.model   
728     
729    def get_data(self):
[aa36f96]730        """
731       
[c4d6900]732        :return: list of data data_list
[aa36f96]733       
734        """
[c4d6900]735        #return self.data_list
736        return self.data_list[0] 
[d4b0687]737     
[89f3b66]738    def remove_data(self, data):
[d4b0687]739        """
[aa36f96]740        Remove one element from the list
741       
[c4d6900]742        :param data: Data to remove from data_list
[aa36f96]743       
[d4b0687]744        """
[c4d6900]745        if data in self.data_list:
746            self.data_list.remove(data)
[aa36f96]747           
[a9e04aa]748    def set_to_fit (self, value=0):
749        """
[aa36f96]750        set self.selected to 0 or 1  for other values raise an exception
751       
752        :param value: integer between 0 or 1
753       
[a9e04aa]754        """
[89f3b66]755        self.selected = value
[a9e04aa]756       
757    def get_to_fit(self):
758        """
[aa36f96]759        return self.selected value
[a9e04aa]760        """
761        return self.selected
Note: See TracBrowser for help on using the repository browser.