source: sasview/sansview/perspectives/fitting/fitting.py @ 156a0b2

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

This fixes one of Fit problem on MAC.

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