source: sasview/sansview/perspectives/fitting/fitting.py @ 4c530a5

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 4c530a5 was 4c530a5, checked in by Mathieu Doucet <doucetm@…>, 16 years ago

Added proper group id to theory curves

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