source: sasview/sansview/perspectives/fitting/fitting.py @ 75d6342

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 75d6342 was 75d6342, checked in by Gervaise Alina <gervyh@…>, 15 years ago

commit sld calculator

  • 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                (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            self.calc_fit.queue()
490            self.calc_fit.ready(2.5)
491       
492        except FitAbort:
493            print "in pluging"
494        except:
495            msg= "%s error: %s" % (engineType,sys.exc_value)
496            wx.PostEvent(self.parent, StatusEvent(status= msg ,type="stop"))
497            return 
498           
499    def onCalculateSld(self, event):
500        """
501            Compute the scattering length density of molecula
502        """ 
503        from sldPanel import SldWindow
504        frame = SldWindow(base=self.parent)
505        frame.Show(True) 
506     
507         
508    def _onEVT_SLICER_PANEL(self, event):
509        """
510            receive and event telling to update a panel with a name starting with
511            event.panel_name. this method update slicer panel for a given interactor.
512            @param event: contains type of slicer , paramaters for updating the panel
513            and panel_name to find the slicer 's panel concerned.
514        """
515       
516        for item in self.parent.panels:
517            if self.parent.panels[item].window_caption.startswith(event.panel_name):
518                self.parent.panels[item].set_slicer(event.type, event.params)
519               
520        self.parent._mgr.Update()
521               
522           
523           
524    def _closed_fitpage(self, event):   
525        """
526            request fitpanel to close a given page when its unique data is removed
527            from the plot
528        """   
529        self.fit_panel._close_fitpage(event.data) 
530       
531       
532    def _add_page_onmenu(self, name,fitproblem=None):
533        """
534            Add name of a closed page of fitpanel in a menu
535        """
536        list = self.menu1.GetMenuItems()
537        for item in list:
538            if name == item.GetItemLabel():
539                self.closed_page_dict[name][1] = fitproblem
540               
541        if not name in self.closed_page_dict.keys():   
542            # Post paramters
543            event_id = wx.NewId()
544            self.menu1.Append(event_id, name, "Show %s fit panel" % name)
545            self.closed_page_dict[name]= [event_id, fitproblem]
546            wx.EVT_MENU(self.parent,event_id,  self._open_closed_page)
547       
548       
549    def _open_closed_page(self, event):   
550        """
551            reopen a closed page
552        """
553        for name, value in self.closed_page_dict.iteritems():
554            if event.GetId() in value:
555                id,fitproblem = value
556                if name !="Model":
557                    data= fitproblem.get_fit_data()
558                    page = self.fit_panel.add_fit_page(data= data,reset=True)
559                    if fitproblem != None:
560                        self.page_finder[page]=fitproblem
561                        if self.sim_page != None:
562                            self.sim_page.draw_page()
563                           
564                else:
565                    model = fitproblem
566                    self.fit_panel.add_model_page(model=model, topmenu=True,
567                                                  reset= True)
568                    break
569       
570       
571    def _reset_schedule_problem(self, value=0):
572        """
573             unschedule or schedule all fitproblem to be fit
574        """
575        for page, fitproblem in self.page_finder.iteritems():
576            fitproblem.schedule_tofit(value)
577           
578    def _fit_helper(self,pars,value, id, title="Single Fit " ):
579        """
580            helper for fitting
581        """
582        metadata = value.get_fit_data()
583        model = value.get_model()
584        smearer = value.get_smearer()
585        qmin , qmax = value.get_range()
586        self.fit_id =id
587        #Create list of parameters for fitting used
588        templist=[]
589        pars=pars
590        try:
591            ## create a park model and reset parameter value if constraint
592            ## is given
593            new_model = Model(model)
594            param = value.get_model_param()
595            if len(param)>0:
596                for item in param:
597                    param_value = item[1]
598                    param_name = item[0]
599                    ## check if constraint
600                    if param_value !=None and param_name != None:
601                        new_model.parameterset[ param_name].set( param_value )
602           
603            #Do the single fit
604            self.fitter.set_model(new_model, self.fit_id, pars)
605           
606            self.fitter.set_data(data=metadata,Uid=self.fit_id,
607                                 smearer=smearer,qmin= qmin,qmax=qmax )
608           
609            self.fitter.select_problem_for_fit(Uid= self.fit_id,
610                                               value= value.get_scheduled())
611            value.clear_model_param()
612        except:
613            msg= title +" error: %s" % sys.exc_value
614            wx.PostEvent(self.parent, StatusEvent(status= msg, type="stop"))
615            return
616       
617    def _onSelect(self,event):
618        """
619            when Select data to fit a new page is created .Its reference is
620            added to self.page_finder
621        """
622        self.panel = event.GetEventObject()
623        Plugin.on_perspective(self,event=event)
624        for plottable in self.panel.graph.plottables:
625            if plottable.name == self.panel.graph.selected_plottable:
626                #if not hasattr(plottable, "is_data"):
627                   
628                if  plottable.__class__.__name__=="Theory1D":
629                    dy=numpy.zeros(len(plottable.y))
630                    if hasattr(plottable, "dy"):
631                        dy= copy.deepcopy(plottable.dy)
632                       
633                    item= self.copy_data(plottable, dy)
634                    item.group_id += "data1D"
635                    item.id +="data1D"
636                    item.is_data= False
637                    title = item.name
638                    wx.PostEvent(self.parent, NewPlotEvent(plot=item, title=str(title)))
639                else:
640                    item= self.copy_data(plottable, plottable.dy) 
641                    item.is_data=True
642                   
643                ## put the errors values back to the model if the errors were hiden
644                ## before sending them to the fit engine
645                if len(self.err_dy)>0:
646                    if item.name in  self.err_dy.iterkeys():
647                        dy= self.err_dy[item.name]
648                        data= self.copy_data(item, dy)
649                        data.is_data= item.is_data
650                    else:
651                        data= self.copy_data(item, item.dy)
652                        data.is_data= item.is_data
653                       
654                       
655                else:
656                    if item.dy==None:
657                        dy= numpy.zeros(len(item.y))
658                        data= self.copy_data(item, dy)
659                        data.is_data=item.is_data
660                    else:
661                        data= self.copy_data(item,item.dy)
662                        data.is_data=item.is_data
663                       
664            else:
665                if plottable.__class__.__name__ !="Data2D":
666                    return
667                ## Data2D case
668                if not hasattr(plottable, "is_data"):
669                    item= copy.deepcopy(plottable)
670                    item.group_id += "data2D"
671                    item.id +="data2D"
672                    item.is_data= False
673                    title = item.name
674                    title += " Fit"
675                    data = item
676                    wx.PostEvent(self.parent, NewPlotEvent(plot=item, title=str(title)))
677                else:
678                    item= copy.deepcopy(plottable )
679                    data= copy.deepcopy(plottable )
680                    item.is_data=True
681                    data.is_data=True
682               
683               
684            ## create anew page                   
685            if item.name == self.panel.graph.selected_plottable or\
686                 item.__class__.__name__ is "Data2D":
687                try:
688                    page = self.fit_panel.add_fit_page(data)
689                    # add data associated to the page created
690                    if page !=None:   
691                        #create a fitproblem storing all link to data,model,page creation
692                        if not page in self.page_finder.keys():
693                            self.page_finder[page]= FitProblem()
694                        ## item is almost the same as data but contains
695                        ## axis info for plotting
696                        self.page_finder[page].add_plotted_data(item)
697                        self.page_finder[page].add_fit_data(data)
698
699                        wx.PostEvent(self.parent, StatusEvent(status="Page Created"))
700                    else:
701                        wx.PostEvent(self.parent, StatusEvent(status="Page was already Created"))
702                except:
703                    wx.PostEvent(self.parent, StatusEvent(status="Creating Fit page: %s"\
704                    %sys.exc_value))
705                    return
706               
707               
708    def _updateFit(self):
709        """
710            Is called when values of result are available
711        """
712        ##Sending a progess message to the status bar
713        wx.PostEvent(self.parent, StatusEvent(status="Computing..."))
714           
715    def _single_fit_completed(self,result,pars,cpage, elapsed=None):
716        """
717            Display fit result on one page of the notebook.
718            @param result: result of fit
719            @param pars: list of names of parameters fitted
720            @param current_pg: the page where information will be displayed
721            @param qmin: the minimum value of x to replot the model
722            @param qmax: the maximum value of x to replot model
723         
724        """
725        wx.PostEvent(self.parent, StatusEvent(status="Single fit \
726        complete! " ))
727     
728        try:
729            if result ==None:
730                msg= "Simple Fitting Stop !!!"
731                wx.PostEvent(self.parent, StatusEvent(status=msg,type="stop"))
732                return
733            if numpy.any(result.pvec ==None )or not numpy.all(numpy.isfinite(result.pvec) ):
734                msg= "Single Fitting did not converge!!!"
735                wx.PostEvent(self.parent, StatusEvent(status=msg,type="stop"))
736                return
737            for page, value in self.page_finder.iteritems():
738                if page==cpage :
739                    model= value.get_model()
740                    break
741            i = 0
742            for name in pars:
743                if result.pvec.__class__==numpy.float64:
744                    model.setParam(name,result.pvec)
745                else:
746                    model.setParam(name,result.pvec[i])
747                    i += 1
748            ## Reset values of the current page to fit result
749            cpage.onsetValues(result.fitness, result.pvec,result.stderr)
750            ## plot the current model with new param
751            metadata =  self.page_finder[cpage].get_fit_data()
752            model = self.page_finder[cpage].get_model()
753            qmin, qmax= self.page_finder[cpage].get_range()
754            smearer =self.page_finder[cpage].get_smearer()
755            #Replot models
756            msg= "Single Fit completed. plotting... %s:"%model.name
757            wx.PostEvent(self.parent, StatusEvent(status="%s " % msg))
758            self.draw_model( model=model, data= metadata, smearer= smearer,
759                             qmin= qmin, qmax= qmax)
760            wx.PostEvent(self.parent, StatusEvent(status=" " , type="stop")) 
761        except:
762            msg= "Single Fit completed but Following error occurred:%s"% sys.exc_value
763            wx.PostEvent(self.parent, StatusEvent(status=msg,type="stop"))
764            return
765       
766       
767    def _simul_fit_completed(self,result,pars=None,cpage=None, elapsed=None):
768        """
769            Parameter estimation completed,
770            display the results to the user
771            @param alpha: estimated best alpha
772            @param elapsed: computation time
773        """
774        wx.PostEvent(self.parent, StatusEvent(status="Simultaneous fit complete "))
775       
776        ## fit more than 1 model at the same time
777        try:
778            if result ==None:
779                msg= "Complex Fitting Stop !!!"
780                wx.PostEvent(self.parent, StatusEvent(status=msg,type="stop"))
781                return
782            if numpy.any(result.pvec ==None )or not numpy.all(numpy.isfinite(result.pvec) ):
783                msg= "Single Fitting did not converge!!!"
784                wx.PostEvent(self.parent, StatusEvent(status=msg,type="stop"))
785                return
786            for page, value in self.page_finder.iteritems():
787                if value.get_scheduled()==1:
788                    model = value.get_model()
789                    metadata =  value.get_plotted_data()
790                    small_out = []
791                    small_cov = []
792                    i = 0
793                    #Separate result in to data corresponding to each page
794                    for p in result.parameters:
795                        model_name,param_name = self.split_string(p.name) 
796                        if model.name == model_name:
797                            p_name= model.name+"."+param_name
798                            if p.name == p_name:
799                                small_out.append(p.value )
800                                model.setParam(param_name,p.value) 
801                             
802                                small_cov.append(p.stderr)
803                            else:
804                                value= model.getParam(param_name)
805                                small_out.append(value )
806                                small_cov.append(None)
807                    # Display result on each page
808                    page.onsetValues(result.fitness, small_out,small_cov)
809                    #Replot models
810                    msg= "Simultaneous Fit completed. plotting... %s:"%model.name
811                    wx.PostEvent(self.parent, StatusEvent(status="%s " % msg))
812                    qmin, qmax= page.get_range()
813                    smearer =self.page_finder[page].get_smearer()
814                    self.draw_model( model=model, data= metadata, smearer=smearer,
815                                     qmin= qmin, qmax= qmax)
816            wx.PostEvent(self.parent, StatusEvent(status="", type="stop"))
817        except:
818             msg= "Simultaneous Fit completed"
819             msg +=" but Following error occurred:%s"%sys.exc_value
820             wx.PostEvent(self.parent, StatusEvent(status=msg,type="stop"))
821             return 
822             
823                           
824       
825    def _on_show_panel(self, event):
826        print "_on_show_panel: fitting"
827     
828     
829    def _onset_engine_park(self,event):
830        """
831            set engine to park
832        """
833        self._on_change_engine('park')
834       
835       
836    def _onset_engine_scipy(self,event):
837        """
838            set engine to scipy
839        """
840        self._on_change_engine('scipy')
841       
842    def _on_slicer_event(self, event):
843        """
844            Receive a panel as event and send it to guiframe
845            @param event: event containing a panel
846        """
847       
848        if event.panel!=None:
849            new_panel = event.panel
850            self.slicer_panels.append(event.panel)
851            # Set group ID if available
852            event_id = self.parent.popup_panel(new_panel)
853            #self.menu3.Append(event_id, new_panel.window_caption,
854            #                 "Show %s plot panel" % new_panel.window_caption)
855            # Set UID to allow us to reference the panel later
856         
857            new_panel.uid = event_id
858            self.mypanels.append(new_panel) 
859        return 
860   
861    def _onclearslicer(self, event):
862        """
863            Clear the boxslicer when close the panel associate with this slicer
864        """
865        name =event.GetPane().caption
866   
867        for panel in self.slicer_panels:
868            if panel.window_caption==name:
869               
870                for item in self.parent.panels:
871                    if hasattr(self.parent.panels[item],"uid"):
872                        if self.parent.panels[item].uid ==panel.base.uid:
873                            self.parent.panels[item].onClearSlicer(event)
874                            self.parent._mgr.Update()
875                            break 
876                break
877       
878       
879       
880       
881    def _return_engine_type(self):
882        """
883            return the current type of engine
884        """
885        return self._fit_engine
886     
887     
888    def _on_change_engine(self, engine='park'):
889        """
890            Allow to select the type of engine to perform fit
891            @param engine: the key work of the engine
892        """
893        ## saving fit engine name
894        self._fit_engine = engine
895        ## change menu item state
896        if engine=="park":
897            self.menu1.FindItemByPosition(0).Check(False)
898            self.menu1.FindItemByPosition(1).Check(True)
899        else:
900            self.menu1.FindItemByPosition(0).Check(True)
901            self.menu1.FindItemByPosition(1).Check(False)
902           
903        ## post a message to status bar
904        wx.PostEvent(self.parent, StatusEvent(status="Engine set to: %s" % self._fit_engine))
905   
906        ## Bind every open fit page with a newevent to know the current fitting engine
907        import fitpage
908        event= fitpage.FitterTypeEvent()
909        event.type = self._fit_engine
910        for key in self.page_finder.keys():
911            wx.PostEvent(key, event)
912       
913   
914    def _on_model_panel(self, evt):
915        """
916            react to model selection on any combo box or model menu.plot the model 
917            @param evt: wx.combobox event
918        """
919        model = evt.model
920       
921        if model ==None:
922            return
923        model.origin_name = model.name
924        self.current_pg = self.fit_panel.get_current_page() 
925        ## make sure nothing is done on self.sim_page
926        ## example trying to call set_panel on self.sim_page
927        if self.current_pg != self.sim_page :
928           
929            if self.page_finder[self.current_pg].get_model()== None :
930               
931                model.name="M"+str(self.index_model)
932                self.index_model += 1 
933            else:
934                model.name= self.page_finder[self.current_pg].get_model().name
935               
936            metadata = self.page_finder[self.current_pg].get_plotted_data()
937           
938            # save the name containing the data name with the appropriate model
939            self.page_finder[self.current_pg].set_model(model)
940            qmin, qmax= self.current_pg.get_range()
941            self.page_finder[self.current_pg].set_range(qmin=qmin, qmax=qmax)
942            smearer=  self.page_finder[self.current_pg].get_smearer()
943            # save model name
944            self.draw_model( model=model,smearer=smearer, 
945                             data= metadata, qmin=qmin, qmax=qmax)
946           
947            if self.sim_page!=None:
948                self.sim_page.draw_page()
949       
950       
951 
952    def _on_model_menu(self, evt):
953        """
954            Plot a theory from a model selected from the menu
955            @param evt: wx.menu event
956        """
957        model = evt.model
958        Plugin.on_perspective(self,event=evt)
959        # Create a model page. If a new page is created, the model
960        # will be plotted automatically. If a page already exists,
961        # the content will be updated and the plot refreshed
962        self.fit_panel.add_model_page(model,topmenu=True)
963   
964   
965   
966   
967    def _update1D(self,x, output):
968        """
969            Update the output of plotting model 1D
970        """
971        wx.PostEvent(self.parent, StatusEvent(status="Plot \
972        #updating ... ",type="update"))
973        self.calc_thread.ready(0.01)
974   
975   
976    def _fill_default_model2D(self, theory, qmax,qstep, qmin=None):
977        """
978            fill Data2D with default value
979            @param theory: Data2D to fill
980        """
981        from DataLoader.data_info import Detector, Source
982       
983        detector = Detector()
984        theory.detector.append(detector) 
985           
986        theory.detector[0].distance=1e+32
987        theory.source= Source()
988        theory.source.wavelength=2*math.pi/1e+32
989     
990        ## Create detector for Model 2D
991        xmax=2*theory.detector[0].distance*math.atan(\
992                            qmax/(4*math.pi/theory.source.wavelength))
993       
994        theory.detector[0].pixel_size.x= xmax/(qstep/2-0.5)
995        theory.detector[0].pixel_size.y= xmax/(qstep/2-0.5)
996        theory.detector[0].beam_center.x= qmax
997        theory.detector[0].beam_center.y= qmax
998        ## create x_bins and y_bins of the model 2D
999        distance   = theory.detector[0].distance
1000        pixel      = qstep/2-1
1001        theta      = pixel / distance / qstep#100.0
1002        wavelength = theory.source.wavelength
1003        pixel_width_x = theory.detector[0].pixel_size.x
1004        pixel_width_y = theory.detector[0].pixel_size.y
1005        center_x      = theory.detector[0].beam_center.x/pixel_width_x
1006        center_y      = theory.detector[0].beam_center.y/pixel_width_y
1007       
1008       
1009        size_x, size_y= numpy.shape(theory.data)
1010        for i_x in range(size_x):
1011            theta = (i_x-center_x)*pixel_width_x / distance
1012            qx = 4.0*math.pi/wavelength * math.tan(theta/2.0)
1013            theory.x_bins.append(qx)   
1014        for i_y in range(size_y):
1015            theta = (i_y-center_y)*pixel_width_y / distance
1016            qy =4.0*math.pi/wavelength * math.tan(theta/2.0)
1017            theory.y_bins.append(qy)
1018           
1019        theory.group_id ="Model"
1020        theory.id ="Model"
1021        ## determine plot boundaries
1022        theory.xmin= -qmax
1023        theory.xmax= qmax
1024        theory.ymin= -qmax
1025        theory.ymax= qmax
1026       
1027       
1028    def _get_plotting_info(self, data=None):
1029        """
1030            get plotting info from data if data !=None
1031            else use some default
1032        """
1033        my_info = PlotInfo()
1034        if data !=None:
1035            if hasattr(data,"info"):
1036                x_name, x_units = data.get_xaxis() 
1037                y_name, y_units = data.get_yaxis() 
1038               
1039                my_info._xunit = x_units
1040                my_info._xaxis = x_name
1041                my_info._yunit = y_units
1042                my_info._yaxis = y_name
1043               
1044            my_info.title= data.name
1045            if hasattr(data, "info"):
1046                my_info.info= data.info
1047            if hasattr(data, "group_id"):
1048                my_info.group_id= data.group_id
1049       
1050        return my_info
1051               
1052               
1053    def _complete1D(self, x,y, elapsed,model,data=None):
1054        """
1055            Complete plotting 1D data
1056        """ 
1057       
1058        try:
1059            new_plot = Theory1D(x=x, y=y)
1060            my_info = self._get_plotting_info( data)
1061            new_plot.name = model.name
1062            new_plot.id = my_info.id
1063            new_plot.group_id = my_info.group_id
1064           
1065            new_plot.xaxis( my_info._xaxis,  my_info._xunit)
1066            new_plot.yaxis( my_info._yaxis, my_info._yunit)
1067            if data!=None:
1068                if new_plot.id == data.id:
1069                    new_plot.id += "Model"
1070                new_plot.is_data =False 
1071           
1072           
1073            title= new_plot.name
1074           
1075            # Pass the reset flag to let the plotting event handler
1076            # know that we are replacing the whole plot
1077            if title== None:
1078                title = "Analytical model 1D "
1079            if data ==None:
1080                wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1081                             title= str(title), reset=True ))
1082            else:
1083                wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1084                             title= str(title)))
1085            msg = "Plot 1D  complete !"
1086            wx.PostEvent( self.parent, StatusEvent( status= msg , type="stop" ))
1087        except:
1088            msg= " Error occurred when drawing %s Model 1D: "%new_plot.name
1089            msg+= " %s"%sys.exc_value
1090            wx.PostEvent( self.parent, StatusEvent(status= msg, type="stop"  ))
1091            return 
1092                 
1093   
1094       
1095    def _update2D(self, output,time=None):
1096        """
1097            Update the output of plotting model
1098        """
1099        wx.PostEvent(self.parent, StatusEvent(status="Plot \
1100        #updating ... ",type="update"))
1101        self.calc_thread.ready(0.01)
1102       
1103       
1104    def _complete2D(self, image,data, model,  elapsed,qmin, qmax,qstep=DEFAULT_NPTS):
1105        """
1106            Complete get the result of modelthread and create model 2D
1107            that can be plot.
1108        """
1109     
1110   
1111        err_image = numpy.zeros(numpy.shape(image))
1112       
1113        theory= Data2D(image= image , err_image= err_image)
1114        theory.name= model.name
1115       
1116        if data ==None:
1117            self._fill_default_model2D(theory= theory, qmax=qmax,qstep=qstep, qmin= qmin)
1118       
1119        else:
1120            theory.id= data.id+"Model"
1121            theory.group_id= data.name+"Model"
1122            theory.x_bins= data.x_bins
1123            theory.y_bins= data.y_bins
1124            theory.detector= data.detector
1125            theory.source= data.source
1126            theory.is_data =False 
1127            ## plot boundaries
1128            theory.ymin= data.ymin
1129            theory.ymax= data.ymax
1130            theory.xmin= data.xmin
1131            theory.xmax= data.xmax
1132     
1133       
1134        ## plot
1135        wx.PostEvent(self.parent, NewPlotEvent(plot=theory,
1136                         title="Analytical model 2D ", reset=True ))
1137        msg = "Plot 2D complete !"
1138        wx.PostEvent( self.parent, StatusEvent( status= msg , type="stop" ))
1139         
1140    def _on_data_error(self, event):
1141        """
1142            receives and event from plotting plu-gins to store the data name and
1143            their errors of y coordinates for 1Data hide and show error
1144        """
1145        self.err_dy= event.err_dy
1146       
1147         
1148    def _draw_model2D(self,model,data=None,description=None, enable2D=False,
1149                      qmin=DEFAULT_QMIN, qmax=DEFAULT_QMAX, qstep=DEFAULT_NPTS):
1150        """
1151            draw model in 2D
1152            @param model: instance of the model to draw
1153            @param description: the description of the model
1154            @param enable2D: when True allows to draw model 2D
1155            @param qmin: the minimum value to  draw model 2D
1156            @param qmax: the maximum value to draw model 2D
1157            @param qstep: the number of division of Qx and Qy of the model to draw
1158           
1159        """
1160        x=  numpy.linspace(start= -1*qmax,
1161                               stop= qmax,
1162                               num= qstep,
1163                               endpoint=True ) 
1164        y = numpy.linspace(start= -1*qmax,
1165                               stop= qmax,
1166                               num= qstep,
1167                               endpoint=True )
1168        ## use data info instead
1169        if data !=None:
1170            ## check if data2D to plot
1171            if hasattr(data, "x_bins"):
1172                enable2D = True
1173                x= data.x_bins
1174                y= data.y_bins
1175           
1176        if not enable2D:
1177            return
1178        try:
1179            from model_thread import Calc2D
1180            ## If a thread is already started, stop it
1181            if self.calc_2D != None and self.calc_2D.isrunning():
1182                self.calc_2D.stop()
1183            self.calc_2D = Calc2D(  x= x,
1184                                    y= y,
1185                                    model= model, 
1186                                    data = data,
1187                                    qmin= qmin,
1188                                    qmax= qmax,
1189                                    qstep= qstep,
1190                                    completefn= self._complete2D,
1191                                    updatefn= self._update2D )
1192            self.calc_2D.queue()
1193           
1194        except:
1195            raise
1196            #msg= " Error occurred when drawing %s Model 2D: "%model.name
1197            #msg+= " %s"%sys.exc_value
1198            #wx.PostEvent( self.parent, StatusEvent(status= msg ))
1199            #return 
1200   
1201    def _draw_model1D(self, model, data=None, smearer= None,
1202                qmin=DEFAULT_QMIN, qmax=DEFAULT_QMAX, qstep= DEFAULT_NPTS,enable1D= True):
1203        """
1204            Draw model 1D from loaded data1D
1205            @param data: loaded data
1206            @param model: the model to plot
1207        """
1208         
1209        x=  numpy.linspace(start= qmin,
1210                           stop= qmax,
1211                           num= qstep,
1212                           endpoint=True
1213                           )
1214        if data!=None:
1215            ## check for data2D
1216            if hasattr(data,"x_bins"):
1217                return
1218            x = data.x
1219            if qmin == DEFAULT_QMIN :
1220                qmin = min(data.x)
1221            if qmax == DEFAULT_QMAX:
1222                qmax = max(data.x) 
1223           
1224       
1225        if not enable1D:
1226            return
1227   
1228        try:
1229            from model_thread import Calc1D
1230            ## If a thread is already started, stop it
1231            if self.calc_1D!= None and self.calc_1D.isrunning():
1232                self.calc_1D.stop()
1233            self.calc_1D= Calc1D( x= x,
1234                                  data = data,
1235                                  model= model, 
1236                                  qmin = qmin,
1237                                  qmax = qmax,
1238                                  smearer = smearer,
1239                                  completefn = self._complete1D,
1240                                  updatefn = self._update1D  )
1241            self.calc_1D.queue()
1242           
1243        except:
1244            msg= " Error occurred when drawing %s Model 1D: "%model.name
1245            msg+= " %s"%sys.exc_value
1246            wx.PostEvent( self.parent, StatusEvent(status= msg ))
1247            return 
1248           
1249
1250def profile(fn, *args, **kw):
1251    import cProfile, pstats, os
1252    global call_result
1253    def call():
1254        global call_result
1255        call_result = fn(*args, **kw)
1256    cProfile.runctx('call()', dict(call=call), {}, 'profile.out')
1257    stats = pstats.Stats('profile.out')
1258    stats.sort_stats('time')
1259    #stats.sort_stats('calls')
1260    stats.print_stats()
1261    os.unlink('profile.out')
1262    return call_result
1263if __name__ == "__main__":
1264    i = Plugin()
1265   
1266   
1267   
1268   
Note: See TracBrowser for help on using the repository browser.