source: sasview/sansview/perspectives/fitting/fitting.py @ 7ab35154

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

fixed not properly displaying error bars and fit_engine_types on resetting fit pages from bookmarks (also some minor fixes for mac)

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