source: sasview/sansview/perspectives/fitting/fitting.py @ 55e13ab

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

smearing added

  • Property mode set to 100644
File size: 24.2 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                    print "Single fit : on smearing",metadata.smearer
335                    self.fitter.set_data(metadata,self.id,qmin,qmax)
336                    self.fitter.select_problem_for_fit(Uid=self.id,value=value.get_scheduled())
337                    page_fitted=page
338                    self.id+=1
339                    self.schedule_for_fit( 0,value) 
340                except:
341                    wx.PostEvent(self.parent, StatusEvent(status="Fitting error: %s" % sys.exc_value))
342                    return
343                # make sure to keep an alphabetic order
344                #of parameter names in the list     
345        try:
346            result=self.fitter.fit()
347            #self._single_fit_completed(result,pars,current_pg,qmin,qmax)
348            print "single_fit: result",result.fitness,result.pvec,result.stderr
349            #self._single_fit_completed(result,pars,page,qmin,qmax)
350            self._single_fit_completed(result,pars,page_fitted,qmin,qmax,ymin,ymax)
351        except:
352            raise
353            wx.PostEvent(self.parent, StatusEvent(status="Single Fit error: %s" % sys.exc_value))
354            return
355         
356    def _on_simul_fit(self, id=None,qmin=None,qmax=None, ymin=None, ymax=None):
357        """
358            perform fit for all the pages selected on simpage and return chisqr,out and cov
359            @param engineName: type of fit to be performed
360            @param id: unique id corresponding to a fit problem(model, set of data)
361             in park_integration
362            @param model: model to fit
363           
364        """
365        #set an engine to perform fit
366        from sans.fit.Fitting import Fit
367        self.fitter= Fit(self._fit_engine)
368       
369        #Setting an id to store model and data
370        if id==None:
371             id = 0
372        self.id = id
373       
374        for page, value in self.page_finder.iteritems():
375            try:
376                if value.get_scheduled()==1:
377                    metadata = value.get_data()
378                    list = value.get_model()
379                    model= list[0]
380                    #Create dictionary of parameters for fitting used
381                    pars = []
382                    templist = []
383                    templist = page.get_param_list()
384                    for element in templist:
385                        try:
386                            name = str(element[0].GetLabelText())
387                            pars.append(name)
388                        except:
389                            wx.PostEvent(self.parent, StatusEvent(status="Fitting error: %s" % sys.exc_value))
390                            return
391                    new_model=Model(model)
392                    param=value.get_model_param()
393                   
394                    if len(param)>0:
395                        for item in param:
396                            param_value = item[1]
397                            param_name = item[0]
398                            #print "fitting ", param,param_name, param_value
399                           
400                            #new_model.set( model.getParam(param_name[0])= param_value)
401                            #new_model.set( exec"%s=%s"%(param_name[0], param_value))
402                            #new_model.set( exec "%s"%(param_nam) = param_value)
403                            new_model.parameterset[ param_name].set( param_value )
404                           
405                    self.fitter.set_model(new_model, self.id, pars) 
406                    self.fitter.set_data(metadata,self.id,qmin,qmax,ymin,ymax)
407                    self.fitter.select_problem_for_fit(Uid=self.id,value=value.get_scheduled())
408                    self.id += 1 
409            except:
410                wx.PostEvent(self.parent, StatusEvent(status="Fitting error: %s" % sys.exc_value))
411                return 
412        #Do the simultaneous fit
413        try:
414            result=self.fitter.fit()
415            self._simul_fit_completed(result,qmin,qmax,ymin,ymax)
416        except:
417            wx.PostEvent(self.parent, StatusEvent(status="Simultaneous Fitting error: %s" % sys.exc_value))
418            return
419       
420       
421    def _onset_engine(self,event):
422        """ set engine to scipy"""
423        if self._fit_engine== 'park':
424            self._on_change_engine('scipy')
425        else:
426            self._on_change_engine('park')
427        wx.PostEvent(self.parent, StatusEvent(status="Engine set to: %s" % self._fit_engine))
428 
429   
430    def _on_change_engine(self, engine='park'):
431        """
432            Allow to select the type of engine to perform fit
433            @param engine: the key work of the engine
434        """
435        self._fit_engine = engine
436   
437   
438    def _on_model_panel(self, evt):
439        """
440            react to model selection on any combo box or model menu.plot the model 
441        """
442       
443        model = evt.model
444        name = evt.name
445        sim_page=self.fit_panel.get_page(0)
446        current_pg = self.fit_panel.get_current_page() 
447        if current_pg != sim_page:
448            current_pg.set_panel(model)
449           
450            try:
451                metadata=self.page_finder[current_pg].get_data()
452                M_name="M"+str(self.index_model)+"= "+name+"("+metadata.group_id+")"
453            except:
454                M_name="M"+str(self.index_model)+"= "+name
455            model.name="M"+str(self.index_model)
456            self.index_model += 1 
457           
458            self.page_finder[current_pg].set_model(model,M_name)
459            self.plot_helper(currpage= current_pg,qmin= None,qmax= None)
460            sim_page.add_model(self.page_finder)
461       
462           
463    def redraw_model(self,qmin= None,qmax= None):
464        """
465            Draw a theory according to model changes or data range.
466            @param qmin: the minimum value plotted for theory
467            @param qmax: the maximum value plotted for theory
468        """
469        current_pg=self.fit_panel.get_current_page()
470        for page, value in self.page_finder.iteritems():
471            if page ==current_pg :
472                break 
473        self.plot_helper(currpage=page,qmin= qmin,qmax= qmax)
474       
475    def plot_helper(self,currpage,qmin=None,qmax=None,ymin=None,ymax=None):
476        """
477            Plot a theory given a model and data
478            @param model: the model from where the theory is derived
479            @param currpage: page in a dictionary referring to some data
480        """
481        if self.fit_panel.get_page_count() >1:
482            for page in self.page_finder.iterkeys():
483                if  page==currpage : 
484                    data=self.page_finder[page].get_data()
485                    list=self.page_finder[page].get_model()
486                    model=list[0]
487                    break 
488           
489            if data!=None and data.__class__.__name__ != 'Data2D':
490                theory = Theory1D(x=[], y=[])
491                theory.name = "Model"
492                theory.group_id = data.group_id
493             
494                x_name, x_units = data.get_xaxis() 
495                y_name, y_units = data.get_yaxis() 
496                theory.xaxis(x_name, x_units)
497                theory.yaxis(y_name, y_units)
498                if qmin == None :
499                   qmin = min(data.x)
500                if qmax == None :
501                    qmax = max(data.x)
502                try:
503                    tempx = qmin
504                    tempy = model.run(qmin)
505                    theory.x.append(tempx)
506                    theory.y.append(tempy)
507                except :
508                        wx.PostEvent(self.parent, StatusEvent(status="fitting \
509                        skipping point x %g %s" %(qmin, sys.exc_value)))
510                           
511                for i in range(len(data.x)):
512                    try:
513                        if data.x[i]> qmin and data.x[i]< qmax:
514                            tempx = data.x[i]
515                            tempy = model.run(tempx)
516                            theory.x.append(tempx) 
517                            theory.y.append(tempy)
518                           
519                    except:
520                        wx.PostEvent(self.parent, StatusEvent(status="fitting \
521                        skipping point x %g %s" %(data.x[i], sys.exc_value)))   
522                try:
523                    tempx = qmax
524                    tempy = model.run(qmax)
525                    theory.x.append(tempx)
526                    theory.y.append(tempy)
527                except:
528                    wx.PostEvent(self.parent, StatusEvent(status="fitting \
529                        skipping point x %g %s" %(qmax, sys.exc_value)))
530               
531            else:
532                theory=Theory2D(data.data, data.err_data)
533                #theory=Theory2D(data.image, data.err_image)
534                theory.x_bins= data.x_bins
535                theory.y_bins= data.y_bins
536                tempy=[]
537                if qmin==None:
538                    qmin=data.xmin
539                if qmax==None:
540                    qmax=data.xmax
541                if ymin==None:
542                    ymin=data.ymin
543                if ymax==None:
544                    ymax=data.ymax
545                   
546                theory.data = numpy.zeros((len(data.y_bins),len(data.x_bins)))
547                for i in range(len(data.y_bins)):
548                    if data.y_bins[i]>= ymin and data.y_bins[i]<= ymax:
549                        for j in range(len(data.x_bins)):
550                            if data.x_bins[i]>= qmin and data.x_bins[i]<= qmax:
551                                theory.data[j][i]=model.runXY([data.x_bins[j],data.y_bins[i]])
552               
553                #print "fitting : plot_helper:", theory.image
554                #print data.image
555                #print "fitting : plot_helper:",theory.image
556                theory.detector= data.detector
557                theory.source= data.source
558                theory.zmin= data.zmin
559                theory.zmax= data.zmax
560                theory.xmin= qmin
561                theory.xmax= qmax
562                theory.ymin= ymin
563                theory.ymax= ymax
564       
565        wx.PostEvent(self.parent, NewPlotEvent(plot=theory, title="Analytical model"))
566       
567       
568    def _on_model_menu(self, evt):
569        """
570            Plot a theory from a model selected from the menu
571        """
572        name="Model View"
573        model=evt.model()
574       
575        description=model.description
576        self.fit_panel.add_model_page(model,description,name)       
577        self.draw_model(model)
578       
579    def draw_model(self,model):
580        """
581             draw model with default data value
582        """
583        x = numpy.arange(0.001, 0.1, 0.001)
584        xlen = len(x)
585        y = numpy.zeros(xlen)
586       
587        for i in range(xlen):
588            y[i] = model.run(x[i])
589
590        try:
591            new_plot = Theory1D(x, y)
592            new_plot.name = "Model"
593            new_plot.xaxis("\\rm{Q}", 'A^{-1}')
594            new_plot.yaxis("\\rm{Intensity} ","cm^{-1}")
595             
596            new_plot.group_id ="Fitness"
597            wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title="Analytical model"))
598        except:
599            raise
600if __name__ == "__main__":
601    i = Plugin()
602   
603   
604   
605   
Note: See TracBrowser for help on using the repository browser.