source: sasview/sansview/perspectives/fitting/fitting.py @ fa9674b

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

constraint fitting is not working yet

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