source: sasview/sansview/perspectives/fitting/fitting.py @ 9455d77

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 9455d77 was 9455d77, checked in by Gervaise Alina <gervyh@…>, 15 years ago

working on the close welcome page

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