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

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 c81140c was 286bb6c, checked in by Gervaise Alina <gervyh@…>, 15 years ago

remove import of dataloader

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