source: sasview/sansview/perspectives/fitting/fitting.py @ 8e87ece

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

refactore _onSelect function

  • 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
15
16from copy import deepcopy
17from DataLoader.data_info import Data1D as LoaderData1D
18from danse.common.plottools.PlotPanel import PlotPanel
19
20from sans.guiframe.dataFitting import Data2D
21from sans.guiframe.dataFitting import Data1D
22from sans.guiframe.dataFitting import Theory1D
23from sans.guiframe import dataFitting
24from sans.guiframe.utils import format_number
25
26from sans.guicomm.events import NewPlotEvent, StatusEvent 
27from sans.guicomm.events import EVT_SLICER_PANEL,ERR_DATA,EVT_REMOVE_DATA
28from sans.guicomm.events import EVT_SLICER_PARS_UPDATE
29
30from sans.fit.AbstractFitEngine import Model
31from sans.fit.AbstractFitEngine import FitAbort
32
33
34from fitproblem import FitProblem
35from fitpanel import FitPanel
36from fit_thread import FitThread
37import models
38import fitpage
39
40DEFAULT_BEAM = 0.005
41DEFAULT_QMIN = 0.001
42DEFAULT_QMAX = 0.13
43DEFAULT_NPTS = 50
44
45(PageInfoEvent, EVT_PAGE_INFO)   = wx.lib.newevent.NewEvent()
46
47
48class PlotInfo:
49    """
50        store some plotting field
51    """
52    _xunit = 'A^{-1}'
53    _xaxis= "\\rm{Q}"
54    _yunit = "cm^{-1}"
55    _yaxis= "\\rm{Intensity} "
56    id = "Model"
57    group_id = "Model"
58    title= None
59    info= None
60   
61   
62class Plugin:
63    """
64        Fitting plugin is used to perform fit
65    """
66    def __init__(self):
67        ## Plug-in name
68        self.sub_menu = "Fitting"
69       
70        ## Reference to the parent window
71        self.parent = None
72        #Provide list of models existing in the application
73        self.menu_mng = models.ModelManager()
74        ## List of panels for the simulation perspective (names)
75        self.perspective = []
76        #list of panel to send to guiframe
77        self.mypanels=[]
78        # reference to the current running thread
79        self.calc_2D= None
80        self.calc_1D= None
81        self.calc_fit= None
82       
83        # Start with a good default
84        self.elapsed = 0.022
85        # the type of optimizer selected, park or scipy
86        self.fitter  = None
87        #let fit ready
88        self.fitproblem_count = None
89        #Flag to let the plug-in know that it is running stand alone
90        self.standalone=True
91        ## dictionary of page closed and id
92        self.closed_page_dict ={}
93        ## Fit engine
94        self._fit_engine = 'scipy'
95        #List of selected data
96        self.selected_data_list=[]
97        ## list of slicer panel created to display slicer parameters and results
98        self.slicer_panels=[]
99        # Log startup
100        logging.info("Fitting plug-in started")   
101        # model 2D view
102        self.model2D_id=None
103        #keep reference of the simultaneous fit page
104        self.sim_page=None
105        #dictionary containing data name and error on dy of that data
106        self.err_dy = {}
107       
108   
109       
110    def populate_menu(self, id, owner):
111        """
112            Create a menu for the Fitting plug-in
113            @param id: id to create a menu
114            @param owner: owner of menu
115            @ return : list of information to populate the main menu
116        """
117        #Menu for fitting
118        self.menu1 = wx.Menu()
119       
120        #Set park engine
121        id3 = wx.NewId()
122        scipy_help= "Scipy Engine: Perform Simple fit. More in Help window...."
123        self.menu1.AppendCheckItem(id3, "Simple Fit  [Scipy]",scipy_help) 
124        wx.EVT_MENU(owner, id3,  self._onset_engine_scipy)
125       
126        id3 = wx.NewId()
127        park_help = "Park Engine: Perform Complex fit. More in Help window...."
128        self.menu1.AppendCheckItem(id3, "Complex Fit  [Park]",park_help) 
129        wx.EVT_MENU(owner, id3,  self._onset_engine_park)
130       
131        self.menu1.FindItemByPosition(0).Check(True)
132        self.menu1.FindItemByPosition(1).Check(False)
133           
134        self.menu1.AppendSeparator()
135        id1 = wx.NewId()
136        simul_help = "Allow to edit fit engine with multiple model and data"
137        self.menu1.Append(id1, '&Simultaneous Page',simul_help)
138        wx.EVT_MENU(owner, id1, self.on_add_sim_page)
139       
140        #menu for model
141        menu2 = wx.Menu()
142        self.menu_mng.populate_menu(menu2, owner)
143        id2 = wx.NewId()
144        owner.Bind(models.EVT_MODEL,self._on_model_menu)
145     
146        self.fit_panel.set_owner(owner)
147        self.fit_panel.set_model_list(self.menu_mng.get_model_list())
148        owner.Bind(fitpage.EVT_MODEL_BOX,self._on_model_panel)
149     
150        #create  menubar items
151        return [(id, self.menu1, "Fitting")]
152               
153    def on_add_sim_page(self, event):
154        """
155            Create a page to access simultaneous fit option
156        """
157        Plugin.on_perspective(self,event=event)
158        if self.sim_page !=None:
159            msg= "Simultaneous Fit page already opened"
160            wx.PostEvent(self.parent, StatusEvent(status= msg))
161            return 
162       
163        self.sim_page= self.fit_panel.add_sim_page()
164       
165    def help(self, evt):
166        """
167            Show a general help dialog.
168            TODO: replace the text with a nice image
169        """
170       
171        from help_panel import  HelpWindow
172        frame = HelpWindow(None, -1, 'HelpWindow')   
173        frame.Show(True)
174       
175       
176    def get_context_menu(self, graph=None):
177        """
178            Get the context menu items available for P(r).them allow fitting option
179            for Data2D and Data1D only.
180           
181            @param graph: the Graph object to which we attach the context menu
182            @return: a list of menu items with call-back function
183            @note: if Data1D was generated from Theory1D 
184                    the fitting option is not allowed
185        """
186        self.graph = graph
187        fit_option = "Select data for fitting"
188        fit_hint =  "Dialog with fitting parameters "
189       
190        for item in graph.plottables:
191            if item.__class__.__name__ is "Data2D":
192               
193                if hasattr(item,"is_data"):
194                    if item.is_data:
195                        return [[fit_option, fit_hint, self._onSelect]]
196                    else:
197                        return [] 
198                return [[fit_option, fit_hint, self._onSelect]]
199            else:
200                if item.name==graph.selected_plottable :
201                    if item.name !="$I_{obs}(q)$" and item.name !="$P_{fit}(r)$":
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=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        if not issubclass(data.__class__, LoaderData1D):
567            raise ValueError, "create_fittable_data1D expects Data1D"
568        #get the appropriate dy
569        dy = deepcopy(data.dy)
570        if len(self.err_dy) > 0:
571            if data.name in  self.err_dy.iterkeys():
572                dy = self.err_dy[data.name]   
573                print "err_dy ====", dy
574        if data.dy is None or data.dy == []:
575            dy = numpy.zeros(len(data.y))
576        print "dy ====", dy
577        if data.__class__.__name__ == "Theory1D":
578           
579            new_data = self.copy_data(data, dy)
580            new_data.group_id += "data1D"
581            new_data.id +="data1D"
582            new_data.is_data = False
583            title = new_data.name
584            title = 'Data created from Theory'
585            wx.PostEvent(self.parent, NewPlotEvent(plot=new_data,
586                                                    title=str(title),
587                                                   reset=True))
588        else:
589            new_data = self.copy_data(data, dy) 
590            new_data.is_data = True
591        return new_data
592           
593    def add_fit_page(self, data):
594        """
595            given a data, ask to the fitting panel to create a new fitting page,
596            get this page and store it into the page_finder of this plug-in
597        """
598        try:
599            page = self.fit_panel.add_fit_page(data)
600            # add data associated to the page created
601            if page != None: 
602                page.set_data(data) 
603                #create a fitproblem storing all link to data,model,page creation
604                if not page in self.page_finder.keys():
605                    self.page_finder[page]= FitProblem()
606                ## item is almost the same as data but contains
607                ## axis info for plotting
608                #self.page_finder[page].add_plotted_data(item)
609                self.page_finder[page].add_fit_data(data)
610
611                wx.PostEvent(self.parent, StatusEvent(status="Page Created"))
612            else:
613                wx.PostEvent(self.parent, StatusEvent(status="Page was already Created"))
614        except:
615            raise
616            #wx.PostEvent(self.parent, StatusEvent(status="Creating Fit page: %s"\
617            #%sys.exc_value))
618            return
619   
620    def _onEVT_SLICER_PANEL(self, event):
621        """
622            receive and event telling to update a panel with a name starting with
623            event.panel_name. this method update slicer panel for a given interactor.
624            @param event: contains type of slicer , paramaters for updating the panel
625            and panel_name to find the slicer 's panel concerned.
626        """
627        for item in self.parent.panels:
628            if self.parent.panels[item].window_caption.startswith(event.panel_name):
629                self.parent.panels[item].set_slicer(event.type, event.params)
630               
631        self.parent._mgr.Update()
632   
633             
634    def _closed_fitpage(self, event):   
635        """
636            request fitpanel to close a given page when its unique data is removed
637            from the plot
638        """   
639        self.fit_panel.close_page_with_data(event.data) 
640       
641    def _add_page_onmenu(self, name,fitproblem=None):
642        """
643            Add name of a closed page of fitpanel in a menu
644        """
645        list = self.menu1.GetMenuItems()
646        for item in list:
647            if name == item.GetItemLabel():
648                self.closed_page_dict[name][1] = fitproblem
649               
650        if not name in self.closed_page_dict.keys():   
651            # Post paramters
652            event_id = wx.NewId()
653            self.menu1.Append(event_id, name, "Show %s fit panel" % name)
654            self.closed_page_dict[name]= [event_id, fitproblem]
655            wx.EVT_MENU(self.parent,event_id,  self._open_closed_page)
656       
657       
658    def _open_closed_page(self, event):   
659        """
660            reopen a closed page
661        """
662        for name, value in self.closed_page_dict.iteritems():
663            if event.GetId() in value:
664                id,fitproblem = value
665                if name !="Model":
666                    data= fitproblem.get_fit_data()
667                    page = self.fit_panel.add_fit_page(data= data,reset=True)
668                    if fitproblem != None:
669                        self.page_finder[page]=fitproblem
670                        if self.sim_page != None:
671                            self.sim_page.draw_page()
672                           
673                else:
674                    model = fitproblem
675                    self.fit_panel.add_model_page(model=model, topmenu=True,
676                                                  reset= True)
677                    break
678       
679       
680    def _reset_schedule_problem(self, value=0):
681        """
682             unschedule or schedule all fitproblem to be fit
683        """
684        for page, fitproblem in self.page_finder.iteritems():
685            fitproblem.schedule_tofit(value)
686           
687    def _fit_helper(self,pars,value, id, title="Single Fit " ):
688        """
689            helper for fitting
690        """
691        metadata = value.get_fit_data()
692        model = value.get_model()
693        smearer = value.get_smearer()
694        qmin , qmax = value.get_range()
695        self.fit_id =id
696        #Create list of parameters for fitting used
697        templist=[]
698       
699        try:
700            #Extra list of parameters and their constraints
701            listOfConstraint= []
702           
703            param = value.get_model_param()
704            if len(param)>0:
705                for item in param:
706                    ## check if constraint
707                    if item[0] !=None and item[1] != None:
708                        listOfConstraint.append((item[0],item[1]))
709                   
710            #Do the single fit
711            self.fitter.set_model(model, self.fit_id,
712                                   pars,constraints = listOfConstraint)
713           
714            self.fitter.set_data(data=metadata,Uid=self.fit_id,
715                                 smearer=smearer,qmin= qmin,qmax=qmax )
716            #print "self.fitter.set_data"
717            self.fitter.select_problem_for_fit(Uid= self.fit_id,
718                                               value= value.get_scheduled())
719            value.clear_model_param()
720        except:
721            msg= title +" error: %s" % sys.exc_value
722            wx.PostEvent(self.parent, StatusEvent(status= msg, type="stop"))
723            return
724       
725    def _onSelect(self,event):
726        """
727            when Select data to fit a new page is created .Its reference is
728            added to self.page_finder
729        """
730        self.panel = event.GetEventObject()
731        Plugin.on_perspective(self,event=event)
732        for plottable in self.panel.graph.plottables:
733            if issubclass(plottable.__class__, LoaderData1D):
734                if plottable.name == self.panel.graph.selected_plottable:
735                    data = self.create_fittable_data1D(data=plottable)
736                    self.add_fit_page(data=data)
737                    return
738            else:
739                data = self.create_fittable_data2D(data=plottable)
740                self.add_fit_page(data=data)
741
742    def _updateFit(self):
743        """
744            Is called when values of result are available
745        """
746        ##Sending a progess message to the status bar
747        wx.PostEvent(self.parent, StatusEvent(status="Computing..."))
748           
749    def _single_fit_completed(self,result,pars,cpage, elapsed=None):
750        """
751            Display fit result on one page of the notebook.
752            @param result: result of fit
753            @param pars: list of names of parameters fitted
754            @param current_pg: the page where information will be displayed
755            @param qmin: the minimum value of x to replot the model
756            @param qmax: the maximum value of x to replot model
757         
758        """     
759        try:
760            if result ==None:
761                msg= "Simple Fitting Stop !!!"
762                wx.PostEvent(self.parent, StatusEvent(status=msg,type="stop"))
763                return
764            if not numpy.isfinite(result.fitness) or numpy.any(result.pvec ==None )or not numpy.all(numpy.isfinite(result.pvec) ):
765                msg= "Single Fitting did not converge!!!"
766                wx.PostEvent(self.parent, StatusEvent(status=msg,type="stop"))
767                return
768            for page, value in self.page_finder.iteritems():
769                if page==cpage :
770                    model= value.get_model()
771                    break
772            param_name = []
773            i = 0
774            for name in pars:
775                param_name.append(name)
776
777            cpage.onsetValues(result.fitness,param_name, result.pvec,result.stderr)
778           
779        except:
780            msg= "Single Fit completed but Following error occurred:%s"% sys.exc_value
781            wx.PostEvent(self.parent, StatusEvent(status=msg,type="stop"))
782            return
783       
784       
785    def _simul_fit_completed(self,result,pars=None,cpage=None, elapsed=None):
786        """
787            Parameter estimation completed,
788            display the results to the user
789            @param alpha: estimated best alpha
790            @param elapsed: computation time
791        """
792        ## fit more than 1 model at the same time
793        try:
794            msg = "" 
795            if result ==None:
796                msg= "Complex Fitting Stop !!!"
797                wx.PostEvent(self.parent, StatusEvent(status=msg,type="stop"))
798                return
799            if not numpy.isfinite(result.fitness) or numpy.any(result.pvec ==None )or not numpy.all(numpy.isfinite(result.pvec) ):
800                msg= "Single Fitting did not converge!!!"
801                wx.PostEvent(self.parent, StatusEvent(status=msg,type="stop"))
802                return
803             
804            for page, value in self.page_finder.iteritems():
805                """
806                if format_number(result.fitness) == page.get_chi2():
807                    #ToDo: Compare parameter inputs with outputs too.
808                    wx.PostEvent(self.parent, StatusEvent(status="%s " % msg))
809                    break     
810                """             
811                if value.get_scheduled()==1:
812                    model = value.get_model()
813                    data =  value.get_fit_data()
814                    small_param_name = []
815                    small_out = []
816                    small_cov = []
817                    i = 0
818                    #Separate result in to data corresponding to each page
819                    for p in result.parameters:
820                        model_name,param_name = self.split_string(p.name) 
821                        if model.name == model_name:
822                            p_name= model.name+"."+param_name
823                            if p.name == p_name:     
824                                if p.value != None and numpy.isfinite(p.value):
825                                    small_out.append(p.value )
826                                    small_param_name.append(param_name)
827                                    small_cov.append(p.stderr)
828
829                    # Display result on each page
830                    page.onsetValues(result.fitness, small_param_name,small_out,small_cov)
831        except:
832             msg= "Simultaneous Fit completed"
833             msg +=" but Following error occurred:%s"%sys.exc_value
834             wx.PostEvent(self.parent, StatusEvent(status=msg,type="stop"))
835             return 
836             
837                           
838       
839    def _on_show_panel(self, event):
840        print "_on_show_panel: fitting"
841     
842     
843    def _onset_engine_park(self,event):
844        """
845            set engine to park
846        """
847        Plugin.on_perspective(self,event=event)
848        self._on_change_engine('park')
849       
850       
851    def _onset_engine_scipy(self,event):
852        """
853            set engine to scipy
854        """
855        self._on_change_engine('scipy')
856       
857    def _on_slicer_event(self, event):
858        """
859            Receive a panel as event and send it to guiframe
860            @param event: event containing a panel
861        """
862       
863        if event.panel!=None:
864            new_panel = event.panel
865            self.slicer_panels.append(event.panel)
866            # Set group ID if available
867            event_id = self.parent.popup_panel(new_panel)
868            #self.menu3.Append(event_id, new_panel.window_caption,
869            #                 "Show %s plot panel" % new_panel.window_caption)
870            # Set UID to allow us to reference the panel later
871         
872            new_panel.uid = event_id
873            self.mypanels.append(new_panel) 
874        return 
875   
876    def _onclearslicer(self, event):
877        """
878            Clear the boxslicer when close the panel associate with this slicer
879        """
880        name =event.GetPane().caption
881   
882        for panel in self.slicer_panels:
883            if panel.window_caption==name:
884               
885                for item in self.parent.panels:
886                    if hasattr(self.parent.panels[item],"uid"):
887                        if self.parent.panels[item].uid ==panel.base.uid:
888                            self.parent.panels[item].onClearSlicer(event)
889                            self.parent._mgr.Update()
890                            break 
891                break
892       
893       
894       
895       
896    def _return_engine_type(self):
897        """
898            return the current type of engine
899        """
900        return self._fit_engine
901     
902     
903    def _on_change_engine(self, engine='park'):
904        """
905            Allow to select the type of engine to perform fit
906            @param engine: the key work of the engine
907        """
908       
909        ## saving fit engine name
910        self._fit_engine = engine
911        ## change menu item state
912        if engine=="park":
913            self.menu1.FindItemByPosition(0).Check(False)
914            self.menu1.FindItemByPosition(1).Check(True)
915        else:
916            self.menu1.FindItemByPosition(0).Check(True)
917            self.menu1.FindItemByPosition(1).Check(False)
918           
919        ## post a message to status bar
920        wx.PostEvent(self.parent, StatusEvent(status="Engine set to: %s" % self._fit_engine))
921   
922        ## Bind every open fit page with a newevent to know the current fitting engine
923        import fitpage
924        event= fitpage.FitterTypeEvent()
925        event.type = self._fit_engine
926        for key in self.page_finder.keys():
927            wx.PostEvent(key, event)
928       
929   
930    def _on_model_panel(self, evt):
931        """
932            react to model selection on any combo box or model menu.plot the model 
933            @param evt: wx.combobox event
934        """
935        model = evt.model
936       
937        if model == None:
938            return
939        model.origin_name = model.name
940        self.current_pg = self.fit_panel.get_current_page() 
941        ## make sure nothing is done on self.sim_page
942        ## example trying to call set_panel on self.sim_page
943        if self.current_pg != self.sim_page :
944           
945            if self.page_finder[self.current_pg].get_model()== None :
946               
947                model.name = "M"+str(self.index_model)
948                self.index_model += 1 
949            else:
950                model.name= self.page_finder[self.current_pg].get_model().name
951               
952            data = self.page_finder[self.current_pg].get_fit_data()
953           
954            # save the name containing the data name with the appropriate model
955            self.page_finder[self.current_pg].set_model(model)
956            qmin, qmax= self.current_pg.get_range()
957            self.page_finder[self.current_pg].set_range(qmin=qmin, qmax=qmax)
958            smearer=  self.page_finder[self.current_pg].get_smearer()
959            # save model name
960            self.draw_model( model=model, smearer=smearer, 
961                             data=data, qmin=qmin, qmax=qmax)
962           
963            if self.sim_page!=None:
964                self.sim_page.draw_page()
965       
966    def _on_model_menu(self, evt):
967        """
968            Plot a theory from a model selected from the menu
969            @param evt: wx.menu event
970        """
971        model = evt.model
972        Plugin.on_perspective(self,event=evt)
973        # Create a model page. If a new page is created, the model
974        # will be plotted automatically. If a page already exists,
975        # the content will be updated and the plot refreshed
976        self.fit_panel.add_model_page(model,topmenu=True)
977   
978   
979   
980   
981    def _update1D(self,x, output):
982        """
983            Update the output of plotting model 1D
984        """
985        wx.PostEvent(self.parent, StatusEvent(status="Plot \
986        #updating ... ",type="update"))
987        self.ready_fit()
988        #self.calc_thread.ready(0.01)
989   
990   
991    def _fill_default_model2D(self, theory, qmax,qstep, qmin=None):
992        """
993            fill Data2D with default value
994            @param theory: Data2D to fill
995        """
996        from DataLoader.data_info import Detector, Source
997       
998        detector = Detector()
999        theory.detector.append(detector)         
1000        theory.source= Source()
1001       
1002        ## Default values   
1003        theory.detector[0].distance= 8000   # mm       
1004        theory.source.wavelength= 6         # A     
1005        theory.detector[0].pixel_size.x= 5  # mm
1006        theory.detector[0].pixel_size.y= 5  # mm
1007       
1008        theory.detector[0].beam_center.x= qmax
1009        theory.detector[0].beam_center.y= qmax
1010       
1011       
1012        ## create x_bins and y_bins of the model 2D
1013        pixel_width_x = theory.detector[0].pixel_size.x
1014        pixel_width_y = theory.detector[0].pixel_size.y
1015        center_x      = theory.detector[0].beam_center.x/pixel_width_x
1016        center_y      = theory.detector[0].beam_center.y/pixel_width_y
1017
1018        # theory default: assume the beam center is located at the center of sqr detector
1019        xmax = qmax
1020        xmin = -qmax
1021        ymax = qmax
1022        ymin = -qmax
1023       
1024        x=  numpy.linspace(start= -1*qmax,
1025                               stop= qmax,
1026                               num= qstep,
1027                               endpoint=True ) 
1028        y = numpy.linspace(start= -1*qmax,
1029                               stop= qmax,
1030                               num= qstep,
1031                               endpoint=True )
1032         
1033        ## use data info instead
1034        new_x = numpy.tile(x, (len(y),1))
1035        new_y = numpy.tile(y, (len(x),1))
1036        new_y = new_y.swapaxes(0,1)
1037       
1038        # all data reuire now in 1d array
1039        qx_data = new_x.flatten()
1040        qy_data = new_y.flatten()
1041       
1042        q_data = numpy.sqrt(qx_data*qx_data+qy_data*qy_data)
1043        # set all True (standing for unmasked) as default
1044        mask    = numpy.ones(len(qx_data), dtype = bool)
1045       
1046        # calculate the range of qx and qy: this way, it is a little more independent
1047        x_size = xmax- xmin
1048        y_size = ymax -ymin
1049       
1050        # store x and y bin centers in q space
1051        x_bins  = x
1052        y_bins  = y
1053        # bin size: x- & y-directions
1054        xstep = x_size/len(x_bins-1)
1055        ystep = y_size/len(y_bins-1)
1056       
1057        #theory.data = numpy.zeros(len(mask))
1058        theory.err_data = numpy.zeros(len(mask))
1059        theory.qx_data = qx_data
1060        theory.qy_data = qy_data 
1061        theory.q_data = q_data
1062        theory.mask = mask           
1063        theory.x_bins = x_bins 
1064        theory.y_bins = y_bins   
1065       
1066        # max and min taking account of the bin sizes
1067        theory.xmin= xmin
1068        theory.xmax= xmax
1069        theory.ymin= ymin
1070        theory.ymax= ymax
1071        theory.group_id ="Model"
1072        theory.id ="Model"
1073       
1074       
1075    def _get_plotting_info(self, data=None):
1076        """
1077            get plotting info from data if data !=None
1078            else use some default
1079        """
1080        my_info = PlotInfo()
1081        if data !=None:
1082            if hasattr(data,"info"):
1083                x_name, x_units = data.get_xaxis() 
1084                y_name, y_units = data.get_yaxis() 
1085               
1086                my_info._xunit = x_units
1087                my_info._xaxis = x_name
1088                my_info._yunit = y_units
1089                my_info._yaxis = y_name
1090               
1091            my_info.title= data.name
1092            if hasattr(data, "info"):
1093                my_info.info= data.info
1094            if hasattr(data, "group_id"):
1095                my_info.group_id= data.group_id
1096       
1097        return my_info
1098               
1099               
1100    def _complete1D(self, x,y, elapsed,model,data=None):
1101        """
1102            Complete plotting 1D data
1103        """ 
1104        try:
1105            new_plot = Theory1D(x=x, y=y)
1106            my_info = self._get_plotting_info( data)
1107            new_plot.name = model.name
1108            new_plot.id = my_info.id
1109            new_plot.group_id = my_info.group_id
1110           
1111            new_plot.xaxis( my_info._xaxis,  my_info._xunit)
1112            new_plot.yaxis( my_info._yaxis, my_info._yunit)
1113            if data!=None:
1114                if new_plot.id == data.id:
1115                    new_plot.id += "Model"
1116                new_plot.is_data =False 
1117           
1118            title= new_plot.name
1119            #new_plot.perspective = self.get_perspective()
1120            # Pass the reset flag to let the plotting event handler
1121            # know that we are replacing the whole plot
1122            if title== None:
1123                title = "Analytical model 1D "
1124            if data ==None:
1125                wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1126                             title=str(title), reset=True))
1127            else:
1128                wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1129                             title= str(title)))
1130            msg = "Plot 1D  complete !"
1131            wx.PostEvent( self.parent, StatusEvent( status= msg , type="stop" ))
1132        except:
1133            msg= " Error occurred when drawing %s Model 1D: "%new_plot.name
1134            msg+= " %s"%sys.exc_value
1135            wx.PostEvent( self.parent, StatusEvent(status= msg, type="stop"  ))
1136            return 
1137                 
1138    def _update2D(self, output,time=None):
1139        """
1140            Update the output of plotting model
1141        """
1142        wx.PostEvent(self.parent, StatusEvent(status="Plot \
1143        #updating ... ",type="update"))
1144        self.ready_fit()
1145        #self.calc_thread.ready(0.01)
1146       
1147       
1148    def _complete2D(self, image,data, model,  elapsed,qmin, qmax,qstep=DEFAULT_NPTS):
1149        """
1150            Complete get the result of modelthread and create model 2D
1151            that can be plot.
1152        """
1153        err_image = numpy.zeros(numpy.shape(image))
1154       
1155        theory= Data2D(image= image , err_image= err_image)
1156        theory.name= model.name
1157       
1158        if data ==None:
1159            self._fill_default_model2D(theory= theory, qmax=qmax,qstep=qstep, qmin= qmin)
1160       
1161        else:
1162            theory.id= data.id+"Model"
1163            theory.group_id= data.name+"Model"
1164            theory.x_bins= data.x_bins
1165            theory.y_bins= data.y_bins
1166            theory.detector= data.detector
1167            theory.source= data.source
1168            theory.is_data =False 
1169            theory.qx_data = data.qx_data
1170            theory.qy_data = data.qy_data
1171            theory.q_data = data.q_data
1172            theory.err_data = data.err_data
1173            theory.mask = data.mask
1174            ## plot boundaries
1175            theory.ymin= data.ymin
1176            theory.ymax= data.ymax
1177            theory.xmin= data.xmin
1178            theory.xmax= data.xmax
1179     
1180       
1181        ## plot
1182        wx.PostEvent(self.parent, NewPlotEvent(plot=theory,
1183                         title="Analytical model 2D ", reset=True ))
1184        msg = "Plot 2D complete !"
1185        wx.PostEvent( self.parent, StatusEvent( status= msg , type="stop" ))
1186         
1187    def _on_data_error(self, event):
1188        """
1189            receives and event from plotting plu-gins to store the data name and
1190            their errors of y coordinates for 1Data hide and show error
1191        """
1192        self.err_dy = event.err_dy
1193        print "receiving error dy",self.err_dy
1194         
1195    def _draw_model2D(self,model,data=None,description=None, enable2D=False,
1196                      qmin=DEFAULT_QMIN, qmax=DEFAULT_QMAX, qstep=DEFAULT_NPTS):
1197        """
1198            draw model in 2D
1199            @param model: instance of the model to draw
1200            @param description: the description of the model
1201            @param enable2D: when True allows to draw model 2D
1202            @param qmin: the minimum value to  draw model 2D
1203            @param qmax: the maximum value to draw model 2D
1204            @param qstep: the number of division of Qx and Qy of the model to draw
1205           
1206        """
1207        x=  numpy.linspace(start= -1*qmax,
1208                               stop= qmax,
1209                               num= qstep,
1210                               endpoint=True ) 
1211        y = numpy.linspace(start= -1*qmax,
1212                               stop= qmax,
1213                               num= qstep,
1214                               endpoint=True )
1215        ## use data info instead
1216        if data !=None:
1217            ## check if data2D to plot
1218            if hasattr(data, "x_bins"):
1219                enable2D = True
1220                x= data.x_bins
1221                y= data.y_bins
1222               
1223        if not enable2D:
1224            return
1225        try:
1226            from model_thread import Calc2D
1227            ## If a thread is already started, stop it
1228            if self.calc_2D != None and self.calc_2D.isrunning():
1229                self.calc_2D.stop()
1230            self.calc_2D = Calc2D(  x= x,
1231                                    y= y,
1232                                    model= model, 
1233                                    data = data,
1234                                    qmin= qmin,
1235                                    qmax= qmax,
1236                                    qstep= qstep,
1237                                    completefn= self._complete2D,
1238                                    updatefn= self._update2D )
1239            self.calc_2D.queue()
1240           
1241        except:
1242            msg= " Error occurred when drawing %s Model 2D: "%model.name
1243            msg+= " %s"%sys.exc_value
1244            wx.PostEvent( self.parent, StatusEvent(status= msg ))
1245            return 
1246   
1247    def _draw_model1D(self, model, data=None, smearer= None,
1248                qmin=DEFAULT_QMIN, qmax=DEFAULT_QMAX, qstep= DEFAULT_NPTS,enable1D= True):
1249        """
1250            Draw model 1D from loaded data1D
1251            @param data: loaded data
1252            @param model: the model to plot
1253        """
1254        x=  numpy.linspace(start= qmin,
1255                           stop= qmax,
1256                           num= qstep,
1257                           endpoint=True
1258                           )
1259        if data!=None:
1260            ## check for data2D
1261            if hasattr(data,"x_bins"):
1262                return
1263            x = data.x
1264            if qmin == DEFAULT_QMIN :
1265                qmin = min(data.x)
1266            if qmax == DEFAULT_QMAX:
1267                qmax = max(data.x) 
1268           
1269       
1270        if not enable1D:
1271            return
1272   
1273        try:
1274            from model_thread import Calc1D
1275            ## If a thread is already started, stop it
1276            if self.calc_1D!= None and self.calc_1D.isrunning():
1277                self.calc_1D.stop()
1278            self.calc_1D= Calc1D( x= x,
1279                                  data = data,
1280                                  model= model, 
1281                                  qmin = qmin,
1282                                  qmax = qmax,
1283                                  smearer = smearer,
1284                                  completefn = self._complete1D,
1285                                  updatefn = self._update1D  )
1286            self.calc_1D.queue()
1287           
1288        except:
1289            msg= " Error occurred when drawing %s Model 1D: "%model.name
1290            msg+= " %s"%sys.exc_value
1291            wx.PostEvent( self.parent, StatusEvent(status= msg ))
1292            return 
1293           
1294
1295def profile(fn, *args, **kw):
1296    import cProfile, pstats, os
1297    global call_result
1298    def call():
1299        global call_result
1300        call_result = fn(*args, **kw)
1301    cProfile.runctx('call()', dict(call=call), {}, 'profile.out')
1302    stats = pstats.Stats('profile.out')
1303    stats.sort_stats('time')
1304    #stats.sort_stats('calls')
1305    stats.print_stats()
1306    os.unlink('profile.out')
1307    return call_result
1308if __name__ == "__main__":
1309    i = Plugin()
1310   
1311   
1312   
1313   
Note: See TracBrowser for help on using the repository browser.