source: sasview/sansview/perspectives/fitting/fitting.py @ 3b19ac9

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

working on simultaneous fit and scipy/park fit added on menubar

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