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

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

fixed fitting crash on Mac

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