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

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

poststatus event

  • Property mode set to 100644
File size: 50.5 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, info="error",
462                                                      type="stop"))
463                return 
464           
465        #Do the simultaneous fit
466        try:
467            ## If a thread is already started, stop it
468            #if self.calc_fit!= None and self.calc_fit.isrunning():
469            #    self.calc_fit.stop()
470           
471            wx.PostEvent(self.parent, StatusEvent(status="Start the computation",
472                                        curr_thread=self.calc_fit,type="start"))
473            time.sleep(0.5)
474            wx.PostEvent(self.parent, StatusEvent(status="Computing...",
475                                        curr_thread=self.calc_fit,type="progress"))
476            time.sleep(0.5)
477            ## perform single fit
478            if fitproblem_count == 1:
479                #qmin, qmax= self.current_pg.get_range()
480                #print "went here fitproblem_count == 1",fitproblem_count == 1
481                calc_fit=FitThread(parent =self.parent,
482                                        fn= self.fitter,
483                                       cpage=self.current_pg,
484                                       pars= pars,
485                                       completefn= self._single_fit_completed,
486                                       updatefn=self._updateFit)
487               
488                     
489            else:
490                ## Perform more than 1 fit at the time
491                calc_fit=FitThread(parent =self.parent,
492                                        fn= self.fitter,
493                                       completefn= self._simul_fit_completed,
494                                       updatefn=self._updateFit)
495           
496            calc_fit.queue()
497            self.ready_fit(calc_fit=calc_fit)
498           
499        except FitAbort:
500            print "in pluging"
501        except:
502            msg= "%s error: %s" % (engineType,sys.exc_value)
503            wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
504                                                    type="stop"))
505            return 
506       
507    def ready_fit(self, calc_fit):
508        """
509        Ready for another fit
510        """
511        if self.fitproblem_count != None and self.fitproblem_count > 1:
512            calc_fit.ready(2.5)
513           
514        else:
515            time.sleep(0.4)
516           
517    def remove_plot(self, page, theory=False):
518        """
519            remove model plot when a fit page is closed
520        """
521        fitproblem = self.page_finder[page]
522        data = fitproblem.get_fit_data()
523        model = fitproblem.get_model()
524        if model is not None:
525            name = model.name
526            new_plot = Theory1D(x=[], y=[], dy=None)
527            new_plot.name = name
528            new_plot.xaxis(data._xaxis, data._xunit)
529            new_plot.yaxis(data._yaxis, data._yunit)
530            new_plot.group_id = data.group_id
531            new_plot.id = data.id + name
532            wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title=data.name))
533        if theory:
534            new_plot_data = Data1D(x=[], y=[], dx=None, dy=None)
535            new_plot_data.name = data.name
536            new_plot_data.xaxis(data._xaxis, data._xunit)
537            new_plot_data.yaxis(data._yaxis, data._yunit)
538            new_plot_data.group_id = data.group_id
539            new_plot_data.id = data.id
540            wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot_data,
541                                                    title=data.name))
542    def create_fittable_data2D(self, data):
543        """
544            check if the current data is a data 2d and add dy to that data
545            @return Data2D
546        """
547        if data.__class__.__name__ != "Data2D":
548            raise ValueError, " create_fittable_data2D expects a Data2D"
549        ## Data2D case
550        new_data = deepcopy(data)
551        if not hasattr(data, "is_data"):
552            new_data.group_id += "data2D"
553            new_data.id +="data2D"
554            new_data.is_data = False
555            title = new_data.name
556            title += " Fit"
557            wx.PostEvent(self.parent, NewPlotEvent(plot=new_data,
558                                                    title=str(title)))
559        else:
560            new_data.is_data = True
561        return new_data
562       
563    def create_fittable_data1D(self, data):
564        """
565            check if the current data is a theory 1d and add dy to that data
566            @return Data1D
567        """
568        class_name = data.__class__.__name__
569        if not class_name in ["Data1D", "Theory1D"]:
570            raise ValueError, "create_fittable_data1D expects Data1D"
571     
572        #get the appropriate dy
573        dy = deepcopy(data.dy)
574        if len(self.err_dy) > 0:
575            if data.name in  self.err_dy.iterkeys():
576                dy = self.err_dy[data.name]   
577        if data.__class__.__name__ == "Theory1D":
578            new_data = self.copy_data(data, dy)
579            new_data.group_id = str(new_data.group_id)+"data1D"
580            new_data.id = str(new_data.id)+"data1D"
581            new_data.is_data = False
582            title = new_data.name
583            title = 'Data created from Theory'
584            wx.PostEvent(self.parent, NewPlotEvent(plot=new_data,
585                                                    title=str(title),
586                                                   reset=True))
587        else:
588            new_data = self.copy_data(data, dy) 
589            new_data.id = data.id
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                                                                info="info"))
613            else:
614                msg = "Page was already Created"
615                wx.PostEvent(self.parent, StatusEvent(status=msg, info="warning"))
616        except:
617            msg = "Creating Fit page: %s"%sys.exc_value
618            wx.PostEvent(self.parent, StatusEvent(status=msg, info="error"))
619            return
620   
621    def _onEVT_SLICER_PANEL(self, event):
622        """
623            receive and event telling to update a panel with a name starting with
624            event.panel_name. this method update slicer panel for a given interactor.
625            @param event: contains type of slicer , paramaters for updating the panel
626            and panel_name to find the slicer 's panel concerned.
627        """
628        for item in self.parent.panels:
629            if self.parent.panels[item].window_caption.startswith(event.panel_name):
630                self.parent.panels[item].set_slicer(event.type, event.params)
631               
632        self.parent._mgr.Update()
633   
634             
635    def _closed_fitpage(self, event):   
636        """
637            request fitpanel to close a given page when its unique data is removed
638            from the plot
639        """   
640        self.fit_panel.close_page_with_data(event.data) 
641       
642    def _add_page_onmenu(self, name,fitproblem=None):
643        """
644            Add name of a closed page of fitpanel in a menu
645        """
646        list = self.menu1.GetMenuItems()
647        for item in list:
648            if name == item.GetItemLabel():
649                self.closed_page_dict[name][1] = fitproblem
650               
651        if not name in self.closed_page_dict.keys():   
652            # Post paramters
653            event_id = wx.NewId()
654            self.menu1.Append(event_id, name, "Show %s fit panel" % name)
655            self.closed_page_dict[name]= [event_id, fitproblem]
656            wx.EVT_MENU(self.parent,event_id,  self._open_closed_page)
657       
658       
659    def _open_closed_page(self, event):   
660        """
661            reopen a closed page
662        """
663        for name, value in self.closed_page_dict.iteritems():
664            if event.GetId() in value:
665                id,fitproblem = value
666                if name !="Model":
667                    data= fitproblem.get_fit_data()
668                    page = self.fit_panel.add_fit_page(data= data,reset=True)
669                    if fitproblem != None:
670                        self.page_finder[page]=fitproblem
671                        if self.sim_page != None:
672                            self.sim_page.draw_page()
673                           
674                else:
675                    model = fitproblem
676                    self.fit_panel.add_model_page(model=model, topmenu=True,
677                                                  reset= True)
678                    break
679       
680       
681    def _reset_schedule_problem(self, value=0):
682        """
683             unschedule or schedule all fitproblem to be fit
684        """
685        for page, fitproblem in self.page_finder.iteritems():
686            fitproblem.schedule_tofit(value)
687           
688    def _fit_helper(self,pars,value, id, title="Single Fit " ):
689        """
690            helper for fitting
691        """
692        metadata = value.get_fit_data()
693        model = value.get_model()
694        smearer = value.get_smearer()
695        qmin , qmax = value.get_range()
696        self.fit_id =id
697        #Create list of parameters for fitting used
698        templist=[]
699       
700        try:
701            #Extra list of parameters and their constraints
702            listOfConstraint= []
703           
704            param = value.get_model_param()
705            if len(param)>0:
706                for item in param:
707                    ## check if constraint
708                    if item[0] !=None and item[1] != None:
709                        listOfConstraint.append((item[0],item[1]))
710                   
711            #Do the single fit
712            self.fitter.set_model(model, self.fit_id,
713                                   pars,constraints = listOfConstraint)
714           
715            self.fitter.set_data(data=metadata,Uid=self.fit_id,
716                                 smearer=smearer,qmin= qmin,qmax=qmax )
717            #print "self.fitter.set_data"
718            self.fitter.select_problem_for_fit(Uid= self.fit_id,
719                                               value= value.get_scheduled())
720            value.clear_model_param()
721        except:
722            msg= title +" error: %s" % sys.exc_value
723            wx.PostEvent(self.parent, StatusEvent(status= msg, type="stop"))
724            return
725       
726    def _onSelect(self,event):
727        """
728            when Select data to fit a new page is created .Its reference is
729            added to self.page_finder
730        """
731        self.panel = event.GetEventObject()
732        Plugin.on_perspective(self,event=event)
733        for plottable in self.panel.graph.plottables:
734            if plottable.__class__.__name__ in ["Data1D", "Theory1D"]:
735                if plottable.name == self.panel.graph.selected_plottable:
736                    data = self.create_fittable_data1D(data=plottable)
737                    self.add_fit_page(data=data)
738                    return
739            else:
740                data = self.create_fittable_data2D(data=plottable)
741                self.add_fit_page(data=data)
742
743    def _updateFit(self):
744        """
745            Is called when values of result are available
746        """
747        ##Sending a progess message to the status bar
748        wx.PostEvent(self.parent, StatusEvent(status="Computing..."))
749           
750    def _single_fit_completed(self,result,pars,cpage, elapsed=None):
751        """
752            Display fit result on one page of the notebook.
753            @param result: result of fit
754            @param pars: list of names of parameters fitted
755            @param current_pg: the page where information will be displayed
756            @param qmin: the minimum value of x to replot the model
757            @param qmax: the maximum value of x to replot model
758         
759        """     
760        try:
761            if result ==None:
762                msg= "Simple Fitting Stop !!!"
763                wx.PostEvent(self.parent, StatusEvent(status=msg,info="warning",
764                                                      type="stop"))
765                return
766            if not numpy.isfinite(result.fitness) or numpy.any(result.pvec ==None )or not numpy.all(numpy.isfinite(result.pvec) ):
767                msg= "Single Fitting did not converge!!!"
768                wx.PostEvent(self.parent, StatusEvent(status=msg,type="stop"))
769                return
770            for page, value in self.page_finder.iteritems():
771                if page==cpage :
772                    model= value.get_model()
773                    break
774            param_name = []
775            i = 0
776            for name in pars:
777                param_name.append(name)
778
779            cpage.onsetValues(result.fitness,param_name, result.pvec,result.stderr)
780           
781        except:
782            msg= "Single Fit completed but Following error occurred:%s"% sys.exc_value
783            wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
784                                                  type="stop"))
785            return
786       
787       
788    def _simul_fit_completed(self,result,pars=None,cpage=None, elapsed=None):
789        """
790            Parameter estimation completed,
791            display the results to the user
792            @param alpha: estimated best alpha
793            @param elapsed: computation time
794        """
795        ## fit more than 1 model at the same time
796        try:
797            msg = "" 
798            if result ==None:
799                msg= "Complex Fitting Stop !!!"
800                wx.PostEvent(self.parent, StatusEvent(status=msg,type="stop"))
801                return
802            if not numpy.isfinite(result.fitness) or numpy.any(result.pvec ==None )or not numpy.all(numpy.isfinite(result.pvec) ):
803                msg= "Single Fitting did not converge!!!"
804                wx.PostEvent(self.parent, StatusEvent(status=msg,type="stop"))
805                return
806             
807            for page, value in self.page_finder.iteritems():
808                """
809                if format_number(result.fitness) == page.get_chi2():
810                    #ToDo: Compare parameter inputs with outputs too.
811                    wx.PostEvent(self.parent, StatusEvent(status="%s " % msg))
812                    break     
813                """             
814                if value.get_scheduled()==1:
815                    model = value.get_model()
816                    data =  value.get_fit_data()
817                    small_param_name = []
818                    small_out = []
819                    small_cov = []
820                    i = 0
821                    #Separate result in to data corresponding to each page
822                    for p in result.parameters:
823                        model_name,param_name = self.split_string(p.name) 
824                        if model.name == model_name:
825                            p_name= model.name+"."+param_name
826                            if p.name == p_name:     
827                                if p.value != None and numpy.isfinite(p.value):
828                                    small_out.append(p.value )
829                                    small_param_name.append(param_name)
830                                    small_cov.append(p.stderr)
831
832                    # Display result on each page
833                    page.onsetValues(result.fitness, small_param_name,small_out,small_cov)
834        except:
835             msg= "Simultaneous Fit completed"
836             msg +=" but Following error occurred:%s"%sys.exc_value
837             wx.PostEvent(self.parent, StatusEvent(status=msg,type="stop"))
838             return 
839             
840                           
841       
842    def _on_show_panel(self, event):
843        print "_on_show_panel: fitting"
844     
845     
846    def _onset_engine_park(self,event):
847        """
848            set engine to park
849        """
850        Plugin.on_perspective(self,event=event)
851        self._on_change_engine('park')
852       
853       
854    def _onset_engine_scipy(self,event):
855        """
856            set engine to scipy
857        """
858        self._on_change_engine('scipy')
859       
860    def _on_slicer_event(self, event):
861        """
862            Receive a panel as event and send it to guiframe
863            @param event: event containing a panel
864        """
865       
866        if event.panel!=None:
867            new_panel = event.panel
868            self.slicer_panels.append(event.panel)
869            # Set group ID if available
870            event_id = self.parent.popup_panel(new_panel)
871            #self.menu3.Append(event_id, new_panel.window_caption,
872            #                 "Show %s plot panel" % new_panel.window_caption)
873            # Set UID to allow us to reference the panel later
874         
875            new_panel.uid = event_id
876            self.mypanels.append(new_panel) 
877        return 
878   
879    def _onclearslicer(self, event):
880        """
881            Clear the boxslicer when close the panel associate with this slicer
882        """
883        name =event.GetPane().caption
884   
885        for panel in self.slicer_panels:
886            if panel.window_caption==name:
887               
888                for item in self.parent.panels:
889                    if hasattr(self.parent.panels[item],"uid"):
890                        if self.parent.panels[item].uid ==panel.base.uid:
891                            self.parent.panels[item].onClearSlicer(event)
892                            self.parent._mgr.Update()
893                            break 
894                break
895       
896       
897       
898       
899    def _return_engine_type(self):
900        """
901            return the current type of engine
902        """
903        return self._fit_engine
904     
905     
906    def _on_change_engine(self, engine='park'):
907        """
908            Allow to select the type of engine to perform fit
909            @param engine: the key work of the engine
910        """
911       
912        ## saving fit engine name
913        self._fit_engine = engine
914        ## change menu item state
915        if engine=="park":
916            self.menu1.FindItemByPosition(0).Check(False)
917            self.menu1.FindItemByPosition(1).Check(True)
918        else:
919            self.menu1.FindItemByPosition(0).Check(True)
920            self.menu1.FindItemByPosition(1).Check(False)
921           
922        ## post a message to status bar
923        wx.PostEvent(self.parent, StatusEvent(status="Engine set to: %s" % self._fit_engine))
924   
925        ## Bind every open fit page with a newevent to know the current fitting engine
926        import fitpage
927        event= fitpage.FitterTypeEvent()
928        event.type = self._fit_engine
929        for key in self.page_finder.keys():
930            wx.PostEvent(key, event)
931       
932   
933    def _on_model_panel(self, evt):
934        """
935            react to model selection on any combo box or model menu.plot the model 
936            @param evt: wx.combobox event
937        """
938        model = evt.model
939        if model == None:
940            return
941        smearer = None
942        qmin = None
943        qmax = None
944        if hasattr(evt, "qmin"):
945            qmin = evt.qmin
946        if hasattr(evt, "qmax"):
947            qmax = evt.qmax
948        if hasattr(evt, "smearer"):
949            smearer = evt.smearer
950        #model.origin_name = model.name
951        self.current_pg = self.fit_panel.get_current_page() 
952        ## make sure nothing is done on self.sim_page
953        ## example trying to call set_panel on self.sim_page
954        if self.current_pg != self.sim_page :
955            if self.page_finder[self.current_pg].get_model()== None :
956               
957                model.name = "M"+str(self.index_model)
958                self.index_model += 1 
959            else:
960                model.name= self.page_finder[self.current_pg].get_model().name
961           
962            data = self.page_finder[self.current_pg].get_fit_data()
963           
964            # save the name containing the data name with the appropriate model
965            self.page_finder[self.current_pg].set_model(model)
966            qmin, qmax= self.current_pg.get_range()
967            self.page_finder[self.current_pg].set_range(qmin=qmin, qmax=qmax)
968            smearer=  self.page_finder[self.current_pg].get_smearer()
969            # save model name
970            self.set_smearer(smearer=smearer, qmin=qmin, qmax=qmax)
971           
972            if self.sim_page!=None:
973                self.sim_page.draw_page()
974       
975    def _on_model_menu(self, evt):
976        """
977            Plot a theory from a model selected from the menu
978            @param evt: wx.menu event
979        """
980        model = evt.model
981        Plugin.on_perspective(self,event=evt)
982        # Create a model page. If a new page is created, the model
983        # will be plotted automatically. If a page already exists,
984        # the content will be updated and the plot refreshed
985        self.fit_panel.add_model_page(model,topmenu=True)
986   
987   
988   
989   
990    def _update1D(self,x, output):
991        """
992            Update the output of plotting model 1D
993        """
994        wx.PostEvent(self.parent, StatusEvent(status="Plot \
995        #updating ... ",type="update"))
996        self.ready_fit()
997        #self.calc_thread.ready(0.01)
998   
999   
1000    def _fill_default_model2D(self, theory, qmax,qstep, qmin=None):
1001        """
1002            fill Data2D with default value
1003            @param theory: Data2D to fill
1004        """
1005        from DataLoader.data_info import Detector, Source
1006       
1007        detector = Detector()
1008        theory.detector.append(detector)         
1009        theory.source= Source()
1010       
1011        ## Default values   
1012        theory.detector[0].distance= 8000   # mm       
1013        theory.source.wavelength= 6         # A     
1014        theory.detector[0].pixel_size.x= 5  # mm
1015        theory.detector[0].pixel_size.y= 5  # mm
1016       
1017        theory.detector[0].beam_center.x= qmax
1018        theory.detector[0].beam_center.y= qmax
1019       
1020       
1021        ## create x_bins and y_bins of the model 2D
1022        pixel_width_x = theory.detector[0].pixel_size.x
1023        pixel_width_y = theory.detector[0].pixel_size.y
1024        center_x      = theory.detector[0].beam_center.x/pixel_width_x
1025        center_y      = theory.detector[0].beam_center.y/pixel_width_y
1026
1027        # theory default: assume the beam center is located at the center of sqr detector
1028        xmax = qmax
1029        xmin = -qmax
1030        ymax = qmax
1031        ymin = -qmax
1032       
1033        x=  numpy.linspace(start= -1*qmax,
1034                               stop= qmax,
1035                               num= qstep,
1036                               endpoint=True ) 
1037        y = numpy.linspace(start= -1*qmax,
1038                               stop= qmax,
1039                               num= qstep,
1040                               endpoint=True )
1041         
1042        ## use data info instead
1043        new_x = numpy.tile(x, (len(y),1))
1044        new_y = numpy.tile(y, (len(x),1))
1045        new_y = new_y.swapaxes(0,1)
1046       
1047        # all data reuire now in 1d array
1048        qx_data = new_x.flatten()
1049        qy_data = new_y.flatten()
1050       
1051        q_data = numpy.sqrt(qx_data*qx_data+qy_data*qy_data)
1052        # set all True (standing for unmasked) as default
1053        mask    = numpy.ones(len(qx_data), dtype = bool)
1054       
1055        # calculate the range of qx and qy: this way, it is a little more independent
1056        x_size = xmax- xmin
1057        y_size = ymax -ymin
1058       
1059        # store x and y bin centers in q space
1060        x_bins  = x
1061        y_bins  = y
1062        # bin size: x- & y-directions
1063        xstep = x_size/len(x_bins-1)
1064        ystep = y_size/len(y_bins-1)
1065       
1066        #theory.data = numpy.zeros(len(mask))
1067        theory.err_data = numpy.zeros(len(mask))
1068        theory.qx_data = qx_data
1069        theory.qy_data = qy_data 
1070        theory.q_data = q_data
1071        theory.mask = mask           
1072        theory.x_bins = x_bins 
1073        theory.y_bins = y_bins   
1074       
1075        # max and min taking account of the bin sizes
1076        theory.xmin= xmin
1077        theory.xmax= xmax
1078        theory.ymin= ymin
1079        theory.ymax= ymax
1080        theory.group_id ="Model"
1081        theory.id ="Model"
1082       
1083       
1084    def _get_plotting_info(self, data=None):
1085        """
1086            get plotting info from data if data !=None
1087            else use some default
1088        """
1089        my_info = PlotInfo()
1090        if data !=None:
1091            if hasattr(data,"info"):
1092                x_name, x_units = data.get_xaxis() 
1093                y_name, y_units = data.get_yaxis() 
1094               
1095                my_info._xunit = x_units
1096                my_info._xaxis = x_name
1097                my_info._yunit = y_units
1098                my_info._yaxis = y_name
1099               
1100            my_info.title= data.name
1101            if hasattr(data, "info"):
1102                my_info.info= data.info
1103            if hasattr(data, "group_id"):
1104                my_info.group_id= data.group_id
1105       
1106        return my_info
1107               
1108               
1109    def _complete1D(self, x,y, elapsed,model,data=None):
1110        """
1111            Complete plotting 1D data
1112        """ 
1113        try:
1114            new_plot = Theory1D(x=x, y=y)
1115            my_info = self._get_plotting_info( data)
1116            new_plot.name = model.name
1117            new_plot.id = my_info.id
1118            new_plot.group_id = my_info.group_id
1119           
1120            new_plot.xaxis( my_info._xaxis,  my_info._xunit)
1121            new_plot.yaxis( my_info._yaxis, my_info._yunit)
1122            if data!=None:
1123                if new_plot.id == data.id:
1124                    new_plot.id += "Model"
1125                new_plot.is_data =False 
1126           
1127            title= new_plot.name
1128            #new_plot.perspective = self.get_perspective()
1129            # Pass the reset flag to let the plotting event handler
1130            # know that we are replacing the whole plot
1131            if title== None:
1132                title = "Analytical model 1D "
1133            if data ==None:
1134                wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1135                             title=str(title), reset=True))
1136            else:
1137                wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,title= str(title)))
1138            msg = "Plot 1D  complete !"
1139            wx.PostEvent( self.parent, StatusEvent(status=msg , type="stop" ))
1140        except:
1141            msg= " Error occurred when drawing %s Model 1D: "%new_plot.name
1142            msg+= " %s"%sys.exc_value
1143            wx.PostEvent( self.parent, StatusEvent(status= msg, type="stop"))
1144            return 
1145                 
1146    def _update2D(self, output,time=None):
1147        """
1148            Update the output of plotting model
1149        """
1150        wx.PostEvent(self.parent, StatusEvent(status="Plot \
1151        #updating ... ",type="update"))
1152        self.ready_fit()
1153        #self.calc_thread.ready(0.01)
1154       
1155       
1156    def _complete2D(self, image,data, model,  elapsed,qmin, qmax,qstep=DEFAULT_NPTS):
1157        """
1158            Complete get the result of modelthread and create model 2D
1159            that can be plot.
1160        """
1161        err_image = numpy.zeros(numpy.shape(image))
1162       
1163        theory= Data2D(image= image , err_image= err_image)
1164        theory.name= model.name
1165       
1166        if data ==None:
1167            self._fill_default_model2D(theory= theory, qmax=qmax,qstep=qstep, qmin= qmin)
1168       
1169        else:
1170            theory.id= data.id+"Model"
1171            theory.group_id= data.name+"Model"
1172            theory.x_bins= data.x_bins
1173            theory.y_bins= data.y_bins
1174            theory.detector= data.detector
1175            theory.source= data.source
1176            theory.is_data =False 
1177            theory.qx_data = data.qx_data
1178            theory.qy_data = data.qy_data
1179            theory.q_data = data.q_data
1180            theory.err_data = data.err_data
1181            theory.mask = data.mask
1182            ## plot boundaries
1183            theory.ymin= data.ymin
1184            theory.ymax= data.ymax
1185            theory.xmin= data.xmin
1186            theory.xmax= data.xmax
1187     
1188       
1189        ## plot
1190        wx.PostEvent(self.parent, NewPlotEvent(plot=theory,
1191                         title="Analytical model 2D ", reset=True ))
1192        msg = "Plot 2D complete !"
1193        wx.PostEvent( self.parent, StatusEvent( status= msg , type="stop" ))
1194         
1195    def _on_data_error(self, event):
1196        """
1197            receives and event from plotting plu-gins to store the data name and
1198            their errors of y coordinates for 1Data hide and show error
1199        """
1200        self.err_dy = event.err_dy
1201        #print "receiving error dy",self.err_dy
1202         
1203    def _draw_model2D(self,model,data=None,description=None, enable2D=False,
1204                      qmin=DEFAULT_QMIN, qmax=DEFAULT_QMAX, qstep=DEFAULT_NPTS):
1205        """
1206            draw model in 2D
1207            @param model: instance of the model to draw
1208            @param description: the description of the model
1209            @param enable2D: when True allows to draw model 2D
1210            @param qmin: the minimum value to  draw model 2D
1211            @param qmax: the maximum value to draw model 2D
1212            @param qstep: the number of division of Qx and Qy of the model to draw
1213           
1214        """
1215        x=  numpy.linspace(start= -1*qmax,
1216                               stop= qmax,
1217                               num= qstep,
1218                               endpoint=True ) 
1219        y = numpy.linspace(start= -1*qmax,
1220                               stop= qmax,
1221                               num= qstep,
1222                               endpoint=True )
1223        ## use data info instead
1224        if data !=None:
1225            ## check if data2D to plot
1226            if hasattr(data, "x_bins"):
1227                enable2D = True
1228                x= data.x_bins
1229                y= data.y_bins
1230               
1231        if not enable2D:
1232            return
1233        try:
1234            from model_thread import Calc2D
1235            ## If a thread is already started, stop it
1236            if self.calc_2D != None and self.calc_2D.isrunning():
1237                self.calc_2D.stop()
1238            self.calc_2D = Calc2D(  x= x,
1239                                    y= y,
1240                                    model= model, 
1241                                    data = data,
1242                                    qmin= qmin,
1243                                    qmax= qmax,
1244                                    qstep= qstep,
1245                                    completefn= self._complete2D,
1246                                    updatefn= self._update2D )
1247            self.calc_2D.queue()
1248           
1249        except:
1250            msg= " Error occurred when drawing %s Model 2D: "%model.name
1251            msg+= " %s"%sys.exc_value
1252            wx.PostEvent( self.parent, StatusEvent(status= msg ))
1253            return 
1254   
1255    def _draw_model1D(self, model, data=None, smearer= None,
1256                qmin=DEFAULT_QMIN, qmax=DEFAULT_QMAX, qstep= DEFAULT_NPTS,enable1D= True):
1257        """
1258            Draw model 1D from loaded data1D
1259            @param data: loaded data
1260            @param model: the model to plot
1261        """
1262        x=  numpy.linspace(start= qmin,
1263                           stop= qmax,
1264                           num= qstep,
1265                           endpoint=True
1266                           )
1267        if data!=None:
1268            ## check for data2D
1269            if hasattr(data,"x_bins"):
1270                return
1271            x = data.x
1272            if qmin == DEFAULT_QMIN :
1273                qmin = min(data.x)
1274            if qmax == DEFAULT_QMAX:
1275                qmax = max(data.x) 
1276           
1277       
1278        if not enable1D:
1279            return
1280   
1281        try:
1282            from model_thread import Calc1D
1283            ## If a thread is already started, stop it
1284            if self.calc_1D!= None and self.calc_1D.isrunning():
1285                self.calc_1D.stop()
1286            self.calc_1D= Calc1D( x= x,
1287                                  data = data,
1288                                  model= model, 
1289                                  qmin = qmin,
1290                                  qmax = qmax,
1291                                  smearer = smearer,
1292                                  completefn = self._complete1D,
1293                                  updatefn = self._update1D  )
1294            self.calc_1D.queue()
1295           
1296        except:
1297            msg= " Error occurred when drawing %s Model 1D: "%model.name
1298            msg+= " %s"%sys.exc_value
1299            wx.PostEvent( self.parent, StatusEvent(status= msg ))
1300            return 
1301           
1302
1303def profile(fn, *args, **kw):
1304    import cProfile, pstats, os
1305    global call_result
1306    def call():
1307        global call_result
1308        call_result = fn(*args, **kw)
1309    cProfile.runctx('call()', dict(call=call), {}, 'profile.out')
1310    stats = pstats.Stats('profile.out')
1311    #stats.sort_stats('time')
1312    stats.sort_stats('calls')
1313    stats.print_stats()
1314    os.unlink('profile.out')
1315    return call_result
1316if __name__ == "__main__":
1317    i = Plugin()
1318   
1319   
1320   
1321   
Note: See TracBrowser for help on using the repository browser.