source: sasview/sansview/perspectives/fitting/fitting.py @ a890be6

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

modified fitting for data 2dD

  • Property mode set to 100644
File size: 23.6 KB
Line 
1import os,os.path, re
2import sys, wx, logging
3import string, numpy, pylab, math
4
5from copy import deepcopy
6from danse.common.plottools.plottables import Data1D, Theory1D, Data2D
7
8from sans.guiframe.data_loader import MetaData1D, MetaTheory1D, MetaData2D
9from danse.common.plottools.PlotPanel import PlotPanel
10from sans.guicomm.events import NewPlotEvent, StatusEvent 
11from sans.fit.AbstractFitEngine import Model,Data,FitData1D,FitData2D
12from fitproblem import FitProblem
13from fitpanel import FitPanel
14
15import models
16import fitpage1D,fitpage2D
17import park
18
19class Plugin:
20    """
21        Fitting plugin is used to perform fit
22    """
23    def __init__(self):
24        ## Plug-in name
25        self.sub_menu = "Fitting"
26       
27        ## Reference to the parent window
28        self.parent = None
29        self.menu_mng = models.ModelManager()
30        ## List of panels for the simulation perspective (names)
31        self.perspective = []
32        # Start with a good default
33        self.elapsed = 0.022
34        self.fitter  = None
35       
36        #Flag to let the plug-in know that it is running standalone
37        self.standalone=True
38        ## Fit engine
39        self._fit_engine = 'scipy'
40        # Log startup
41        logging.info("Fitting plug-in started")   
42
43    def populate_menu(self, id, owner):
44        """
45            Create a menu for the Fitting plug-in
46            @param id: id to create a menu
47            @param owner: owner of menu
48            @ return : list of information to populate the main menu
49        """
50        #Menu for fitting
51        self.menu1 = wx.Menu()
52        id1 = wx.NewId()
53        self.menu1.Append(id1, '&Show fit panel')
54        wx.EVT_MENU(owner, id1, self.on_perspective)
55        id3 = wx.NewId()
56        self.menu1.AppendCheckItem(id3, "park") 
57        wx.EVT_MENU(owner, id3, self._onset_engine)
58       
59        #menu for model
60        menu2 = wx.Menu()
61        self.menu_mng.populate_menu(menu2, owner)
62        id2 = wx.NewId()
63        owner.Bind(models.EVT_MODEL,self._on_model_menu)
64        self.fit_panel.set_owner(owner)
65        self.fit_panel.set_model_list(self.menu_mng.get_model_list())
66        owner.Bind(fitpage1D.EVT_MODEL_BOX,self._on_model_panel)
67        owner.Bind(fitpage2D.EVT_MODEL_BOX,self._on_model_panel)
68        #create  menubar items
69        return [(id, self.menu1, "Fitting"),(id2, menu2, "Model")]
70   
71   
72    def help(self, evt):
73        """
74            Show a general help dialog.
75            TODO: replace the text with a nice image
76        """
77        pass
78   
79    def get_context_menu(self, graph=None):
80        """
81            Get the context menu items available for P(r)
82            @param graph: the Graph object to which we attach the context menu
83            @return: a list of menu items with call-back function
84        """
85        self.graph=graph
86        for item in graph.plottables:
87            if item.__class__.__name__ is "MetaData2D":
88                return [["Fit Data2D", "Dialog with fitting parameters ", self._onSelect]] 
89            else:
90                if item.name==graph.selected_plottable and (item.__class__.__name__ is  "MetaData1D"or \
91                                        item.__class__.__name__ is  "Data1D" ):
92                    return [["Fit Data1D", "Dialog with fitting parameters ", self._onSelect]] 
93        return []   
94
95
96    def get_panels(self, parent):
97        """
98            Create and return a list of panel objects
99        """
100        self.parent = parent
101        # Creation of the fit panel
102        self.fit_panel = FitPanel(self.parent, -1)
103        #Set the manager forthe main panel
104        self.fit_panel.set_manager(self)
105        # List of windows used for the perspective
106        self.perspective = []
107        self.perspective.append(self.fit_panel.window_name)
108        # take care of saving  data, model and page associated with each other
109        self.page_finder = {}
110        #index number to create random model name
111        self.index_model = 0
112        #create the fitting panel
113        return [self.fit_panel]
114   
115     
116    def get_perspective(self):
117        """
118            Get the list of panel names for this perspective
119        """
120        return self.perspective
121   
122   
123    def on_perspective(self, event):
124        """
125            Call back function for the perspective menu item.
126            We notify the parent window that the perspective
127            has changed.
128        """
129        self.parent.set_perspective(self.perspective)
130   
131   
132    def post_init(self):
133        """
134            Post initialization call back to close the loose ends
135            [Somehow openGL needs this call]
136        """
137        self.parent.set_perspective(self.perspective)
138       
139       
140    def _onSelect(self,event):
141        """
142            when Select data to fit a new page is created .Its reference is
143            added to self.page_finder
144        """
145        self.panel = event.GetEventObject()
146        for item in self.panel.graph.plottables:
147            if item.name == self.panel.graph.selected_plottable or item.__class__.__name__ is "MetaData2D":
148                #find a name for the page created for notebook
149                try:
150                    page = self.fit_panel.add_fit_page(item)
151                    # add data associated to the page created
152                   
153                    if page !=None:   
154                       
155                        #create a fitproblem storing all link to data,model,page creation
156                        self.page_finder[page]= FitProblem()
157                        self.page_finder[page].add_data(item)
158                except:
159                    wx.PostEvent(self.parent, StatusEvent(status="Creating Fit page: %s"\
160                    %sys.exc_value))
161    def schedule_for_fit(self,value=0,fitproblem =None): 
162        """
163       
164        """   
165        if fitproblem !=None:
166            fitproblem.schedule_tofit(value)
167        else:
168            current_pg=self.fit_panel.get_current_page() 
169            for page, val in self.page_finder.iteritems():
170                if page ==current_pg :
171                    val.schedule_tofit(value)
172                    break
173                     
174                   
175    def get_page_finder(self):
176        """ @return self.page_finder used also by simfitpage.py""" 
177        return self.page_finder
178   
179   
180    def set_page_finder(self,modelname,names,values):
181        """
182             Used by simfitpage.py to reset a parameter given the string constrainst.
183             @param modelname: the name ot the model for with the parameter has to reset
184             @param value: can be a string in this case.
185             @param names: the paramter name
186             @note: expecting park used for fit.
187        """ 
188        sim_page=self.fit_panel.get_page(0)
189        for page, value in self.page_finder.iteritems():
190            if page != sim_page:
191                list=value.get_model()
192                model=list[0]
193                #print "fitting",model.name,modelname
194                if model.name== modelname:
195                    value.set_model_param(names,values)
196                    break
197
198   
199                           
200    def split_string(self,item): 
201        """
202            receive a word containing dot and split it. used to split parameterset
203            name into model name and parameter name example:
204            paramaterset (item) = M1.A
205            @return model_name =M1 , parameter name =A
206        """
207        if string.find(item,".")!=-1:
208            param_names= re.split("\.",item)
209            model_name=param_names[0]
210            param_name=param_names[1] 
211            return model_name,param_name
212       
213       
214    def _single_fit_completed(self,result,pars,cpage,qmin,qmax,ymin=None, ymax=None):
215        """
216            Display fit result on one page of the notebook.
217            @param result: result of fit
218            @param pars: list of names of parameters fitted
219            @param current_pg: the page where information will be displayed
220            @param qmin: the minimum value of x to replot the model
221            @param qmax: the maximum value of x to replot model
222         
223        """
224        try:
225            for page, value in self.page_finder.iteritems():
226                if page==cpage :
227                    #fitdata = value.get_data()
228                    list = value.get_model()
229                    model= list[0]
230                    break
231            i = 0
232#            print "fitting: single fit pars ", pars
233            for name in pars:
234                if result.pvec.__class__==numpy.float64:
235                    model.setParam(name,result.pvec)
236                else:
237                    model.setParam(name,result.pvec[i])
238#                    print "fitting: single fit", name, result.pvec[i]
239                    i += 1
240#            print "fitting result : chisqr",result.fitness
241#            print "fitting result : pvec",result.pvec
242#            print "fitting result : stderr",result.stderr
243           
244            cpage.onsetValues(result.fitness, result.pvec,result.stderr)
245            self.plot_helper(currpage=cpage,qmin=qmin,qmax=qmax,ymin=ymin, ymax=ymax)
246        except:
247            raise
248            wx.PostEvent(self.parent, StatusEvent(status="Fitting error: %s" % sys.exc_value))
249           
250       
251    def _simul_fit_completed(self,result,qmin,qmax,ymin=None, ymax=None):
252        """
253            Parameter estimation completed,
254            display the results to the user
255            @param alpha: estimated best alpha
256            @param elapsed: computation time
257        """
258        try:
259            for page, value in self.page_finder.iteritems():
260                if value.get_scheduled()==1:
261                    #fitdata = value.get_data()
262                    list = value.get_model()
263                    model= list[0]
264                   
265                    small_out = []
266                    small_cov = []
267                    i = 0
268                    #Separate result in to data corresponding to each page
269                    for p in result.parameters:
270                        model_name,param_name = self.split_string(p.name) 
271                        if model.name == model_name:
272                            small_out.append(p.value )
273                            small_cov.append(p.stderr)
274                            model.setParam(param_name,p.value) 
275                    # Display result on each page
276                    page.onsetValues(result.fitness, small_out,small_cov)
277                    #Replot model
278                    self.plot_helper(currpage= page,qmin= qmin,qmax= qmax,ymin=ymin, ymax=ymax) 
279        except:
280             wx.PostEvent(self.parent, StatusEvent(status="Fitting error: %s" % sys.exc_value))
281           
282   
283    def _on_single_fit(self,id=None,qmin=None,qmax=None,ymin=None,ymax=None):
284        """
285            perform fit for the  current page  and return chisqr,out and cov
286            @param engineName: type of fit to be performed
287            @param id: unique id corresponding to a fit problem(model, set of data)
288            @param model: model to fit
289           
290        """
291        #print "in single fitting"
292        #set an engine to perform fit
293        from sans.fit.Fitting import Fit
294        self.fitter= Fit(self._fit_engine)
295        #Setting an id to store model and data in fit engine
296        if id==None:
297            id=0
298        self.id = id
299        page_fitted=None
300        fit_problem=None
301        #Get information (model , data) related to the page on
302        #with the fit will be perform
303        #current_pg=self.fit_panel.get_current_page()
304        #simul_pg=self.fit_panel.get_page(0)
305           
306        for page, value in self.page_finder.iteritems():
307            if  value.get_scheduled() ==1 :
308                metadata = value.get_data()
309                list=value.get_model()
310                model=list[0]
311                #Create list of parameters for fitting used
312                pars=[]
313                templist=[]
314                try:
315                    #templist=current_pg.get_param_list()
316                    templist=page.get_param_list()
317                    for element in templist:
318                        pars.append(str(element[0].GetLabelText()))
319                    pars.sort()
320                    #Do the single fit
321                    self.fitter.set_model(Model(model), self.id, pars) 
322                    self.fitter.set_data(metadata,self.id,qmin,qmax)
323                    self.fitter.select_problem_for_fit(Uid=self.id,value=value.get_scheduled())
324                    page_fitted=page
325                    self.id+=1
326                    self.schedule_for_fit( 0,value) 
327                except:
328                    wx.PostEvent(self.parent, StatusEvent(status="Fitting error: %s" % sys.exc_value))
329                    return
330                # make sure to keep an alphabetic order
331                #of parameter names in the list     
332        try:
333            result=self.fitter.fit()
334            #self._single_fit_completed(result,pars,current_pg,qmin,qmax)
335            print "single_fit: result",result.fitness,result.pvec,result.stderr
336            #self._single_fit_completed(result,pars,page,qmin,qmax)
337            self._single_fit_completed(result,pars,page_fitted,qmin,qmax,ymin,ymax)
338        except:
339            raise
340            wx.PostEvent(self.parent, StatusEvent(status="Single Fit error: %s" % sys.exc_value))
341            return
342         
343    def _on_simul_fit(self, id=None,qmin=None,qmax=None, ymin=None, ymax=None):
344        """
345            perform fit for all the pages selected on simpage and return chisqr,out and cov
346            @param engineName: type of fit to be performed
347            @param id: unique id corresponding to a fit problem(model, set of data)
348             in park_integration
349            @param model: model to fit
350           
351        """
352        #set an engine to perform fit
353        from sans.fit.Fitting import Fit
354        self.fitter= Fit(self._fit_engine)
355       
356        #Setting an id to store model and data
357        if id==None:
358             id = 0
359        self.id = id
360       
361        for page, value in self.page_finder.iteritems():
362            try:
363                if value.get_scheduled()==1:
364                    metadata = value.get_data()
365                    list = value.get_model()
366                    model= list[0]
367                    #Create dictionary of parameters for fitting used
368                    pars = []
369                    templist = []
370                    templist = page.get_param_list()
371                    for element in templist:
372                        try:
373                            name = str(element[0].GetLabelText())
374                            pars.append(name)
375                        except:
376                            wx.PostEvent(self.parent, StatusEvent(status="Fitting error: %s" % sys.exc_value))
377                            return
378                    new_model=Model(model)
379                    param=value.get_model_param()
380                   
381                    if len(param)>0:
382                        for item in param:
383                            param_value = item[1]
384                            param_name = item[0]
385                            #print "fitting ", param,param_name, param_value
386                           
387                            #new_model.set( model.getParam(param_name[0])= param_value)
388                            #new_model.set( exec"%s=%s"%(param_name[0], param_value))
389                            #new_model.set( exec "%s"%(param_nam) = param_value)
390                            new_model.parameterset[ param_name].set( param_value )
391                           
392                    self.fitter.set_model(new_model, self.id, pars) 
393                    self.fitter.set_data(metadata,self.id,qmin,qmax,ymin,ymax)
394                    self.fitter.select_problem_for_fit(Uid=self.id,value=value.get_scheduled())
395                    self.id += 1 
396            except:
397                wx.PostEvent(self.parent, StatusEvent(status="Fitting error: %s" % sys.exc_value))
398                return 
399        #Do the simultaneous fit
400        try:
401            result=self.fitter.fit()
402            self._simul_fit_completed(result,qmin,qmax,ymin,ymax)
403        except:
404            wx.PostEvent(self.parent, StatusEvent(status="Simultaneous Fitting error: %s" % sys.exc_value))
405            return
406       
407       
408    def _onset_engine(self,event):
409        """ set engine to scipy"""
410        if self._fit_engine== 'park':
411            self._on_change_engine('scipy')
412        else:
413            self._on_change_engine('park')
414        wx.PostEvent(self.parent, StatusEvent(status="Engine set to: %s" % self._fit_engine))
415 
416   
417    def _on_change_engine(self, engine='park'):
418        """
419            Allow to select the type of engine to perform fit
420            @param engine: the key work of the engine
421        """
422        self._fit_engine = engine
423   
424   
425    def _on_model_panel(self, evt):
426        """
427            react to model selection on any combo box or model menu.plot the model 
428        """
429       
430        model = evt.model
431        name = evt.name
432        sim_page=self.fit_panel.get_page(0)
433        current_pg = self.fit_panel.get_current_page() 
434        if current_pg != sim_page:
435            current_pg.set_panel(model)
436           
437            try:
438                metadata=self.page_finder[current_pg].get_data()
439                M_name="M"+str(self.index_model)+"= "+name+"("+metadata.group_id+")"
440            except:
441                M_name="M"+str(self.index_model)+"= "+name
442            model.name="M"+str(self.index_model)
443            self.index_model += 1 
444           
445            self.page_finder[current_pg].set_model(model,M_name)
446            self.plot_helper(currpage= current_pg,qmin= None,qmax= None)
447            sim_page.add_model(self.page_finder)
448       
449           
450    def redraw_model(self,qmin= None,qmax= None):
451        """
452            Draw a theory according to model changes or data range.
453            @param qmin: the minimum value plotted for theory
454            @param qmax: the maximum value plotted for theory
455        """
456        current_pg=self.fit_panel.get_current_page()
457        for page, value in self.page_finder.iteritems():
458            if page ==current_pg :
459                break 
460        self.plot_helper(currpage=page,qmin= qmin,qmax= qmax)
461       
462    def plot_helper(self,currpage,qmin=None,qmax=None,ymin=None,ymax=None):
463        """
464            Plot a theory given a model and data
465            @param model: the model from where the theory is derived
466            @param currpage: page in a dictionary referring to some data
467        """
468        if self.fit_panel.get_page_count() >1:
469            for page in self.page_finder.iterkeys():
470                if  page==currpage : 
471                    data=self.page_finder[page].get_data()
472                    list=self.page_finder[page].get_model()
473                    model=list[0]
474                    break 
475           
476            if data!=None and data.__class__.__name__ != 'MetaData2D':
477                theory = Theory1D(x=[], y=[])
478                theory.name = "Model"
479                theory.group_id = data.group_id
480             
481                x_name, x_units = data.get_xaxis() 
482                y_name, y_units = data.get_yaxis() 
483                theory.xaxis(x_name, x_units)
484                theory.yaxis(y_name, y_units)
485                if qmin == None :
486                   qmin = min(data.x)
487                if qmax == None :
488                    qmax = max(data.x)
489                try:
490                    tempx = qmin
491                    tempy = model.run(qmin)
492                    theory.x.append(tempx)
493                    theory.y.append(tempy)
494                except :
495                        wx.PostEvent(self.parent, StatusEvent(status="fitting \
496                        skipping point x %g %s" %(qmin, sys.exc_value)))
497                           
498                for i in range(len(data.x)):
499                    try:
500                        if data.x[i]> qmin and data.x[i]< qmax:
501                            tempx = data.x[i]
502                            tempy = model.run(tempx)
503                            theory.x.append(tempx) 
504                            theory.y.append(tempy)
505                    except:
506                        wx.PostEvent(self.parent, StatusEvent(status="fitting \
507                        skipping point x %g %s" %(data.x[i], sys.exc_value)))   
508                try:
509                    tempx = qmax
510                    tempy = model.run(qmax)
511                    theory.x.append(tempx)
512                    theory.y.append(tempy)
513                   
514                except:
515                    wx.PostEvent(self.parent, StatusEvent(status="fitting \
516                        skipping point x %g %s" %(qmax, sys.exc_value)))
517            else:
518                theory=Data2D(data.image, data.err_image)
519                theory.x_bins= data.x_bins
520                theory.y_bins= data.y_bins
521                tempy=[]
522                if qmin==None:
523                    qmin=data.xmin
524                if qmax==None:
525                    qmin=data.xmax
526                if ymin==None:
527                    ymin=data.ymin
528                if ymax==None:
529                    qmin=data.ymax
530             
531                for i in range(len(data.y_bins)):
532                    if data.y_bins[i]>= ymin and data.y_bins[i]<= ymax:
533                        for j in range(len(data.x_bins)):
534                            if data.x_bins[i]>= xmin and data.x_bins[i]<= xmax:
535                                theory.image= model.runXY([data.x_bins[j],data.y_bins[i]])
536               
537                   
538                    #print "fitting : plot_helper:", theory.image
539                #print data.image
540                #theory.image=model.runXY(data.image)
541               
542                #print "fitting : plot_helper:",theory.image
543                theory.zmin= data.zmin
544                theory.zmax= data.zmax
545                theory.xmin= data.xmin
546                theory.xmax= data.xmax
547                theory.ymin= data.ymin
548                theory.ymax= data.ymax
549               
550        wx.PostEvent(self.parent, NewPlotEvent(plot=theory, title="Analytical model"))
551       
552       
553    def _on_model_menu(self, evt):
554        """
555            Plot a theory from a model selected from the menu
556        """
557        name="Model View"
558        model=evt.modelinfo.model()
559        description=evt.modelinfo.description
560        self.fit_panel.add_model_page(model,description,name)       
561        self.draw_model(model)
562       
563    def draw_model(self,model):
564        """
565             draw model with default data value
566        """
567        x = pylab.arange(0.001, 0.1, 0.001)
568        xlen = len(x)
569        dy = numpy.zeros(xlen)
570        y = numpy.zeros(xlen)
571       
572        for i in range(xlen):
573            y[i] = model.run(x[i])
574            dy[i] = math.sqrt(math.fabs(y[i]))
575        try:
576           
577            new_plot = Theory1D(x, y)
578            new_plot.name = "Model"
579            new_plot.xaxis("\\rm{Q}", 'A^{-1}')
580            new_plot.yaxis("\\rm{Intensity} ","cm^{-1}")
581            new_plot.group_id ="Fitness"
582            wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title="Analytical model"))
583        except:
584            wx.PostEvent(self.parent, StatusEvent(status="fitting \
585                        skipping point x %g %s" %(qmax, sys.exc_value)))
586
587if __name__ == "__main__":
588    i = Plugin()
589   
590   
591   
592   
Note: See TracBrowser for help on using the repository browser.