source: sasview/sansview/perspectives/fitting/fitting.py @ 57668f8

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

working on sector averaging

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