source: sasview/park_integration/AbstractFitEngine.py @ 03da8ed

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 03da8ed was 1ab9dc1, checked in by Jae Cho <jhjcho@…>, 13 years ago

more stable Fit abort

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