source: sasview/sansview/perspectives/fitting/fitting.py @ 6ef7ac5a

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

correction of chi2 value from park, cleaned unnecessary calculation, reduced mac crash by placing sleep(0.5) between layouts.

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