source: sasview/sansview/perspectives/fitting/fitting.py @ 2d107b8

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

fix small bugs

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