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

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

fixed dipspersion errors displaying wrong values

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