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

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

removed extra sizer resets and fixed bugs on mac

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