source: sasview/sansview/perspectives/fitting/fitting.py @ 4faf4ba

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

sld calculator implemented using elements package

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