source: sasview/sansview/perspectives/fitting/fitting.py @ 49815a2

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

still working on 2d panel

  • Property mode set to 100644
File size: 23.0 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):
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)
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):
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) 
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):
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                fitdata = 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(fitdata,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)
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):
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                    fitdata = 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(fitdata,self.id,qmin,qmax)
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)
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_model_name(name)
436            current_pg.set_panel(model)
437           
438            try:
439                metadata=self.page_finder[current_pg].get_data()
440                M_name="M"+str(self.index_model)+"= "+name+"("+metadata.group_id+")"
441            except:
442                M_name="M"+str(self.index_model)+"= "+name
443            model.name="M"+str(self.index_model)
444            self.index_model += 1 
445           
446            self.page_finder[current_pg].set_model(model,M_name)
447            self.plot_helper(currpage= current_pg,qmin= None,qmax= None)
448            sim_page.add_model(self.page_finder)
449       
450           
451    def redraw_model(self,qmin= None,qmax= None):
452        """
453            Draw a theory according to model changes or data range.
454            @param qmin: the minimum value plotted for theory
455            @param qmax: the maximum value plotted for theory
456        """
457        current_pg=self.fit_panel.get_current_page()
458        for page, value in self.page_finder.iteritems():
459            if page ==current_pg :
460                break 
461        self.plot_helper(currpage=page,qmin= qmin,qmax= qmax)
462       
463    def plot_helper(self,currpage,qmin=None,qmax=None):
464        """
465            Plot a theory given a model and data
466            @param model: the model from where the theory is derived
467            @param currpage: page in a dictionary referring to some data
468        """
469        if self.fit_panel.get_page_count() >1:
470            for page in self.page_finder.iterkeys():
471                if  page==currpage : 
472                    data=self.page_finder[page].get_data()
473                    list=self.page_finder[page].get_model()
474                    model=list[0]
475                    break 
476           
477            if data!=None and data.__class__.__name__ != 'MetaData2D':
478                theory = Theory1D(x=[], y=[])
479                theory.name = "Model"
480                theory.group_id = data.group_id
481             
482                x_name, x_units = data.get_xaxis() 
483                y_name, y_units = data.get_yaxis() 
484                theory.xaxis(x_name, x_units)
485                theory.yaxis(y_name, y_units)
486                if qmin == None :
487                   qmin = min(data.x)
488                if qmax == None :
489                    qmax = max(data.x)
490                try:
491                    tempx = qmin
492                    tempy = model.run(qmin)
493                    theory.x.append(tempx)
494                    theory.y.append(tempy)
495                except :
496                        wx.PostEvent(self.parent, StatusEvent(status="fitting \
497                        skipping point x %g %s" %(qmin, sys.exc_value)))
498                           
499                for i in range(len(data.x)):
500                    try:
501                        if data.x[i]> qmin and data.x[i]< qmax:
502                            tempx = data.x[i]
503                            tempy = model.run(tempx)
504                            theory.x.append(tempx) 
505                            theory.y.append(tempy)
506                    except:
507                        wx.PostEvent(self.parent, StatusEvent(status="fitting \
508                        skipping point x %g %s" %(data.x[i], sys.exc_value)))   
509                try:
510                    tempx = qmax
511                    tempy = model.run(qmax)
512                    theory.x.append(tempx)
513                    theory.y.append(tempy)
514                   
515                except:
516                    wx.PostEvent(self.parent, StatusEvent(status="fitting \
517                        skipping point x %g %s" %(qmax, sys.exc_value)))
518            else:
519                theory=Data2D(data.image, data.err_image)
520                theory.x_bins= data.x_bins
521                theory.y_bins= data.y_bins
522                tempy=[]
523                for i in range(len(data.x_bins)):
524                    theory.image= model.runXY([data.x_bins[i],data.y_bins[i]])
525                    #print "fitting : plot_helper:", theory.image
526                #print data.image
527                #theory.image=model.runXY(data.image)
528                theory.image=model.run(data.image)
529                print "fitting : plot_helper:",theory.image
530                theory.zmin= data.zmin
531                theory.zmax= data.zmax
532                theory.xmin= data.xmin
533                theory.xmax= data.xmax
534                theory.ymin= data.ymin
535                theory.ymax= data.ymax
536               
537        wx.PostEvent(self.parent, NewPlotEvent(plot=theory, title="Analytical model"))
538       
539       
540    def _on_model_menu(self, evt):
541        """
542            Plot a theory from a model selected from the menu
543        """
544        name="Model View"
545        model=evt.modelinfo.model()
546        description=evt.modelinfo.description
547        self.fit_panel.add_model_page(model,description,name)       
548        self.draw_model(model)
549       
550    def draw_model(self,model):
551        """
552             draw model with default data value
553        """
554        x = pylab.arange(0.001, 0.1, 0.001)
555        xlen = len(x)
556        dy = numpy.zeros(xlen)
557        y = numpy.zeros(xlen)
558       
559        for i in range(xlen):
560            y[i] = model.run(x[i])
561            dy[i] = math.sqrt(math.fabs(y[i]))
562        try:
563           
564            new_plot = Theory1D(x, y)
565            new_plot.name = "Model"
566            new_plot.xaxis("\\rm{Q}", 'A^{-1}')
567            new_plot.yaxis("\\rm{Intensity} ","cm^{-1}")
568            new_plot.group_id ="Fitness"
569            wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title="Analytical model"))
570        except:
571            wx.PostEvent(self.parent, StatusEvent(status="fitting \
572                        skipping point x %g %s" %(qmax, sys.exc_value)))
573
574if __name__ == "__main__":
575    i = Plugin()
576   
577   
578   
579   
Note: See TracBrowser for help on using the repository browser.