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

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

modified constraint button

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