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

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

change window title

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