source: sasview/sansview/perspectives/fitting/fitting.py @ 509af3d

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

refactore fitpanel and removing the bug introduce while changing add fitpage use case

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