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

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 d8a2e31 was 5612152, checked in by Mathieu Doucet <doucetm@…>, 15 years ago

sansview: modified the application to pass a welcome panel class to the gui manager instead of making it part of the fit perspective.

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