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

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

reverce code for copy_data and add check for value of source

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