source: sasview/sansview/perspectives/fitting/fitting.py @ 35c9d31

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

more fixes on problems of performing fitting on mac after changing param min/max

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