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

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

disable fitting for data generated for prview

  • Property mode set to 100644
File size: 50.0 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       
935        if model == None:
936            return
937        model.origin_name = model.name
938        self.current_pg = self.fit_panel.get_current_page() 
939        ## make sure nothing is done on self.sim_page
940        ## example trying to call set_panel on self.sim_page
941        if self.current_pg != self.sim_page :
942           
943            if self.page_finder[self.current_pg].get_model()== None :
944               
945                model.name = "M"+str(self.index_model)
946                self.index_model += 1 
947            else:
948                model.name= self.page_finder[self.current_pg].get_model().name
949               
950            data = self.page_finder[self.current_pg].get_fit_data()
951           
952            # save the name containing the data name with the appropriate model
953            self.page_finder[self.current_pg].set_model(model)
954            qmin, qmax= self.current_pg.get_range()
955            self.page_finder[self.current_pg].set_range(qmin=qmin, qmax=qmax)
956            smearer=  self.page_finder[self.current_pg].get_smearer()
957            # save model name
958            self.draw_model( model=model, smearer=smearer, 
959                             data=data, qmin=qmin, qmax=qmax)
960           
961            if self.sim_page!=None:
962                self.sim_page.draw_page()
963       
964    def _on_model_menu(self, evt):
965        """
966            Plot a theory from a model selected from the menu
967            @param evt: wx.menu event
968        """
969        model = evt.model
970        Plugin.on_perspective(self,event=evt)
971        # Create a model page. If a new page is created, the model
972        # will be plotted automatically. If a page already exists,
973        # the content will be updated and the plot refreshed
974        self.fit_panel.add_model_page(model,topmenu=True)
975   
976   
977   
978   
979    def _update1D(self,x, output):
980        """
981            Update the output of plotting model 1D
982        """
983        wx.PostEvent(self.parent, StatusEvent(status="Plot \
984        #updating ... ",type="update"))
985        self.ready_fit()
986        #self.calc_thread.ready(0.01)
987   
988   
989    def _fill_default_model2D(self, theory, qmax,qstep, qmin=None):
990        """
991            fill Data2D with default value
992            @param theory: Data2D to fill
993        """
994        from DataLoader.data_info import Detector, Source
995       
996        detector = Detector()
997        theory.detector.append(detector)         
998        theory.source= Source()
999       
1000        ## Default values   
1001        theory.detector[0].distance= 8000   # mm       
1002        theory.source.wavelength= 6         # A     
1003        theory.detector[0].pixel_size.x= 5  # mm
1004        theory.detector[0].pixel_size.y= 5  # mm
1005       
1006        theory.detector[0].beam_center.x= qmax
1007        theory.detector[0].beam_center.y= qmax
1008       
1009       
1010        ## create x_bins and y_bins of the model 2D
1011        pixel_width_x = theory.detector[0].pixel_size.x
1012        pixel_width_y = theory.detector[0].pixel_size.y
1013        center_x      = theory.detector[0].beam_center.x/pixel_width_x
1014        center_y      = theory.detector[0].beam_center.y/pixel_width_y
1015
1016        # theory default: assume the beam center is located at the center of sqr detector
1017        xmax = qmax
1018        xmin = -qmax
1019        ymax = qmax
1020        ymin = -qmax
1021       
1022        x=  numpy.linspace(start= -1*qmax,
1023                               stop= qmax,
1024                               num= qstep,
1025                               endpoint=True ) 
1026        y = numpy.linspace(start= -1*qmax,
1027                               stop= qmax,
1028                               num= qstep,
1029                               endpoint=True )
1030         
1031        ## use data info instead
1032        new_x = numpy.tile(x, (len(y),1))
1033        new_y = numpy.tile(y, (len(x),1))
1034        new_y = new_y.swapaxes(0,1)
1035       
1036        # all data reuire now in 1d array
1037        qx_data = new_x.flatten()
1038        qy_data = new_y.flatten()
1039       
1040        q_data = numpy.sqrt(qx_data*qx_data+qy_data*qy_data)
1041        # set all True (standing for unmasked) as default
1042        mask    = numpy.ones(len(qx_data), dtype = bool)
1043       
1044        # calculate the range of qx and qy: this way, it is a little more independent
1045        x_size = xmax- xmin
1046        y_size = ymax -ymin
1047       
1048        # store x and y bin centers in q space
1049        x_bins  = x
1050        y_bins  = y
1051        # bin size: x- & y-directions
1052        xstep = x_size/len(x_bins-1)
1053        ystep = y_size/len(y_bins-1)
1054       
1055        #theory.data = numpy.zeros(len(mask))
1056        theory.err_data = numpy.zeros(len(mask))
1057        theory.qx_data = qx_data
1058        theory.qy_data = qy_data 
1059        theory.q_data = q_data
1060        theory.mask = mask           
1061        theory.x_bins = x_bins 
1062        theory.y_bins = y_bins   
1063       
1064        # max and min taking account of the bin sizes
1065        theory.xmin= xmin
1066        theory.xmax= xmax
1067        theory.ymin= ymin
1068        theory.ymax= ymax
1069        theory.group_id ="Model"
1070        theory.id ="Model"
1071       
1072       
1073    def _get_plotting_info(self, data=None):
1074        """
1075            get plotting info from data if data !=None
1076            else use some default
1077        """
1078        my_info = PlotInfo()
1079        if data !=None:
1080            if hasattr(data,"info"):
1081                x_name, x_units = data.get_xaxis() 
1082                y_name, y_units = data.get_yaxis() 
1083               
1084                my_info._xunit = x_units
1085                my_info._xaxis = x_name
1086                my_info._yunit = y_units
1087                my_info._yaxis = y_name
1088               
1089            my_info.title= data.name
1090            if hasattr(data, "info"):
1091                my_info.info= data.info
1092            if hasattr(data, "group_id"):
1093                my_info.group_id= data.group_id
1094       
1095        return my_info
1096               
1097               
1098    def _complete1D(self, x,y, elapsed,model,data=None):
1099        """
1100            Complete plotting 1D data
1101        """ 
1102        try:
1103            new_plot = Theory1D(x=x, y=y)
1104            my_info = self._get_plotting_info( data)
1105            new_plot.name = model.name
1106            new_plot.id = my_info.id
1107            new_plot.group_id = my_info.group_id
1108           
1109            new_plot.xaxis( my_info._xaxis,  my_info._xunit)
1110            new_plot.yaxis( my_info._yaxis, my_info._yunit)
1111            if data!=None:
1112                if new_plot.id == data.id:
1113                    new_plot.id += "Model"
1114                new_plot.is_data =False 
1115           
1116            title= new_plot.name
1117            #new_plot.perspective = self.get_perspective()
1118            # Pass the reset flag to let the plotting event handler
1119            # know that we are replacing the whole plot
1120            if title== None:
1121                title = "Analytical model 1D "
1122            if data ==None:
1123                wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1124                             title=str(title), reset=True))
1125            else:
1126                wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1127                             title= str(title)))
1128            msg = "Plot 1D  complete !"
1129            wx.PostEvent( self.parent, StatusEvent( status= msg , type="stop" ))
1130        except:
1131            msg= " Error occurred when drawing %s Model 1D: "%new_plot.name
1132            msg+= " %s"%sys.exc_value
1133            wx.PostEvent( self.parent, StatusEvent(status= msg, type="stop"  ))
1134            return 
1135                 
1136    def _update2D(self, output,time=None):
1137        """
1138            Update the output of plotting model
1139        """
1140        wx.PostEvent(self.parent, StatusEvent(status="Plot \
1141        #updating ... ",type="update"))
1142        self.ready_fit()
1143        #self.calc_thread.ready(0.01)
1144       
1145       
1146    def _complete2D(self, image,data, model,  elapsed,qmin, qmax,qstep=DEFAULT_NPTS):
1147        """
1148            Complete get the result of modelthread and create model 2D
1149            that can be plot.
1150        """
1151        err_image = numpy.zeros(numpy.shape(image))
1152       
1153        theory= Data2D(image= image , err_image= err_image)
1154        theory.name= model.name
1155       
1156        if data ==None:
1157            self._fill_default_model2D(theory= theory, qmax=qmax,qstep=qstep, qmin= qmin)
1158       
1159        else:
1160            theory.id= data.id+"Model"
1161            theory.group_id= data.name+"Model"
1162            theory.x_bins= data.x_bins
1163            theory.y_bins= data.y_bins
1164            theory.detector= data.detector
1165            theory.source= data.source
1166            theory.is_data =False 
1167            theory.qx_data = data.qx_data
1168            theory.qy_data = data.qy_data
1169            theory.q_data = data.q_data
1170            theory.err_data = data.err_data
1171            theory.mask = data.mask
1172            ## plot boundaries
1173            theory.ymin= data.ymin
1174            theory.ymax= data.ymax
1175            theory.xmin= data.xmin
1176            theory.xmax= data.xmax
1177     
1178       
1179        ## plot
1180        wx.PostEvent(self.parent, NewPlotEvent(plot=theory,
1181                         title="Analytical model 2D ", reset=True ))
1182        msg = "Plot 2D complete !"
1183        wx.PostEvent( self.parent, StatusEvent( status= msg , type="stop" ))
1184         
1185    def _on_data_error(self, event):
1186        """
1187            receives and event from plotting plu-gins to store the data name and
1188            their errors of y coordinates for 1Data hide and show error
1189        """
1190        self.err_dy = event.err_dy
1191        print "receiving error dy",self.err_dy
1192         
1193    def _draw_model2D(self,model,data=None,description=None, enable2D=False,
1194                      qmin=DEFAULT_QMIN, qmax=DEFAULT_QMAX, qstep=DEFAULT_NPTS):
1195        """
1196            draw model in 2D
1197            @param model: instance of the model to draw
1198            @param description: the description of the model
1199            @param enable2D: when True allows to draw model 2D
1200            @param qmin: the minimum value to  draw model 2D
1201            @param qmax: the maximum value to draw model 2D
1202            @param qstep: the number of division of Qx and Qy of the model to draw
1203           
1204        """
1205        x=  numpy.linspace(start= -1*qmax,
1206                               stop= qmax,
1207                               num= qstep,
1208                               endpoint=True ) 
1209        y = numpy.linspace(start= -1*qmax,
1210                               stop= qmax,
1211                               num= qstep,
1212                               endpoint=True )
1213        ## use data info instead
1214        if data !=None:
1215            ## check if data2D to plot
1216            if hasattr(data, "x_bins"):
1217                enable2D = True
1218                x= data.x_bins
1219                y= data.y_bins
1220               
1221        if not enable2D:
1222            return
1223        try:
1224            from model_thread import Calc2D
1225            ## If a thread is already started, stop it
1226            if self.calc_2D != None and self.calc_2D.isrunning():
1227                self.calc_2D.stop()
1228            self.calc_2D = Calc2D(  x= x,
1229                                    y= y,
1230                                    model= model, 
1231                                    data = data,
1232                                    qmin= qmin,
1233                                    qmax= qmax,
1234                                    qstep= qstep,
1235                                    completefn= self._complete2D,
1236                                    updatefn= self._update2D )
1237            self.calc_2D.queue()
1238           
1239        except:
1240            msg= " Error occurred when drawing %s Model 2D: "%model.name
1241            msg+= " %s"%sys.exc_value
1242            wx.PostEvent( self.parent, StatusEvent(status= msg ))
1243            return 
1244   
1245    def _draw_model1D(self, model, data=None, smearer= None,
1246                qmin=DEFAULT_QMIN, qmax=DEFAULT_QMAX, qstep= DEFAULT_NPTS,enable1D= True):
1247        """
1248            Draw model 1D from loaded data1D
1249            @param data: loaded data
1250            @param model: the model to plot
1251        """
1252        x=  numpy.linspace(start= qmin,
1253                           stop= qmax,
1254                           num= qstep,
1255                           endpoint=True
1256                           )
1257        if data!=None:
1258            ## check for data2D
1259            if hasattr(data,"x_bins"):
1260                return
1261            x = data.x
1262            if qmin == DEFAULT_QMIN :
1263                qmin = min(data.x)
1264            if qmax == DEFAULT_QMAX:
1265                qmax = max(data.x) 
1266           
1267       
1268        if not enable1D:
1269            return
1270   
1271        try:
1272            from model_thread import Calc1D
1273            ## If a thread is already started, stop it
1274            if self.calc_1D!= None and self.calc_1D.isrunning():
1275                self.calc_1D.stop()
1276            self.calc_1D= Calc1D( x= x,
1277                                  data = data,
1278                                  model= model, 
1279                                  qmin = qmin,
1280                                  qmax = qmax,
1281                                  smearer = smearer,
1282                                  completefn = self._complete1D,
1283                                  updatefn = self._update1D  )
1284            self.calc_1D.queue()
1285           
1286        except:
1287            msg= " Error occurred when drawing %s Model 1D: "%model.name
1288            msg+= " %s"%sys.exc_value
1289            wx.PostEvent( self.parent, StatusEvent(status= msg ))
1290            return 
1291           
1292
1293def profile(fn, *args, **kw):
1294    import cProfile, pstats, os
1295    global call_result
1296    def call():
1297        global call_result
1298        call_result = fn(*args, **kw)
1299    cProfile.runctx('call()', dict(call=call), {}, 'profile.out')
1300    stats = pstats.Stats('profile.out')
1301    stats.sort_stats('time')
1302    #stats.sort_stats('calls')
1303    stats.print_stats()
1304    os.unlink('profile.out')
1305    return call_result
1306if __name__ == "__main__":
1307    i = Plugin()
1308   
1309   
1310   
1311   
Note: See TracBrowser for help on using the repository browser.