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

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 6e0e2d85 was 484faf7, checked in by Gervaise Alina <gervyh@…>, 15 years ago

adding invarant panel

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