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

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

Updated all 2D data inputs for new 2d format

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