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

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

remove double call of draw panel in fitpanel select model

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