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

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 4cabee7 was 4cabee7, checked in by Jae Cho <jhjcho@…>, 15 years ago

Fixed scipy fitting on Mac: park fitting still to go.

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