source: sasview/sansview/perspectives/fitting/fitting.py @ 8d78399

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 8d78399 was 6ced1cc, checked in by Jae Cho <jhjcho@…>, 15 years ago

changed the default qmin 0.001. Former value 0.0001 made poor plot since there was not many points near the value

  • Property mode set to 100644
File size: 48.7 KB
Line 
1"""
2This software was developed by the University of Tennessee as part of the
3Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
4project funded by the US National Science Foundation.
5
6See the license text in license.txt
7
8copyright 2009, University of Tennessee
9"""
10import  re,copy
11import sys, wx, logging
12import string, numpy, math
13import time
14import thread
15#from danse.common.plottools.plottables import Theory1D
16from danse.common.plottools.PlotPanel import PlotPanel
17
18from sans.guiframe.dataFitting import Data2D
19from sans.guiframe.dataFitting import Data1D
20from sans.guiframe.dataFitting import Theory1D
21from sans.guiframe import dataFitting
22
23from sans.guicomm.events import NewPlotEvent, StatusEvent 
24from sans.guicomm.events import EVT_SLICER_PANEL,ERR_DATA,EVT_REMOVE_DATA
25from sans.guicomm.events import EVT_SLICER_PARS_UPDATE
26
27from sans.fit.AbstractFitEngine import Model
28from sans.fit.AbstractFitEngine import FitAbort
29
30
31from fitproblem import FitProblem
32from fitpanel import FitPanel
33from fit_thread import FitThread
34import models
35import fitpage
36
37DEFAULT_BEAM = 0.005
38DEFAULT_QMIN = 0.001
39DEFAULT_QMAX = 0.13
40DEFAULT_NPTS = 50
41
42(PageInfoEvent, EVT_PAGE_INFO)   = wx.lib.newevent.NewEvent()
43
44
45class PlotInfo:
46    """
47        store some plotting field
48    """
49    _xunit = 'A^{-1}'
50    _xaxis= "\\rm{Q}"
51    _yunit = "cm^{-1}"
52    _yaxis= "\\rm{Intensity} "
53    id = "Model"
54    group_id = "Model"
55    title= None
56    info= None
57   
58   
59class Plugin:
60    """
61        Fitting plugin is used to perform fit
62    """
63    def __init__(self):
64        ## Plug-in name
65        self.sub_menu = "Fitting"
66       
67        ## Reference to the parent window
68        self.parent = None
69        #Provide list of models existing in the application
70        self.menu_mng = models.ModelManager()
71        ## List of panels for the simulation perspective (names)
72        self.perspective = []
73        #list of panel to send to guiframe
74        self.mypanels=[]
75        # reference to the current running thread
76        self.calc_2D= None
77        self.calc_1D= None
78        self.calc_fit= None
79       
80        # Start with a good default
81        self.elapsed = 0.022
82        # the type of optimizer selected, park or scipy
83        self.fitter  = None
84        #Flag to let the plug-in know that it is running stand alone
85        self.standalone=True
86        ## dictionary of page closed and id
87        self.closed_page_dict ={}
88        ## Fit engine
89        self._fit_engine = 'scipy'
90        #List of selected data
91        self.selected_data_list=[]
92        ## list of slicer panel created to display slicer parameters and results
93        self.slicer_panels=[]
94        # Log startup
95        logging.info("Fitting plug-in started")   
96        # model 2D view
97        self.model2D_id=None
98        #keep reference of the simultaneous fit page
99        self.sim_page=None
100        #dictionary containing data name and error on dy of that data
101        self.err_dy={}
102       
103   
104       
105    def populate_menu(self, id, owner):
106        """
107            Create a menu for the Fitting plug-in
108            @param id: id to create a menu
109            @param owner: owner of menu
110            @ return : list of information to populate the main menu
111        """
112        #Menu for fitting
113        self.menu1 = wx.Menu()
114       
115        #Set park engine
116        id3 = wx.NewId()
117        scipy_help= "Scipy Engine: Perform Simple fit. More in Help window...."
118        self.menu1.AppendCheckItem(id3, "Simple Fit  [Scipy]",scipy_help) 
119        wx.EVT_MENU(owner, id3,  self._onset_engine_scipy)
120       
121        id3 = wx.NewId()
122        park_help = "Park Engine: Perform Complex fit. More in Help window...."
123        self.menu1.AppendCheckItem(id3, "Complex Fit  [Park]",park_help) 
124        wx.EVT_MENU(owner, id3,  self._onset_engine_park)
125       
126        self.menu1.FindItemByPosition(0).Check(True)
127        self.menu1.FindItemByPosition(1).Check(False)
128           
129        self.menu1.AppendSeparator()
130        id1 = wx.NewId()
131        simul_help = "Allow to edit fit engine with multiple model and data"
132        self.menu1.Append(id1, '&Simultaneous Page',simul_help)
133        wx.EVT_MENU(owner, id1, self.on_add_sim_page)
134        #menu for SLD Calculator
135        #self.tool_menu = wx.Menu()
136        #id_tool_menu = wx.NewId()
137        #sld_id = wx.NewId()
138        #sld_help= "Compute the scattering length density of molecules"
139        #self.tool_menu.Append(sld_id, "SLD Calculator",sld_help)
140        #wx.EVT_MENU(owner,sld_id,  self.onCalculateSld)
141   
142        #menu for model
143        menu2 = wx.Menu()
144        self.menu_mng.populate_menu(menu2, owner)
145        id2 = wx.NewId()
146        owner.Bind(models.EVT_MODEL,self._on_model_menu)
147     
148        self.fit_panel.set_owner(owner)
149        self.fit_panel.set_model_list(self.menu_mng.get_model_list())
150        owner.Bind(fitpage.EVT_MODEL_BOX,self._on_model_panel)
151     
152        #create  menubar items
153        return [(id, self.menu1, "Fitting"),
154                (id2, menu2, "Model")]
155       
156   
157    def on_add_sim_page(self, event):
158        """
159            Create a page to access simultaneous fit option
160        """
161        Plugin.on_perspective(self,event=event)
162        if self.sim_page !=None:
163            msg= "Simultaneous Fit page already opened"
164            wx.PostEvent(self.parent, StatusEvent(status= msg))
165            return 
166       
167        self.sim_page= self.fit_panel.add_sim_page()
168       
169       
170       
171    def help(self, evt):
172        """
173            Show a general help dialog.
174            TODO: replace the text with a nice image
175        """
176        from helpPanel import  HelpWindow
177        frame = HelpWindow(None, -1, 'HelpWindow')   
178        frame.Show(True)
179       
180       
181    def get_context_menu(self, graph=None):
182        """
183            Get the context menu items available for P(r)
184            @param graph: the Graph object to which we attach the context menu
185            @return: a list of menu items with call-back function
186        """
187        #TODO: clean this up so that the string are not copied
188        #      multiple times.
189        self.graph=graph
190       
191        for item in graph.plottables:
192            if item.__class__.__name__ is "Data2D":
193               
194                if hasattr(item,"is_data"):
195                    if item.is_data:
196                        return [["Select data for fitting", \
197                         "Dialog with fitting parameters ", self._onSelect]]
198                    else:
199                        return [] 
200                return [["Select data for fitting",\
201                          "Dialog with fitting parameters ", self._onSelect]] 
202            else:
203                if item.name==graph.selected_plottable :
204                    if item.name !="$I_{obs}(q)$" and item.name !="$P_{fit}(r)$":
205                        if hasattr(item, "group_id"):
206                            if hasattr(item,"is_data"):
207                                if item.is_data:
208                                    return [["Select data for fitting", \
209                                     "Dialog with fitting parameters ", self._onSelect]]
210                                else:
211                                    return [] 
212                            else:
213                                return [["Select data for fitting", \
214                                     "Dialog with fitting parameters ", self._onSelect]] 
215        return []   
216
217
218    def get_panels(self, parent):
219        """
220            Create and return a list of panel objects
221        """
222        self.parent = parent
223        # Creation of the fit panel
224        self.fit_panel = FitPanel(self.parent, -1)
225        #Set the manager for the main panel
226        self.fit_panel.set_manager(self)
227        # List of windows used for the perspective
228        self.perspective = []
229        self.perspective.append(self.fit_panel.window_name)
230        # take care of saving  data, model and page associated with each other
231        self.page_finder = {}
232        #index number to create random model name
233        self.index_model = 0
234        self.index_theory= 0
235        self.parent.Bind(EVT_SLICER_PANEL, self._on_slicer_event)
236        self.parent.Bind( ERR_DATA, self._on_data_error)
237        self.parent.Bind(EVT_REMOVE_DATA, self._closed_fitpage)
238        self.parent.Bind(EVT_SLICER_PARS_UPDATE, self._onEVT_SLICER_PANEL)
239        self.parent._mgr.Bind(wx.aui.EVT_AUI_PANE_CLOSE,self._onclearslicer)   
240
241       
242        #Send the fitting panel to guiframe
243        self.mypanels.append(self.fit_panel)
244        return self.mypanels
245
246       
247     
248    def get_perspective(self):
249        """
250            Get the list of panel names for this perspective
251        """
252        return self.perspective
253   
254   
255    def on_perspective(self, event):
256        """
257            Call back function for the perspective menu item.
258            We notify the parent window that the perspective
259            has changed.
260        """
261        self.parent.set_perspective(self.perspective)
262   
263   
264    def post_init(self):
265        """
266            Post initialization call back to close the loose ends
267        """
268        # Do not show the fitting perspective immediately and
269        # let the application show the Welcome Page.
270        #self.parent.set_perspective(self.perspective)
271        pass
272
273
274    def copy_data(self, item, dy=None):
275        """
276            receive a data 1D and the list of errors on dy
277            and create a new data1D data
278            @param return
279        """
280     
281        id=None
282       
283        if hasattr(item,"id"):
284            id = copy.deepcopy(item.id)
285       
286       
287        data= Data1D(x=item.x, y=item.y,dx=None, dy=None)
288        data.copy_from_datainfo(item) 
289        item.clone_without_data(clone=data)   
290        data.dy=dy
291        data.name = item.name
292       
293        ## allow to highlight data when plotted
294        data.interactive = copy.deepcopy(item.interactive)
295        ## when 2 data have the same id override the 1 st plotted
296        data.id = id
297       
298        data.group_id = item.group_id
299        return data
300   
301    def set_fit_range(self, page, qmin, qmax):
302        """
303            Set the fitting range of a given page
304        """
305        if page in self.page_finder.iterkeys():
306            fitproblem= self.page_finder[page]
307            fitproblem.set_range(qmin= qmin, qmax= qmax)
308                   
309    def schedule_for_fit(self,value=0,page=None,fitproblem =None): 
310        """
311            Set the fit problem field to 0 or 1 to schedule that problem to fit.
312            Schedule the specified fitproblem or get the fit problem related to
313            the current page and set value.
314            @param value : integer 0 or 1
315            @param fitproblem: fitproblem to schedule or not to fit
316        """   
317        if fitproblem !=None:
318            fitproblem.schedule_tofit(value)
319        else:
320            if page in self.page_finder.iterkeys():
321                fitproblem= self.page_finder[page]
322                fitproblem.schedule_tofit(value)
323         
324                     
325                   
326    def get_page_finder(self):
327        """ @return self.page_finder used also by simfitpage.py""" 
328        return self.page_finder
329   
330   
331    def set_page_finder(self,modelname,names,values):
332        """
333             Used by simfitpage.py to reset a parameter given the string constrainst.
334             @param modelname: the name ot the model for with the parameter has to reset
335             @param value: can be a string in this case.
336             @param names: the paramter name
337             @note: expecting park used for fit.
338        """ 
339        sim_page= self.sim_page
340        for page, value in self.page_finder.iteritems():
341            if page != sim_page:
342                list=value.get_model()
343                model = list[0]
344                if model.name== modelname:
345                    value.set_model_param(names,values)
346                    break
347
348   
349                           
350    def split_string(self,item): 
351        """
352            receive a word containing dot and split it. used to split parameterset
353            name into model name and parameter name example:
354            paramaterset (item) = M1.A
355            @return model_name =M1 , parameter name =A
356        """
357        if string.find(item,".")!=-1:
358            param_names= re.split("\.",item)
359            model_name=param_names[0]
360            param_name=param_names[1] 
361            return model_name,param_name
362       
363   
364    def stop_fit(self):
365        """
366            Stop the fit engine
367        """
368        if self.calc_fit!= None and self.calc_fit.isrunning():
369            self.calc_fit.stop()
370            wx.PostEvent(self.parent, StatusEvent(status="Fitting  \
371                is cancelled" , type="stop"))
372           
373   
374     
375    def set_smearer(self,smearer, qmin=None, qmax=None):
376        """
377            Get a smear object and store it to a fit problem
378            @param smearer: smear object to allow smearing data
379        """   
380        current_pg=self.fit_panel.get_current_page()
381        self.page_finder[current_pg].set_smearer(smearer)
382        ## draw model 1D with smeared data
383        data =  self.page_finder[current_pg].get_plotted_data()
384        model = self.page_finder[current_pg].get_model()
385        ## if user has already selected a model to plot
386        ## redraw the model with data smeared
387       
388        smear =self.page_finder[current_pg].get_smearer()
389        if model!= None:
390            self.draw_model( model=model, data= data, smearer= smear,
391                qmin= qmin, qmax= qmax)
392
393   
394   
395    def draw_model(self, model, data= None,smearer= None,
396                   enable1D= True, enable2D= False,
397                   qmin= DEFAULT_QMIN, qmax= DEFAULT_QMAX, qstep= DEFAULT_NPTS):
398        """
399             Draw model.
400             @param model: the model to draw
401             @param name: the name of the model to draw
402             @param data: the data on which the model is based to be drawn
403             @param description: model's description
404             @param enable1D: if true enable drawing model 1D
405             @param enable2D: if true enable drawing model 2D
406             @param qmin:  Range's minimum value to draw model
407             @param qmax:  Range's maximum value to draw model
408             @param qstep: number of step to divide the x and y-axis
409             
410        """
411        ## draw model 1D with no loaded data
412        self._draw_model1D( model= model, data= data,enable1D=enable1D, smearer= smearer,
413                           qmin= qmin, qmax= qmax, qstep= qstep )
414        ## draw model 2D with no initial data
415        self._draw_model2D(model=model,
416                           data = data,
417                           enable2D= enable2D,
418                           qmin=qmin,
419                           qmax=qmax,
420                           qstep=qstep)
421       
422 
423                       
424    def onFit(self):
425        """
426            perform fit
427        """
428        ##  count the number of fitproblem schedule to fit
429        fitproblem_count= 0
430        for value in self.page_finder.itervalues():
431            if value.get_scheduled()==1:
432                fitproblem_count += 1
433               
434        ## if simultaneous fit change automatically the engine to park
435        if fitproblem_count >1:
436            self._on_change_engine(engine='park')
437           
438        from sans.fit.Fitting import Fit
439        self.fitter= Fit(self._fit_engine)
440       
441        if self._fit_engine=="park":
442            engineType="Simultaneous Fit"
443        else:
444            engineType="Single Fit"
445           
446        fproblemId = 0
447        current_pg=None
448        for page, value in self.page_finder.iteritems():
449            try:
450                if value.get_scheduled()==1:
451                    #Get list of parameters name to fit
452                    pars = []
453                    templist = []
454                    templist = page.get_param_list()
455                    for element in templist:
456                        name = str(element[1])
457                        pars.append(name)
458                    #Set Engine  (model , data) related to the page on
459                    self._fit_helper( current_pg=page, value=value,pars=pars,
460                                      id=fproblemId, title= engineType ) 
461                    fproblemId += 1 
462                    current_pg= page
463            except:
464                msg= "%s error: %s" % (engineType,sys.exc_value)
465                wx.PostEvent(self.parent, StatusEvent(status= msg ))
466                return 
467        #Do the simultaneous fit
468        try:
469            ## If a thread is already started, stop it
470            if self.calc_fit!= None and self.calc_fit.isrunning():
471                self.calc_fit.stop()
472           
473            wx.PostEvent(self.parent, StatusEvent(status="Start the computation",
474                                        curr_thread=self.calc_fit,type="start"))
475            wx.PostEvent(self.parent, StatusEvent(status="Computing...",
476                                        curr_thread=self.calc_fit,type="progress"))
477            ## perform single fit
478            if self._fit_engine=="scipy":
479                qmin, qmax= current_pg.get_range()
480                self.calc_fit=FitThread(parent =self.parent,
481                                        fn= self.fitter,
482                                       cpage=current_pg,
483                                       pars= pars,
484                                       completefn= self._single_fit_completed,
485                                       updatefn=self._updateFit)
486                     
487            else:
488                ## Perform more than 1 fit at the time
489                self.calc_fit=FitThread(parent =self.parent,
490                                        fn= self.fitter,
491                                       completefn= self._simul_fit_completed,
492                                       updatefn=self._updateFit)
493            self.calc_fit.queue()
494            self.calc_fit.ready(2.5)
495       
496        except FitAbort:
497            print "in pluging"
498        except:
499            msg= "%s error: %s" % (engineType,sys.exc_value)
500            wx.PostEvent(self.parent, StatusEvent(status= msg ,type="stop"))
501            return 
502           
503    def onCalculateSld(self, event):
504        """
505            Compute the scattering length density of molecula
506        """ 
507        from sldPanel import SldWindow
508        frame = SldWindow(base=self.parent)
509        frame.Show(True) 
510     
511         
512    def _onEVT_SLICER_PANEL(self, event):
513        """
514            receive and event telling to update a panel with a name starting with
515            event.panel_name. this method update slicer panel for a given interactor.
516            @param event: contains type of slicer , paramaters for updating the panel
517            and panel_name to find the slicer 's panel concerned.
518        """
519       
520        for item in self.parent.panels:
521            if self.parent.panels[item].window_caption.startswith(event.panel_name):
522                self.parent.panels[item].set_slicer(event.type, event.params)
523               
524        self.parent._mgr.Update()
525               
526           
527           
528    def _closed_fitpage(self, event):   
529        """
530            request fitpanel to close a given page when its unique data is removed
531            from the plot
532        """   
533        self.fit_panel._close_fitpage(event.data) 
534       
535       
536    def _add_page_onmenu(self, name,fitproblem=None):
537        """
538            Add name of a closed page of fitpanel in a menu
539        """
540        list = self.menu1.GetMenuItems()
541        for item in list:
542            if name == item.GetItemLabel():
543                self.closed_page_dict[name][1] = fitproblem
544               
545        if not name in self.closed_page_dict.keys():   
546            # Post paramters
547            event_id = wx.NewId()
548            self.menu1.Append(event_id, name, "Show %s fit panel" % name)
549            self.closed_page_dict[name]= [event_id, fitproblem]
550            wx.EVT_MENU(self.parent,event_id,  self._open_closed_page)
551       
552       
553    def _open_closed_page(self, event):   
554        """
555            reopen a closed page
556        """
557        for name, value in self.closed_page_dict.iteritems():
558            if event.GetId() in value:
559                id,fitproblem = value
560                if name !="Model":
561                    data= fitproblem.get_fit_data()
562                    page = self.fit_panel.add_fit_page(data= data,reset=True)
563                    if fitproblem != None:
564                        self.page_finder[page]=fitproblem
565                        if self.sim_page != None:
566                            self.sim_page.draw_page()
567                           
568                else:
569                    model = fitproblem
570                    self.fit_panel.add_model_page(model=model, topmenu=True,
571                                                  reset= True)
572                    break
573       
574       
575    def _reset_schedule_problem(self, value=0):
576        """
577             unschedule or schedule all fitproblem to be fit
578        """
579        for page, fitproblem in self.page_finder.iteritems():
580            fitproblem.schedule_tofit(value)
581           
582    def _fit_helper(self,current_pg,pars,value, id, title="Single Fit " ):
583        """
584            helper for fitting
585        """
586        metadata = value.get_fit_data()
587        model = value.get_model()
588        smearer = value.get_smearer()
589        qmin , qmax = value.get_range()
590        self.fit_id =id
591        #Create list of parameters for fitting used
592        templist=[]
593        pars=pars
594        try:
595            ## create a park model and reset parameter value if constraint
596            ## is given
597            new_model = Model(model)
598            param = value.get_model_param()
599            if len(param)>0:
600                for item in param:
601                    param_value = item[1]
602                    param_name = item[0]
603                    ## check if constraint
604                    if param_value !=None and param_name != None:
605                        new_model.parameterset[ param_name].set( param_value )
606           
607            #Do the single fit
608            self.fitter.set_model(new_model, self.fit_id, pars)
609           
610            self.fitter.set_data(data=metadata,Uid=self.fit_id,
611                                 smearer=smearer,qmin= qmin,qmax=qmax )
612           
613            self.fitter.select_problem_for_fit(Uid= self.fit_id,
614                                               value= value.get_scheduled())
615            value.clear_model_param()
616        except:
617            msg= title +" error: %s" % sys.exc_value
618            wx.PostEvent(self.parent, StatusEvent(status= msg, type="stop"))
619            return
620       
621    def _onSelect(self,event):
622        """
623            when Select data to fit a new page is created .Its reference is
624            added to self.page_finder
625        """
626        self.panel = event.GetEventObject()
627        Plugin.on_perspective(self,event=event)
628        for plottable in self.panel.graph.plottables:
629            if plottable.name == self.panel.graph.selected_plottable:
630                #if not hasattr(plottable, "is_data"):
631                   
632                if  plottable.__class__.__name__=="Theory1D":
633                    dy=numpy.zeros(len(plottable.y))
634                    if hasattr(plottable, "dy"):
635                        dy= copy.deepcopy(plottable.dy)
636                       
637                    item= self.copy_data(plottable, dy)
638                    item.group_id += "data1D"
639                    item.id +="data1D"
640                    item.is_data= False
641                    title = item.name
642                    wx.PostEvent(self.parent, NewPlotEvent(plot=item, title=str(title)))
643                else:
644                    item= self.copy_data(plottable, plottable.dy) 
645                    item.is_data=True
646                   
647                ## put the errors values back to the model if the errors were hiden
648                ## before sending them to the fit engine
649                if len(self.err_dy)>0:
650                    if item.name in  self.err_dy.iterkeys():
651                        dy= self.err_dy[item.name]
652                        data= self.copy_data(item, dy)
653                        data.is_data= item.is_data
654                    else:
655                        data= self.copy_data(item, item.dy)
656                        data.is_data= item.is_data
657                       
658                       
659                else:
660                    if item.dy==None:
661                        dy= numpy.zeros(len(item.y))
662                        data= self.copy_data(item, dy)
663                        data.is_data=item.is_data
664                    else:
665                        data= self.copy_data(item,item.dy)
666                        data.is_data=item.is_data
667                       
668            else:
669                if plottable.__class__.__name__ !="Data2D":
670                    return
671                ## Data2D case
672                if not hasattr(plottable, "is_data"):
673                    item= copy.deepcopy(plottable)
674                    item.group_id += "data2D"
675                    item.id +="data2D"
676                    item.is_data= False
677                    title = item.name
678                    title += " Fit"
679                    data = item
680                    wx.PostEvent(self.parent, NewPlotEvent(plot=item, title=str(title)))
681                else:
682                    item= copy.deepcopy(plottable )
683                    data= copy.deepcopy(plottable )
684                    item.is_data=True
685                    data.is_data=True
686               
687               
688            ## create anew page                   
689            if item.name == self.panel.graph.selected_plottable or\
690                 item.__class__.__name__ is "Data2D":
691                try:
692                    page = self.fit_panel.add_fit_page(data)
693                    # add data associated to the page created
694                    if page !=None:   
695                        #create a fitproblem storing all link to data,model,page creation
696                        if not page in self.page_finder.keys():
697                            self.page_finder[page]= FitProblem()
698                        ## item is almost the same as data but contains
699                        ## axis info for plotting
700                        self.page_finder[page].add_plotted_data(item)
701                        self.page_finder[page].add_fit_data(data)
702
703                        wx.PostEvent(self.parent, StatusEvent(status="Page Created"))
704                    else:
705                        wx.PostEvent(self.parent, StatusEvent(status="Page was already Created"))
706                except:
707                    wx.PostEvent(self.parent, StatusEvent(status="Creating Fit page: %s"\
708                    %sys.exc_value))
709                    return
710               
711               
712    def _updateFit(self):
713        """
714            Is called when values of result are available
715        """
716        ##Sending a progess message to the status bar
717        wx.PostEvent(self.parent, StatusEvent(status="Computing..."))
718           
719    def _single_fit_completed(self,result,pars,cpage, elapsed=None):
720        """
721            Display fit result on one page of the notebook.
722            @param result: result of fit
723            @param pars: list of names of parameters fitted
724            @param current_pg: the page where information will be displayed
725            @param qmin: the minimum value of x to replot the model
726            @param qmax: the maximum value of x to replot model
727         
728        """
729        wx.PostEvent(self.parent, StatusEvent(status="Single fit \
730        complete! " ))
731     
732        try:
733            if result ==None:
734                msg= "Simple Fitting Stop !!!"
735                wx.PostEvent(self.parent, StatusEvent(status=msg,type="stop"))
736                return
737            if numpy.any(result.pvec ==None )or not numpy.all(numpy.isfinite(result.pvec) ):
738                msg= "Single Fitting did not converge!!!"
739                wx.PostEvent(self.parent, StatusEvent(status=msg,type="stop"))
740                return
741            for page, value in self.page_finder.iteritems():
742                if page==cpage :
743                    model= value.get_model()
744                    break
745            i = 0
746            for name in pars:
747                if result.pvec.__class__==numpy.float64:
748                    model.setParam(name,result.pvec)
749                else:
750                    model.setParam(name,result.pvec[i])
751                    i += 1
752            ## Reset values of the current page to fit result
753            cpage.onsetValues(result.fitness, result.pvec,result.stderr)
754            ## plot the current model with new param
755            metadata =  self.page_finder[cpage].get_fit_data()
756            model = self.page_finder[cpage].get_model()
757            qmin, qmax= self.page_finder[cpage].get_range()
758            smearer =self.page_finder[cpage].get_smearer()
759            #Replot models
760            msg= "Single Fit completed. plotting... %s:"%model.name
761            wx.PostEvent(self.parent, StatusEvent(status="%s " % msg))
762            self.draw_model( model=model, data= metadata, smearer= smearer,
763                             qmin= qmin, qmax= qmax)
764            wx.PostEvent(self.parent, StatusEvent(status=" " , type="stop")) 
765        except:
766            msg= "Single Fit completed but Following error occurred:%s"% sys.exc_value
767            wx.PostEvent(self.parent, StatusEvent(status=msg,type="stop"))
768            return
769       
770       
771    def _simul_fit_completed(self,result,pars=None,cpage=None, elapsed=None):
772        """
773            Parameter estimation completed,
774            display the results to the user
775            @param alpha: estimated best alpha
776            @param elapsed: computation time
777        """
778        wx.PostEvent(self.parent, StatusEvent(status="Simultaneous fit complete "))
779       
780        ## fit more than 1 model at the same time
781        try:
782            if result ==None:
783                msg= "Complex Fitting Stop !!!"
784                wx.PostEvent(self.parent, StatusEvent(status=msg,type="stop"))
785                return
786            if numpy.any(result.pvec ==None )or not numpy.all(numpy.isfinite(result.pvec) ):
787                msg= "Single Fitting did not converge!!!"
788                wx.PostEvent(self.parent, StatusEvent(status=msg,type="stop"))
789                return
790            for page, value in self.page_finder.iteritems():
791                if value.get_scheduled()==1:
792                    model = value.get_model()
793                    metadata =  value.get_plotted_data()
794                    small_out = []
795                    small_cov = []
796                    i = 0
797                    #Separate result in to data corresponding to each page
798                    for p in result.parameters:
799                        model_name,param_name = self.split_string(p.name) 
800                        if model.name == model_name:
801                            p_name= model.name+"."+param_name
802                            if p.name == p_name:
803                                small_out.append(p.value )
804                                model.setParam(param_name,p.value) 
805                             
806                                small_cov.append(p.stderr)
807                            else:
808                                value= model.getParam(param_name)
809                                small_out.append(value )
810                                small_cov.append(None)
811                    # Display result on each page
812                    page.onsetValues(result.fitness, small_out,small_cov)
813                    #Replot models
814                    msg= "Simultaneous Fit completed. plotting... %s:"%model.name
815                    wx.PostEvent(self.parent, StatusEvent(status="%s " % msg))
816                    qmin, qmax= page.get_range()
817                    smearer =self.page_finder[page].get_smearer()
818                    self.draw_model( model=model, data= metadata, smearer=smearer,
819                                     qmin= qmin, qmax= qmax)
820            wx.PostEvent(self.parent, StatusEvent(status="", type="stop"))
821        except:
822             msg= "Simultaneous Fit completed"
823             msg +=" but Following error occurred:%s"%sys.exc_value
824             wx.PostEvent(self.parent, StatusEvent(status=msg,type="stop"))
825             return 
826             
827                           
828       
829    def _on_show_panel(self, event):
830        print "_on_show_panel: fitting"
831     
832     
833    def _onset_engine_park(self,event):
834        """
835            set engine to park
836        """
837        self._on_change_engine('park')
838       
839       
840    def _onset_engine_scipy(self,event):
841        """
842            set engine to scipy
843        """
844        self._on_change_engine('scipy')
845       
846    def _on_slicer_event(self, event):
847        """
848            Receive a panel as event and send it to guiframe
849            @param event: event containing a panel
850        """
851       
852        if event.panel!=None:
853            new_panel = event.panel
854            self.slicer_panels.append(event.panel)
855            # Set group ID if available
856            event_id = self.parent.popup_panel(new_panel)
857            #self.menu3.Append(event_id, new_panel.window_caption,
858            #                 "Show %s plot panel" % new_panel.window_caption)
859            # Set UID to allow us to reference the panel later
860         
861            new_panel.uid = event_id
862            self.mypanels.append(new_panel) 
863        return 
864   
865    def _onclearslicer(self, event):
866        """
867            Clear the boxslicer when close the panel associate with this slicer
868        """
869        name =event.GetPane().caption
870   
871        for panel in self.slicer_panels:
872            if panel.window_caption==name:
873               
874                for item in self.parent.panels:
875                    if hasattr(self.parent.panels[item],"uid"):
876                        if self.parent.panels[item].uid ==panel.base.uid:
877                            self.parent.panels[item].onClearSlicer(event)
878                            self.parent._mgr.Update()
879                            break 
880                break
881       
882       
883       
884       
885    def _return_engine_type(self):
886        """
887            return the current type of engine
888        """
889        return self._fit_engine
890     
891     
892    def _on_change_engine(self, engine='park'):
893        """
894            Allow to select the type of engine to perform fit
895            @param engine: the key work of the engine
896        """
897        ## saving fit engine name
898        self._fit_engine = engine
899        ## change menu item state
900        if engine=="park":
901            self.menu1.FindItemByPosition(0).Check(False)
902            self.menu1.FindItemByPosition(1).Check(True)
903        else:
904            self.menu1.FindItemByPosition(0).Check(True)
905            self.menu1.FindItemByPosition(1).Check(False)
906           
907        ## post a message to status bar
908        wx.PostEvent(self.parent, StatusEvent(status="Engine set to: %s" % self._fit_engine))
909   
910        ## Bind every open fit page with a newevent to know the current fitting engine
911        import fitpage
912        event= fitpage.FitterTypeEvent()
913        event.type = self._fit_engine
914        for key in self.page_finder.keys():
915            wx.PostEvent(key, event)
916       
917   
918    def _on_model_panel(self, evt):
919        """
920            react to model selection on any combo box or model menu.plot the model 
921            @param evt: wx.combobox event
922        """
923        model = evt.model
924       
925        if model ==None:
926            return
927        model.origin_name = model.name
928        current_pg = self.fit_panel.get_current_page() 
929        ## make sure nothing is done on self.sim_page
930        ## example trying to call set_panel on self.sim_page
931        if current_pg != self.sim_page :
932           
933            if self.page_finder[current_pg].get_model()== None :
934               
935                model.name="M"+str(self.index_model)
936                self.index_model += 1 
937            else:
938                model.name= self.page_finder[current_pg].get_model().name
939               
940            metadata = self.page_finder[current_pg].get_plotted_data()
941           
942            # save the name containing the data name with the appropriate model
943            self.page_finder[current_pg].set_model(model)
944            qmin, qmax= current_pg.get_range()
945            self.page_finder[current_pg].set_range(qmin=qmin, qmax=qmax)
946            smearer=  self.page_finder[current_pg].get_smearer()
947            # save model name
948            self.draw_model( model=model,smearer=smearer, 
949                             data= metadata, qmin=qmin, qmax=qmax)
950           
951            if self.sim_page!=None:
952                self.sim_page.draw_page()
953       
954       
955 
956    def _on_model_menu(self, evt):
957        """
958            Plot a theory from a model selected from the menu
959            @param evt: wx.menu event
960        """
961        model = evt.model
962        Plugin.on_perspective(self,event=evt)
963        # Create a model page. If a new page is created, the model
964        # will be plotted automatically. If a page already exists,
965        # the content will be updated and the plot refreshed
966        self.fit_panel.add_model_page(model,topmenu=True)
967   
968   
969   
970   
971    def _update1D(self,x, output):
972        """
973            Update the output of plotting model 1D
974        """
975        wx.PostEvent(self.parent, StatusEvent(status="Plot \
976        #updating ... ",type="update"))
977        self.calc_thread.ready(0.01)
978   
979   
980    def _fill_default_model2D(self, theory, qmax,qstep, qmin=None):
981        """
982            fill Data2D with default value
983            @param theory: Data2D to fill
984        """
985        from DataLoader.data_info import Detector, Source
986       
987        detector = Detector()
988        theory.detector.append(detector) 
989           
990        theory.detector[0].distance=1e+32
991        theory.source= Source()
992        theory.source.wavelength=2*math.pi/1e+32
993     
994        ## Create detector for Model 2D
995        xmax=2*theory.detector[0].distance*math.atan(\
996                            qmax/(4*math.pi/theory.source.wavelength))
997       
998        theory.detector[0].pixel_size.x= xmax/(qstep/2-0.5)
999        theory.detector[0].pixel_size.y= xmax/(qstep/2-0.5)
1000        theory.detector[0].beam_center.x= qmax
1001        theory.detector[0].beam_center.y= qmax
1002        ## create x_bins and y_bins of the model 2D
1003        distance   = theory.detector[0].distance
1004        pixel      = qstep/2-1
1005        theta      = pixel / distance / qstep#100.0
1006        wavelength = theory.source.wavelength
1007        pixel_width_x = theory.detector[0].pixel_size.x
1008        pixel_width_y = theory.detector[0].pixel_size.y
1009        center_x      = theory.detector[0].beam_center.x/pixel_width_x
1010        center_y      = theory.detector[0].beam_center.y/pixel_width_y
1011       
1012       
1013        size_x, size_y= numpy.shape(theory.data)
1014        for i_x in range(size_x):
1015            theta = (i_x-center_x)*pixel_width_x / distance
1016            qx = 4.0*math.pi/wavelength * math.tan(theta/2.0)
1017            theory.x_bins.append(qx)   
1018        for i_y in range(size_y):
1019            theta = (i_y-center_y)*pixel_width_y / distance
1020            qy =4.0*math.pi/wavelength * math.tan(theta/2.0)
1021            theory.y_bins.append(qy)
1022           
1023        theory.group_id ="Model"
1024        theory.id ="Model"
1025        ## determine plot boundaries
1026        theory.xmin= -qmax
1027        theory.xmax= qmax
1028        theory.ymin= -qmax
1029        theory.ymax= qmax
1030       
1031       
1032    def _get_plotting_info(self, data=None):
1033        """
1034            get plotting info from data if data !=None
1035            else use some default
1036        """
1037        my_info = PlotInfo()
1038        if data !=None:
1039            if hasattr(data,"info"):
1040                x_name, x_units = data.get_xaxis() 
1041                y_name, y_units = data.get_yaxis() 
1042               
1043                my_info._xunit = x_units
1044                my_info._xaxis = x_name
1045                my_info._yunit = y_units
1046                my_info._yaxis = y_name
1047               
1048            my_info.title= data.name
1049            if hasattr(data, "info"):
1050                my_info.info= data.info
1051            if hasattr(data, "group_id"):
1052                my_info.group_id= data.group_id
1053       
1054        return my_info
1055               
1056               
1057    def _complete1D(self, x,y, elapsed,model,data=None):
1058        """
1059            Complete plotting 1D data
1060        """ 
1061       
1062        try:
1063           
1064            new_plot = Theory1D(x=x, y=y)
1065            my_info = self._get_plotting_info( data)
1066            new_plot.name = model.name
1067            new_plot.id = my_info.id
1068            new_plot.group_id = my_info.group_id
1069           
1070            new_plot.xaxis( my_info._xaxis,  my_info._xunit)
1071            new_plot.yaxis( my_info._yaxis, my_info._yunit)
1072            if data!=None:
1073                if new_plot.id == data.id:
1074                    new_plot.id += "Model"
1075                new_plot.is_data =False 
1076           
1077           
1078            title= new_plot.name
1079           
1080            # Pass the reset flag to let the plotting event handler
1081            # know that we are replacing the whole plot
1082            if title== None:
1083                title = "Analytical model 1D "
1084                wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1085                             title= str(title), reset=True ))
1086            else:
1087                wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1088                             title= str(title)))
1089            msg = "Plot 1D  complete !"
1090            wx.PostEvent( self.parent, StatusEvent( status= msg , type="stop" ))
1091        except:
1092            msg= " Error occurred when drawing %s Model 1D: "%new_plot.name
1093            msg+= " %s"%sys.exc_value
1094            wx.PostEvent( self.parent, StatusEvent(status= msg, type="stop"  ))
1095            return 
1096                 
1097   
1098       
1099    def _update2D(self, output,time=None):
1100        """
1101            Update the output of plotting model
1102        """
1103        wx.PostEvent(self.parent, StatusEvent(status="Plot \
1104        #updating ... ",type="update"))
1105        self.calc_thread.ready(0.01)
1106       
1107       
1108    def _complete2D(self, image,data, model,  elapsed,qmin, qmax,qstep=DEFAULT_NPTS):
1109        """
1110            Complete get the result of modelthread and create model 2D
1111            that can be plot.
1112        """
1113     
1114   
1115        err_image = numpy.zeros(numpy.shape(image))
1116       
1117        theory= Data2D(image= image , err_image= err_image)
1118        theory.name= model.name
1119       
1120        if data ==None:
1121            self._fill_default_model2D(theory= theory, qmax=qmax,qstep=qstep, qmin= qmin)
1122       
1123        else:
1124            theory.id= data.id+"Model"
1125            theory.group_id= data.name+"Model"
1126            theory.x_bins= data.x_bins
1127            theory.y_bins= data.y_bins
1128            theory.detector= data.detector
1129            theory.source= data.source
1130            theory.is_data =False 
1131            ## plot boundaries
1132            theory.ymin= data.ymin
1133            theory.ymax= data.ymax
1134            theory.xmin= data.xmin
1135            theory.xmax= data.xmax
1136     
1137       
1138        ## plot
1139        wx.PostEvent(self.parent, NewPlotEvent(plot=theory,
1140                         title="Analytical model 2D ", reset=True ))
1141        msg = "Plot 2D complete !"
1142        wx.PostEvent( self.parent, StatusEvent( status= msg , type="stop" ))
1143         
1144    def _on_data_error(self, event):
1145        """
1146            receives and event from plotting plu-gins to store the data name and
1147            their errors of y coordinates for 1Data hide and show error
1148        """
1149        self.err_dy= event.err_dy
1150       
1151         
1152    def _draw_model2D(self,model,data=None,description=None, enable2D=False,
1153                      qmin=DEFAULT_QMIN, qmax=DEFAULT_QMAX, qstep=DEFAULT_NPTS):
1154        """
1155            draw model in 2D
1156            @param model: instance of the model to draw
1157            @param description: the description of the model
1158            @param enable2D: when True allows to draw model 2D
1159            @param qmin: the minimum value to  draw model 2D
1160            @param qmax: the maximum value to draw model 2D
1161            @param qstep: the number of division of Qx and Qy of the model to draw
1162           
1163        """
1164        x=  numpy.linspace(start= -1*qmax,
1165                               stop= qmax,
1166                               num= qstep,
1167                               endpoint=True ) 
1168        y = numpy.linspace(start= -1*qmax,
1169                               stop= qmax,
1170                               num= qstep,
1171                               endpoint=True )
1172        ## use data info instead
1173        if data !=None:
1174            ## check if data2D to plot
1175            if hasattr(data, "x_bins"):
1176                enable2D = True
1177                x= data.x_bins
1178                y= data.y_bins
1179           
1180        if not enable2D:
1181            return
1182        try:
1183            from model_thread import Calc2D
1184            ## If a thread is already started, stop it
1185            if self.calc_2D != None and self.calc_2D.isrunning():
1186                self.calc_2D.stop()
1187            self.calc_2D = Calc2D(  x= x,
1188                                    y= y,
1189                                    model= model, 
1190                                    data = data,
1191                                    qmin= qmin,
1192                                    qmax= qmax,
1193                                    qstep= qstep,
1194                                    completefn= self._complete2D,
1195                                    updatefn= self._update2D )
1196            self.calc_2D.queue()
1197           
1198        except:
1199            raise
1200            #msg= " Error occurred when drawing %s Model 2D: "%model.name
1201            #msg+= " %s"%sys.exc_value
1202            #wx.PostEvent( self.parent, StatusEvent(status= msg ))
1203            #return 
1204   
1205    def _draw_model1D(self, model, data=None, smearer= None,
1206                qmin=DEFAULT_QMIN, qmax=DEFAULT_QMAX, qstep= DEFAULT_NPTS,enable1D= True):
1207        """
1208            Draw model 1D from loaded data1D
1209            @param data: loaded data
1210            @param model: the model to plot
1211        """
1212         
1213        x=  numpy.linspace(start= qmin,
1214                           stop= qmax,
1215                           num= qstep,
1216                           endpoint=True
1217                           )
1218        if data!=None:
1219            ## check for data2D
1220            if hasattr(data,"x_bins"):
1221                return
1222            x = data.x
1223            if qmin == DEFAULT_QMIN :
1224                qmin = min(data.x)
1225            if qmax == DEFAULT_QMAX:
1226                qmax = max(data.x) 
1227           
1228       
1229        if not enable1D:
1230            return
1231   
1232        try:
1233            from model_thread import Calc1D
1234            ## If a thread is already started, stop it
1235            if self.calc_1D!= None and self.calc_1D.isrunning():
1236                self.calc_1D.stop()
1237            self.calc_1D= Calc1D( x= x,
1238                                  data = data,
1239                                  model= model, 
1240                                  qmin = qmin,
1241                                  qmax = qmax,
1242                                  smearer = smearer,
1243                                  completefn = self._complete1D,
1244                                  updatefn = self._update1D  )
1245            self.calc_1D.queue()
1246           
1247        except:
1248            msg= " Error occurred when drawing %s Model 1D: "%model.name
1249            msg+= " %s"%sys.exc_value
1250            wx.PostEvent( self.parent, StatusEvent(status= msg ))
1251            return 
1252           
1253
1254def profile(fn, *args, **kw):
1255    import cProfile, pstats, os
1256    global call_result
1257    def call():
1258        global call_result
1259        call_result = fn(*args, **kw)
1260    cProfile.runctx('call()', dict(call=call), {}, 'profile.out')
1261    stats = pstats.Stats('profile.out')
1262    stats.sort_stats('time')
1263    #stats.sort_stats('calls')
1264    stats.print_stats()
1265    os.unlink('profile.out')
1266    return call_result
1267if __name__ == "__main__":
1268    i = Plugin()
1269   
1270   
1271   
1272   
Note: See TracBrowser for help on using the repository browser.