source: sasview/sansview/perspectives/fitting/fitting.py @ 9ade6ad

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 9ade6ad was f6aee42, checked in by Jae Cho <jhjcho@…>, 13 years ago

give mac some sleep on computation: timing issue

  • Property mode set to 100644
File size: 66.1 KB
Line 
1
2
3################################################################################
4#This software was developed by the University of Tennessee as part of the
5#Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
6#project funded by the US National Science Foundation.
7#
8#See the license text in license.txt
9#
10#copyright 2009, University of Tennessee
11################################################################################
12
13
14import re
15import sys
16import wx
17import logging
18import numpy
19import string
20import time
21from copy import deepcopy
22import models
23import fitpage
24
25
26from DataLoader.loader import Loader
27from sans.guiframe.dataFitting import Data2D
28from sans.guiframe.dataFitting import Data1D
29from sans.guiframe.events import NewPlotEvent
30from sans.guiframe.events import StatusEvent 
31from sans.guiframe.events import EVT_SLICER_PANEL
32from sans.guiframe.events import EVT_SLICER_PARS_UPDATE
33from sans.guiframe.gui_style import GUIFRAME_ID
34from sans.guiframe.plugin_base import PluginBase
35
36#from .console import ConsoleUpdate
37from .fitproblem import FitProblem
38from .fitpanel import FitPanel
39#from .fit_thread import FitThread
40from .pagestate import Reader
41from .fitpage import Chi2UpdateEvent
42
43DEFAULT_BEAM = 0.005
44DEFAULT_QMIN = 0.001
45DEFAULT_QMAX = 0.13
46DEFAULT_NPTS = 50
47MAX_NBR_DATA = 4
48
49
50(PageInfoEvent, EVT_PAGE_INFO)   = wx.lib.newevent.NewEvent()
51
52if sys.platform.count("darwin")==0:
53    ON_MAC = False
54else:
55    ON_MAC = True   
56
57class Plugin(PluginBase):
58    """
59    Fitting plugin is used to perform fit
60    """
61    def __init__(self, standalone=False):
62        PluginBase.__init__(self, name="Fitting", standalone=standalone)
63       
64        #list of panel to send to guiframe
65        self.mypanels = []
66        # reference to the current running thread
67        self.calc_2D = None
68        self.calc_1D = None
69        self.fit_thread_list = {}
70        self.residuals = None
71        self.fit_panel = None
72        # Start with a good default
73        self.elapsed = 0.022
74        # the type of optimizer selected, park or scipy
75        self.fitter  = None
76        self.fit_panel = None
77        #let fit ready
78        self.fitproblem_count = None
79        #Flag to let the plug-in know that it is running stand alone
80        self.standalone = True
81        ## dictionary of page closed and id
82        self.closed_page_dict = {}
83        ## Fit engine
84        self._fit_engine = 'scipy'
85        ## Relative error desired in the sum of squares (float); scipy only
86        self.ftol=1.49012e-08
87        #List of selected data
88        self.selected_data_list = []
89        ## list of slicer panel created to display slicer parameters and results
90        self.slicer_panels = []
91        # model 2D view
92        self.model2D_id = None
93        #keep reference of the simultaneous fit page
94        self.sim_page = None
95        self.index_model = 0
96        #Create a reader for fit page's state
97        self.state_reader = None 
98        self._extensions = '.fitv'
99        self.scipy_id = wx.NewId()
100        self.park_id = wx.NewId()
101       
102        self.temp_state = []
103        self.state_index = 0
104        self.sfile_ext = None
105        # take care of saving  data, model and page associated with each other
106        self.page_finder = {}
107        # Log startup
108        logging.info("Fitting plug-in started") 
109       
110    def populate_menu(self, owner):
111        """
112        Create a menu for the Fitting plug-in
113       
114        :param id: id to create a menu
115        :param owner: owner of menu
116       
117        :return: list of information to populate the main menu
118       
119        """
120        #Menu for fitting
121        self.menu1 = wx.Menu()
122        id1 = wx.NewId()
123        simul_help = "Add new fit panel"
124        self.menu1.Append(id1, '&New Fit Page',simul_help)
125        wx.EVT_MENU(owner, id1, self.on_add_new_page)
126        self.menu1.AppendSeparator()
127        id1 = wx.NewId()
128        simul_help = "Simultaneous Fit"
129        self.menu1.Append(id1, '&Simultaneous Fit',simul_help)
130        wx.EVT_MENU(owner, id1, self.on_add_sim_page)
131        self.menu1.AppendSeparator()
132        #Set park engine
133       
134        scipy_help= "Scipy Engine: Perform Simple fit. More in Help window...."
135        self.menu1.AppendCheckItem(self.scipy_id, "Simple FitEngine  [LeastSq]",scipy_help) 
136        wx.EVT_MENU(owner, self.scipy_id,  self._onset_engine_scipy)
137       
138        park_help = "Park Engine: Perform Complex fit. More in Help window...."
139        self.menu1.AppendCheckItem(self.park_id, "Complex FitEngine [ParkMC]",park_help) 
140        wx.EVT_MENU(owner, self.park_id,  self._onset_engine_park)
141       
142        self.menu1.FindItemById(self.scipy_id).Check(True)
143        self.menu1.FindItemById(self.park_id).Check(False)
144        #create  menubar items
145        return [(self.menu1, self.sub_menu)]
146               
147    def on_add_sim_page(self, event):
148        """
149        Create a page to access simultaneous fit option
150        """
151        if self.sim_page != None:
152            msg= "Simultaneous Fit page already opened"
153            wx.PostEvent(self.parent, StatusEvent(status= msg))
154            return 
155       
156        self.sim_page= self.fit_panel.add_sim_page()
157       
158    def help(self, evt):
159        """
160        Show a general help dialog.
161        """
162        from help_panel import  HelpWindow
163        frame = HelpWindow(None, -1, 'HelpWindow')   
164        frame.Show(True)
165       
166    def get_context_menu(self, plotpanel=None):
167        """
168        Get the context menu items available for P(r).them allow fitting option
169        for Data2D and Data1D only.
170       
171        :param graph: the Graph object to which we attach the context menu
172       
173        :return: a list of menu items with call-back function
174       
175        :note: if Data1D was generated from Theory1D 
176                the fitting option is not allowed
177               
178        """
179        graph = plotpanel.graph
180        fit_option = "Select data for fitting"
181        fit_hint =  "Dialog with fitting parameters "
182       
183        if graph.selected_plottable not in plotpanel.plots:
184            return []
185        item = plotpanel.plots[graph.selected_plottable]
186        if item.__class__.__name__ is "Data2D": 
187            if hasattr(item,"is_data"):
188                if item.is_data:
189                    return [[fit_option, fit_hint, self._onSelect]]
190                else:
191                    return [] 
192            return [[fit_option, fit_hint, self._onSelect]]
193        else:
194           
195            # if is_data is true , this in an actual data loaded
196            #else it is a data created from a theory model
197            if hasattr(item,"is_data"):
198                if item.is_data:
199                    return [[fit_option, fit_hint,
200                              self._onSelect]]
201                else:
202                    return [] 
203        return []   
204
205
206    def get_panels(self, parent):
207        """
208        Create and return a list of panel objects
209        """
210        self.parent = parent
211        #self.parent.Bind(EVT_FITSTATE_UPDATE, self.on_set_state_helper)
212        # Creation of the fit panel
213        self.fit_panel = FitPanel(parent=self.parent, manager=self)
214        self.on_add_new_page(event=None)
215        #Set the manager for the main panel
216        self.fit_panel.set_manager(self)
217        # List of windows used for the perspective
218        self.perspective = []
219        self.perspective.append(self.fit_panel.window_name)
220       
221        #index number to create random model name
222        self.index_model = 0
223        self.index_theory= 0
224        self.parent.Bind(EVT_SLICER_PANEL, self._on_slicer_event)
225        self.parent.Bind(EVT_SLICER_PARS_UPDATE, self._onEVT_SLICER_PANEL)
226        self.parent._mgr.Bind(wx.aui.EVT_AUI_PANE_CLOSE,self._onclearslicer)   
227        #Create reader when fitting panel are created
228        self.state_reader = Reader(self.set_state)   
229        #append that reader to list of available reader
230        loader = Loader()
231        loader.associate_file_reader(".fitv", self.state_reader)
232        #loader.associate_file_reader(".svs", self.state_reader)
233        #from sans.perspectives.calculator.sld_panel import SldPanel
234        #Send the fitting panel to guiframe
235        self.mypanels.append(self.fit_panel) 
236        #self.mypanels.append(SldPanel(parent=self.parent, base=self.parent))
237        return self.mypanels
238   
239    def clear_panel(self):
240        """
241        """
242        self.fit_panel.clear_panel()
243       
244    def set_default_perspective(self):
245        """
246        Call back method that True to notify the parent that the current plug-in
247        can be set as default  perspective.
248        when returning False, the plug-in is not candidate for an automatic
249        default perspective setting
250        """
251        return True
252   
253    def delete_data(self, data):
254        """
255        delete  the given data from panel
256        """
257        self.fit_panel.delete_data(data)
258       
259    def set_data(self, data_list=None):
260        """
261        receive a list of data to fit
262        """
263        if data_list is None:
264            data_list = []
265        selected_data_list = []
266        if len(data_list) > MAX_NBR_DATA :
267            from fitting_widgets import DataDialog
268            dlg = DataDialog(data_list=data_list, nb_data=MAX_NBR_DATA)
269            if dlg.ShowModal() == wx.ID_OK:
270                selected_data_list = dlg.get_data()
271            dlg.Destroy()
272           
273        else:
274            selected_data_list = data_list
275        try:
276            for data in selected_data_list:
277                page = self.add_fit_page(data=data)
278                wx.PostEvent(self.parent, NewPlotEvent(plot=data, 
279                                                       title=str(data.title)))
280        except:
281            msg = "Fitting Set_data: " + str(sys.exc_value)
282            wx.PostEvent(self.parent, StatusEvent(status=msg, info="error"))
283   
284    def set_top_panel(self):
285        """
286        Close default (welcome) panel
287        """
288        if 'default' in self.parent.panels:
289            self.parent.on_close_welcome_panel()
290
291             
292    def set_theory(self,  theory_list=None):
293        """
294        """
295        #set the model state for a given theory_state:
296        for item in theory_list:
297            try:
298                _, theory_state = item
299                self.fit_panel.set_model_state(theory_state)
300            except:
301                msg = "Fitting: cannot deal with the theory received"
302                logging.error("set_theory " + msg + "\n" + str(sys.exc_value))
303                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error"))
304           
305    def set_state(self, state=None, datainfo=None, format=None):
306        """
307        Call-back method for the fit page state reader.
308        This method is called when a .fitv/.svs file is loaded.
309       
310        : param state: PageState object
311        : param datainfo: data
312        """
313        #state = self.state_reader.get_state()
314        if state != None:
315            state = state.clone()
316            # store fitting state in temp_state
317            self.temp_state.append(state) 
318        else:
319            self.temp_state = []
320        # index to start with for a new set_state
321        self.state_index = 0
322        # state file format
323        self.sfile_ext = format
324       
325        self.on_set_state_helper(event=None)
326
327    def  on_set_state_helper(self,event=None):
328        """
329        Set_state_helper. This actually sets state after plotting data from state file.
330       
331        : event: FitStateUpdateEvent called by dataloader.plot_data from guiframe
332        """
333        if len(self.temp_state) == 0:
334            if self.state_index==0 and len(self.mypanels) <= 0 and self.sfile_ext =='.svs':
335                self.fit_panel.add_default_pages()
336                self.temp_state = []
337                self.state_index = 0
338            return
339       
340        try:
341            # Load fitting state
342            state = self.temp_state[self.state_index]
343            #panel state should have model selection to set_state
344            if state.formfactorcombobox != None:
345                #set state
346                data = self.parent.create_gui_data(state.data)
347                data.group_id = state.data.group_id
348                self.parent.add_data(data_list={data.id:data})
349                wx.PostEvent(self.parent, NewPlotEvent(plot=data,
350                                        title=data.title))
351                #need to be fix later make sure we are sendind guiframe.data
352                #to panel
353                state.data = data
354                page = self.fit_panel.set_state(state)   
355            else:
356                #just set data because set_state won't work
357                data = self.parent.create_gui_data(state.data)
358                data.group_id = state.data.group_id
359                self.parent.add_data(data_list={data.id:data})
360                wx.PostEvent(self.parent, NewPlotEvent(plot=data,
361                                        title=data.title))
362                page = self.add_fit_page(data)
363                caption = page.window_name
364                self.store_data(page=page.uid, data=data, caption=caption)
365                self.mypanels.append(page) 
366               
367            # get ready for the next set_state
368            self.state_index += 1
369
370            #reset state variables to default when all set_state is finished.
371            if len(self.temp_state) == self.state_index:
372               
373                self.temp_state = []
374                #self.state_index = 0
375                # Make sure the user sees the fitting panel after loading
376                #self.parent.set_perspective(self.perspective)
377                self.on_perspective(event=None)
378        except:
379            self.state_index==0
380            self.temp_state = []
381            raise
382                 
383    def save_fit_state(self, filepath, fitstate): 
384        """
385        save fit page state into file
386        """
387        self.state_reader.write(filename=filepath, fitstate=fitstate)
388       
389    def set_fit_range(self, uid, qmin, qmax):
390        """
391        Set the fitting range of a given page
392        """
393        self.page_finder[uid].set_range(qmin=qmin, qmax=qmax)
394                   
395    def schedule_for_fit(self,value=0, uid=None,fitproblem =None): 
396        """
397        Set the fit problem field to 0 or 1 to schedule that problem to fit.
398        Schedule the specified fitproblem or get the fit problem related to
399        the current page and set value.
400       
401        :param value: integer 0 or 1
402        :param fitproblem: fitproblem to schedule or not to fit
403       
404        """   
405        if fitproblem !=None:
406            fitproblem.schedule_tofit(value)
407        else:
408            self.page_finder[uid].schedule_tofit(value)
409         
410    def get_page_finder(self):
411        """
412        return self.page_finder used also by simfitpage.py
413        """ 
414        return self.page_finder
415   
416    def set_page_finder(self,modelname,names,values):
417        """
418        Used by simfitpage.py to reset a parameter given the string constrainst.
419         
420        :param modelname: the name ot the model for with the parameter has to reset
421        :param value: can be a string in this case.
422        :param names: the paramter name
423         
424        :note: expecting park used for fit.
425         
426        """ 
427        sim_page_id = self.sim_page.uid
428        for uid, value in self.page_finder.iteritems():
429            if uid != sim_page_id:
430                list = value.get_model()
431                model = list[0]
432                if model.name == modelname:
433                    value.set_model_param(names, values)
434                    break
435         
436    def split_string(self,item): 
437        """
438        receive a word containing dot and split it. used to split parameterset
439        name into model name and parameter name example: ::
440       
441            paramaterset (item) = M1.A
442            Will return model_name = M1 , parameter name = A
443           
444        """
445        if string.find(item,".")!=-1:
446            param_names= re.split("\.",item)
447            model_name=param_names[0]           
448            ##Assume max len is 3; eg., M0.radius.width
449            if len(param_names) == 3:
450                param_name=param_names[1]+"."+param_names[2]
451            else:
452                param_name=param_names[1]                   
453            return model_name,param_name
454   
455    def set_ftol(self, ftol=None):
456        """
457        Set ftol: Relative error desired in the sum of chi squares. 
458        """
459        # check if it is flaot
460        try:
461            f_tol = float(ftol)
462        except:
463            # default
464            f_tol = 1.49012e-08
465           
466        self.ftol = f_tol
467             
468    def stop_fit(self, uid):
469        """
470        Stop the fit engine
471        """
472        if uid in self.fit_thread_list.keys():
473            calc_fit = self.fit_thread_list[uid]
474            if calc_fit is not  None and calc_fit.isrunning():
475                calc_fit.stop()
476                msg = "Fit stop!"
477                wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
478            del self.fit_thread_list[uid]
479        #set the fit button label of page when fit stop is trigger from
480        #simultaneous fit pane
481        if  self.sim_page is not None and uid == self.sim_page.uid:
482            for uid, value in self.page_finder.iteritems():
483                if value.get_scheduled() == 1:
484                    if uid in self.fit_panel.opened_pages.keys():
485                        panel = self.fit_panel.opened_pages[uid]
486                        panel. _on_fit_complete()
487 
488    def set_smearer(self, uid, smearer, qmin=None, qmax=None, draw=True, 
489                    enable2D=False):
490        """
491        Get a smear object and store it to a fit problem
492       
493        :param smearer: smear object to allow smearing data
494       
495        """   
496        if uid not in self.page_finder.keys():
497            msg = "Cannot find ID: %s in page_finder" % str(uid)
498            raise ValueError, msg
499        self.page_finder[uid].set_smearer(smearer)
500        self.page_finder[uid].set_enable2D(enable2D)
501        if draw:
502            ## draw model 1D with smeared data
503            data =  self.page_finder[uid].get_fit_data()
504            model = self.page_finder[uid].get_model()
505            if model is None:
506                return
507            enable1D = True
508            enable2D = self.page_finder[uid].get_enable2D()
509            if enable2D:
510                enable1D = False
511
512            ## if user has already selected a model to plot
513            ## redraw the model with data smeared
514            smear = self.page_finder[uid].get_smearer()
515            self.draw_model(model=model, data=data, page_id=uid, smearer=smear,
516                enable1D=enable1D, enable2D=enable2D,
517                qmin=qmin, qmax=qmax)
518            self._mac_sleep(0.2)
519           
520    def _mac_sleep(self, sec=0.2):
521        """
522        Give sleep to MAC
523        """
524        if ON_MAC:
525           time.sleep(sec)
526               
527    def draw_model(self, model, page_id, data=None, smearer=None,
528                   enable1D=True, enable2D=False,
529                   state=None,
530                   toggle_mode_on=False,
531                   qmin=DEFAULT_QMIN, qmax=DEFAULT_QMAX, 
532                   qstep=DEFAULT_NPTS,
533                   update_chisqr=True):
534        """
535        Draw model.
536       
537        :param model: the model to draw
538        :param name: the name of the model to draw
539        :param data: the data on which the model is based to be drawn
540        :param description: model's description
541        :param enable1D: if true enable drawing model 1D
542        :param enable2D: if true enable drawing model 2D
543        :param qmin:  Range's minimum value to draw model
544        :param qmax:  Range's maximum value to draw model
545        :param qstep: number of step to divide the x and y-axis
546        :param update_chisqr: update chisqr [bool]
547             
548        """
549        if data.__class__.__name__ == "Data1D" or not enable2D:   
550            ## draw model 1D with no loaded data
551            self._draw_model1D(model=model, 
552                               data=data,
553                               page_id=page_id,
554                               enable1D=enable1D, 
555                               smearer=smearer,
556                               qmin=qmin,
557                               qmax=qmax, 
558                               toggle_mode_on=toggle_mode_on,
559                               state=state,
560                               qstep=qstep,
561                               update_chisqr=update_chisqr)
562        else:     
563            ## draw model 2D with no initial data
564            self._draw_model2D(model=model,
565                                page_id=page_id,
566                                data=data,
567                                enable2D=enable2D,
568                                smearer=smearer,
569                                qmin=qmin,
570                                qmax=qmax,
571                                state=state,
572                                toggle_mode_on=toggle_mode_on,
573                                qstep=qstep,
574                                update_chisqr=update_chisqr)
575           
576    def onFit(self, uid=None):
577        """
578        perform fit
579       
580        : param type: uid for single fit, None for simultaneous fit
581        """
582        from sans.fit.Fitting import Fit
583        from .fit_thread import FitThread
584        from .console import ConsoleUpdate
585        ##  count the number of fitproblem schedule to fit
586        #if uid == None or self._fit_engine == "park":
587        fitproblem_count = 0
588        for value in self.page_finder.values():
589            if value.get_scheduled() == 1:
590                fitproblem_count += 1
591               
592        ## if simultaneous fit change automatically the engine to park
593        # scipy can not handle two thread running at the same time
594        if fitproblem_count > 1 or len(self.fit_thread_list)>0:
595            self._on_change_engine(engine='park')
596           
597        self.fitproblem_count = fitproblem_count 
598
599        fitter = Fit(self._fit_engine)
600        if self._fit_engine == "park":
601            engineType = "Simultaneous Fit"
602        else:
603            engineType = "Single Fit"
604           
605        fproblemId = 0
606        self.current_pg = None
607        list_page_id = []
608        for page_id, value in self.page_finder.iteritems():
609            # For simulfit (uid give with None), do for-loop
610            # if uid is specified (singlefit), do it only on the page.
611            if engineType == "Single Fit":
612                if page_id != uid:
613                    continue
614            try:
615                if value.get_scheduled() == 1:
616                    #Get list of parameters name to fit
617                    pars = []
618                    templist = []
619                   
620                    page = self.fit_panel.get_page_by_id(page_id)
621                    templist = page.get_param_list()
622                    # missing fit parameters
623                    #if not templist:
624                    #    return
625                    # have the list
626                    for element in templist:
627                        name = str(element[1])
628                        pars.append(name)
629                    #Set Engine  (model , data) related to the page on
630                    self._fit_helper(value=value, pars=pars,
631                                     fitter=fitter,
632                                      fitproblem_id=fproblemId,
633                                      title=engineType) 
634                    list_page_id.append(page_id)
635                    fproblemId += 1 
636                    current_page_id = page_id
637            except:
638                #raise
639                msg= "%s error: %s" % (engineType, sys.exc_value)
640                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
641                                                      type="stop"))
642                return 
643        ## If a thread is already started, stop it
644        #if self.calc_fit!= None and self.calc_fit.isrunning():
645        #    self.calc_fit.stop()
646        msg = "Fitting is in progress..."
647        wx.PostEvent( self.parent, StatusEvent(status=msg, type="progress" ))
648       
649        #Handler used for park engine displayed message
650        handler = ConsoleUpdate(parent=self.parent,
651                                manager=self,
652                                improvement_delta=0.1)
653       
654        self._mac_sleep(0.2)
655        ## perform single fit
656       
657        if fitproblem_count == 1:
658            calc_fit = FitThread(handler = handler,
659                                    fn=fitter,
660                                    pars=pars,
661                                    page_id=list_page_id,
662                                    completefn=self._single_fit_completed,
663                                    ftol=self.ftol)
664        else:
665            current_page_id = self.sim_page.uid
666            ## Perform more than 1 fit at the time
667            calc_fit = FitThread(handler=handler,
668                                    fn=fitter,
669                                    page_id=list_page_id,
670                                    updatefn=handler.update_fit,
671                                    completefn=self._simul_fit_completed,
672                                    ftol=self.ftol)
673
674        self.fit_thread_list[current_page_id] = calc_fit
675        self._mac_sleep(0.2)
676       
677        #self.ready_fit(calc_fit=calc_fit)
678        calc_fit.queue()
679           
680    def ready_fit(self, calc_fit):
681        """
682        Ready for another fit
683        """
684        if self.fitproblem_count != None and self.fitproblem_count > 1:
685            calc_fit.ready(2.5)
686           
687        else:
688            time.sleep(0.4)
689           
690    def remove_plot(self, uid, theory=False):
691        """
692        remove model plot when a fit page is closed
693        """
694        fitproblem = self.page_finder[uid]
695        data = fitproblem.get_fit_data()
696        model = fitproblem.get_model()
697        plot_id = None
698        if model is not None:
699            plot_id = data.id + name
700        if theory:
701            plot_id = data.id
702        group_id = data.group_id
703        wx.PostEvent(self.parent, NewPlotEvent(id=plot_id,
704                                                   group_id=group_id,
705                                                   action='remove'))
706           
707    def store_data(self, uid, data=None, caption=None):
708        """
709        Helper to save page reference into the plug-in
710       
711        :param page: page to store
712       
713        """
714        #create a fitproblem storing all link to data,model,page creation
715        if not uid in self.page_finder.keys():
716            self.page_finder[uid] = FitProblem()
717        self.page_finder[uid].set_fit_data(data)
718        self.page_finder[uid].set_fit_tab_caption(caption)
719       
720    def on_add_new_page(self, event=None):
721        """
722        ask fit panel to create a new empty page
723        """
724        try:
725            page = self.fit_panel.add_empty_page()
726            page_caption = page.window_name
727            # add data associated to the page created
728            if page != None: 
729                self.store_data(uid=page.uid, caption=page_caption,
730                                data=page.get_data())
731                wx.PostEvent(self.parent, StatusEvent(status="Page Created",
732                                               info="info"))
733            else:
734                msg = "Page was already Created"
735                wx.PostEvent(self.parent, StatusEvent(status=msg,
736                                                       info="warning"))
737            self.set_top_panel()
738        except:
739            raise
740            #msg = "Creating Fit page: %s"%sys.exc_value
741            #wx.PostEvent(self.parent, StatusEvent(status=msg, info="error"))
742       
743       
744    def add_fit_page(self, data):
745        """
746        given a data, ask to the fitting panel to create a new fitting page,
747        get this page and store it into the page_finder of this plug-in
748        """
749        page = self.fit_panel.set_data(data)
750        page_caption = page.window_name
751        #append Data1D to the panel containing its theory
752        #if theory already plotted
753        if page.uid in self.page_finder:
754            theory_data = self.page_finder[page.uid].get_theory_data()
755            if issubclass(data.__class__, Data2D):
756                data.group_id = wx.NewId()
757                if theory_data is not None:
758                    group_id = str(page.uid) + " Model1D"
759                    wx.PostEvent(self.parent, 
760                             NewPlotEvent(group_id=group_id,
761                                               action="delete"))
762                    self.parent.update_data(prev_data=theory_data, new_data=data)     
763            else:
764                if theory_data is not None:
765                    group_id = str(page.uid) + " Model2D"
766                    data.group_id = theory_data.group_id
767                    wx.PostEvent(self.parent, 
768                             NewPlotEvent(group_id=group_id,
769                                               action="delete"))
770                    self.parent.update_data(prev_data=theory_data, new_data=data)   
771             
772        self.store_data(uid=page.uid, data=data, caption=page.window_name)
773        if self.sim_page is not None:
774            self.sim_page.draw_page()
775        return page
776           
777    def _onEVT_SLICER_PANEL(self, event):
778        """
779        receive and event telling to update a panel with a name starting with
780        event.panel_name. this method update slicer panel for a given interactor.
781       
782        :param event: contains type of slicer , paramaters for updating the panel
783            and panel_name to find the slicer 's panel concerned.
784        """
785        for item in self.parent.panels:
786            if self.parent.panels[item].window_caption.startswith(event.panel_name):
787                self.parent.panels[item].set_slicer(event.type, event.params)
788               
789        self.parent._mgr.Update()
790   
791    def _closed_fitpage(self, event):   
792        """
793        request fitpanel to close a given page when its unique data is removed
794        from the plot. close fitpage only when the a loaded data is removed
795        """   
796        if event is None or event.data is None:
797            return
798        if hasattr(event.data,"is_data"):
799            if not event.data.is_data or \
800                event.data.__class__.__name__ == "Data1D":
801                self.fit_panel.close_page_with_data(event.data) 
802       
803    def _add_page_onmenu(self, name, fitproblem=None):
804        """
805        Add name of a closed page of fitpanel in a menu
806        """
807        list = self.menu1.GetMenuItems()
808        for item in list:
809            if name == item.GetItemLabel():
810                self.closed_page_dict[name][1] = fitproblem
811               
812        if not name in self.closed_page_dict.keys():   
813            # Post paramters
814            event_id = wx.NewId()
815            self.menu1.Append(event_id, name, "Show %s fit panel" % name)
816            self.closed_page_dict[name]= [event_id, fitproblem]
817            wx.EVT_MENU(self.parent, event_id, self._open_closed_page)
818       
819    def _open_closed_page(self, event):   
820        """
821        reopen a closed page
822        """
823        for name, value in self.closed_page_dict.iteritems():
824            if event.GetId() in value:
825                uid,fitproblem = value
826                if name !="Model":
827                    data= fitproblem.get_fit_data()
828                    page = self.fit_panel.add_fit_page(data=data, reset=True)
829                    if fitproblem != None:
830                        self.page_finder[uid] = fitproblem
831                        if self.sim_page != None:
832                            self.sim_page.draw_page()
833                           
834                else:
835                    model = fitproblem
836                    self.fit_panel.add_model_page(model=model, topmenu=True,
837                                                  reset= True)
838                    break
839   
840    def _reset_schedule_problem(self, value=0, uid=None):
841        """
842        unschedule or schedule all fitproblem to be fit
843        """
844        # case that uid is not specified
845        if uid == None:
846            for page_id in self.page_finder.keys():
847                self.page_finder[page_id].schedule_tofit(value)
848        # when uid is given
849        else:
850            self.page_finder[uid].schedule_tofit(value)
851               
852    def _fit_helper(self, pars, value, fitproblem_id,fitter, title="Single Fit " ):
853        """
854        helper for fitting
855        """
856        metadata = value.get_fit_data()
857        model = value.get_model()
858        smearer = value.get_smearer()
859        qmin, qmax = value.get_range()
860        self.fit_id = fitproblem_id
861        #Create list of parameters for fitting used
862        templist = []
863       
864        try:
865            #Extra list of parameters and their constraints
866            listOfConstraint = []
867           
868            param = value.get_model_param()
869            if len(param) > 0:
870                for item in param:
871                    ## check if constraint
872                    if item[0] != None and item[1] != None:
873                        listOfConstraint.append((item[0],item[1]))
874                   
875            #Do the single fit
876            fitter.set_model(model, self.fit_id,
877                                   pars, constraints=listOfConstraint)
878           
879            fitter.set_data(data=metadata, id=self.fit_id,
880                                 smearer=smearer, qmin=qmin, qmax=qmax)
881           
882            fitter.select_problem_for_fit(id=self.fit_id,
883                                               value=value.get_scheduled())
884            value.clear_model_param()
885        except:
886            raise
887            #msg = title + " error: %s" % sys.exc_value
888            #wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
889         
890    def _onSelect(self,event):
891        """
892        when Select data to fit a new page is created .Its reference is
893        added to self.page_finder
894        """
895        self.panel = event.GetEventObject()
896        Plugin.on_perspective(self, event=event)
897        for plottable in self.panel.graph.plottables:
898            if plottable.__class__.__name__ in ["Data1D", "Theory1D"]:
899                data_id = self.panel.graph.selected_plottable
900                if plottable == self.panel.plots[data_id]:
901                    data = plottable
902                    self.add_fit_page(data=data)
903                    return
904            else:
905                data = plottable
906                self.add_fit_page(data=data)
907        self.set_top_panel()
908           
909    def update_fit(self, result=None, msg=""):
910        """
911        """
912        print "update_fit result", result
913       
914    def _single_fit_completed(self, result, pars, page_id, elapsed=None):
915        """
916        Display fit result on one page of the notebook.
917       
918        :param result: result of fit
919        :param pars: list of names of parameters fitted
920        :param current_pg: the page where information will be displayed
921        :param qmin: the minimum value of x to replot the model
922        :param qmax: the maximum value of x to replot model
923         
924        """ 
925        self._mac_sleep(0.2)
926        if page_id[0] in self.fit_thread_list.keys():
927            del self.fit_thread_list[page_id[0]] 
928        try:
929            if result == None:
930                self._update_fit_button(page_id)
931                msg= "Single Fitting did not converge!!!"
932                wx.PostEvent(self.parent, 
933                             StatusEvent(status=msg, 
934                                         info="warning",
935                                         type="stop"))
936                return
937            if not numpy.isfinite(result.fitness) or \
938                    numpy.any(result.pvec == None) or \
939                    not numpy.all(numpy.isfinite(result.pvec)):
940                msg = "Single Fitting did not converge!!!"
941                wx.PostEvent(self.parent, 
942                             StatusEvent(status=msg, 
943                                         info="warning",
944                                         type="stop"))
945                self._update_fit_button(page_id)
946                return
947           
948            for uid in page_id:
949                value = self.page_finder[uid]   
950                model = value.get_model()
951                page_id = uid
952                   
953                param_name = []
954                for name in pars:
955                    param_name.append(name)
956   
957                cpage = self.fit_panel.get_page_by_id(uid)
958
959                cpage.onsetValues(result.fitness, 
960                                  param_name, result.pvec,result.stderr)
961                wx.CallAfter(cpage._on_fit_complete)
962            if result.stderr == None:
963                msg = "Fit Abort: "
964            else:
965                msg = "Fitting: "
966            msg += "Completed!!!"
967            wx.PostEvent(self.parent, StatusEvent(status=msg))
968            return
969        except ValueError:
970            self._update_fit_button(page_id)
971            msg = "Single Fitting did not converge!!!"
972            wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
973                                                  type="stop"))
974            return 
975        except:
976            self._update_fit_button(page_id)
977            msg = "Single Fit completed but Following"
978            msg += " error occurred:%s" % sys.exc_value
979            wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
980                                                  type="stop"))
981            raise
982            return
983       
984    def _simul_fit_completed(self, result, page_id,pars=None, elapsed=None):
985        """
986        Parameter estimation completed,
987        display the results to the user
988       
989        :param alpha: estimated best alpha
990        :param elapsed: computation time
991       
992        """
993        self.fit_thread_list = {}
994        if page_id is None:
995            page_id = []
996        ## fit more than 1 model at the same time
997        self._mac_sleep(0.2) 
998        try:
999            msg = "" 
1000            if result == None:
1001                self._update_fit_button(page_id)
1002                msg= "Complex Fitting did not converge!!!"
1003                wx.PostEvent(self.parent, StatusEvent(status=msg,
1004                                                      type="stop"))
1005                return
1006            if not numpy.isfinite(result.fitness) or \
1007                numpy.any(result.pvec == None) or not \
1008                numpy.all(numpy.isfinite(result.pvec)):
1009                self._update_fit_button(page_id)
1010                msg= "Simultaneous Fitting did not converge!!!"
1011                wx.PostEvent(self.parent, StatusEvent(status=msg,type="stop"))
1012                return
1013             
1014            for uid in page_id:   
1015                value = self.page_finder[uid]
1016                model = value.get_model()
1017                data =  value.get_fit_data()
1018                small_param_name = []
1019                small_out = []
1020                small_cov = []
1021                #Separate result in to data corresponding to each page
1022                for p in result.parameters:
1023                    model_name, param_name = self.split_string(p.name) 
1024                    if model.name == model_name:
1025                        p_name= model.name+"."+param_name
1026                        if p.name == p_name:     
1027                            if p.value != None and numpy.isfinite(p.value):
1028                                small_out.append(p.value)
1029                                small_param_name.append(param_name)
1030                                small_cov.append(p.stderr)
1031                # Display result on each page
1032                cpage = self.fit_panel.get_page_by_id(uid)
1033                cpage.onsetValues(result.fitness,
1034                                  small_param_name,
1035                                  small_out,small_cov)
1036                wx.CallAfter(cpage._on_fit_complete)
1037                msg = "Fit completed!"
1038                wx.PostEvent(self.parent, StatusEvent(status=msg))
1039        except Exception:
1040            self._update_fit_button(page_id)
1041            msg = "Complex Fitting did not converge!!!"
1042            wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
1043                                                  type="stop"))
1044            return
1045
1046        except:
1047            self._update_fit_button(page_id)
1048            msg = "Simultaneous Fit completed"
1049            msg += " but Following error occurred:%s" % sys.exc_value
1050            wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1051   
1052    def _update_fit_button(self, page_id):
1053        """
1054        Update Fit button when fit stopped
1055       
1056        : parameter page_id: fitpage where the button is
1057        """
1058        if page_id.__class__.__name__ != 'list':
1059            page_id = [page_id]
1060        for uid in page_id: 
1061            page = self.fit_panel.get_page_by_id(uid)
1062            page._on_fit_complete()
1063       
1064    def _on_show_panel(self, event):
1065        """
1066        """
1067        pass
1068       
1069    def _onset_engine_park(self,event):
1070        """
1071        set engine to park
1072        """
1073        self._on_change_engine('park')
1074       
1075    def _onset_engine_scipy(self,event):
1076        """
1077        set engine to scipy
1078        """
1079        self._on_change_engine('scipy')
1080       
1081    def _on_slicer_event(self, event):
1082        """
1083        Receive a panel as event and send it to guiframe
1084       
1085        :param event: event containing a panel
1086       
1087        """
1088        if event.panel is not None:
1089            new_panel = event.panel
1090            self.slicer_panels.append(event.panel)
1091            # Set group ID if available
1092            event_id = self.parent.popup_panel(new_panel)
1093            #self.menu3.Append(event_id, new_panel.window_caption,
1094            #                 "Show %s plot panel" % new_panel.window_caption)
1095            # Set id to allow us to reference the panel later
1096         
1097            new_panel.uid = event_id
1098            self.mypanels.append(new_panel) 
1099       
1100    def _onclearslicer(self, event):
1101        """
1102        Clear the boxslicer when close the panel associate with this slicer
1103        """
1104        name =event.GetPane().caption
1105   
1106        for panel in self.slicer_panels:
1107            if panel.window_caption==name:
1108               
1109                for item in self.parent.panels:
1110                    if hasattr(self.parent.panels[item],"uid"):
1111                        if self.parent.panels[item].uid ==panel.base.uid:
1112                            self.parent.panels[item].onClearSlicer(event)
1113                            self.parent._mgr.Update()
1114                            break 
1115                break
1116   
1117    def _return_engine_type(self):
1118        """
1119        return the current type of engine
1120        """
1121        return self._fit_engine
1122     
1123     
1124    def _on_change_engine(self, engine='park'):
1125        """
1126        Allow to select the type of engine to perform fit
1127       
1128        :param engine: the key work of the engine
1129       
1130        """
1131        ## saving fit engine name
1132        self._fit_engine = engine
1133        ## change menu item state
1134        if engine=="park":
1135            self.menu1.FindItemById(self.park_id).Check(True)
1136            self.menu1.FindItemById(self.scipy_id).Check(False)
1137        else:
1138            self.menu1.FindItemById(self.park_id).Check(False)
1139            self.menu1.FindItemById(self.scipy_id).Check(True)
1140        ## post a message to status bar
1141        msg = "Engine set to: %s" % self._fit_engine
1142        wx.PostEvent(self.parent, 
1143                     StatusEvent(status=msg))
1144        ## send the current engine type to fitpanel
1145        self.fit_panel._on_engine_change(name=self._fit_engine)
1146
1147       
1148    def _on_model_panel(self, evt):
1149        """
1150        react to model selection on any combo box or model menu.plot the model 
1151       
1152        :param evt: wx.combobox event
1153       
1154        """
1155        model = evt.model
1156        uid = evt.uid
1157        qmin = evt.qmin
1158        qmax = evt.qmax
1159        smearer = evt.smearer
1160       
1161        if model == None:
1162            return
1163       
1164        if self.page_finder[uid].get_model() is None:
1165            model.name = "M" + str(self.index_model)
1166            self.index_model += 1 
1167        else:
1168            model.name = self.page_finder[uid].get_model().name
1169        # save the name containing the data name with the appropriate model
1170        self.page_finder[uid].set_model(model)
1171        self.page_finder[uid].set_range(qmin=qmin, qmax=qmax)
1172        if self.sim_page is not None:
1173            self.sim_page.draw_page()
1174       
1175    def _update1D(self,x, output):
1176        """
1177        Update the output of plotting model 1D
1178        """
1179        msg = "Plot updating ... "
1180        wx.PostEvent(self.parent, StatusEvent(status=msg,type="update"))
1181        #self.ready_fit()
1182       
1183   
1184    def _fill_default_model2D(self, theory, page_id, qmax,qstep, qmin=None):
1185        """
1186        fill Data2D with default value
1187       
1188        :param theory: Data2D to fill
1189       
1190        """
1191        from DataLoader.data_info import Detector, Source
1192       
1193        detector = Detector()
1194        theory.detector.append(detector)         
1195        theory.source= Source()
1196       
1197        ## Default values   
1198        theory.detector[0].distance= 8000   # mm       
1199        theory.source.wavelength= 6         # A     
1200        theory.detector[0].pixel_size.x= 5  # mm
1201        theory.detector[0].pixel_size.y= 5  # mm
1202       
1203        theory.detector[0].beam_center.x= qmax
1204        theory.detector[0].beam_center.y= qmax
1205   
1206        ## create x_bins and y_bins of the model 2D
1207        pixel_width_x = theory.detector[0].pixel_size.x
1208        pixel_width_y = theory.detector[0].pixel_size.y
1209        center_x      = theory.detector[0].beam_center.x/pixel_width_x
1210        center_y      = theory.detector[0].beam_center.y/pixel_width_y
1211
1212        # theory default: assume the beam
1213        #center is located at the center of sqr detector
1214        xmax = qmax
1215        xmin = -qmax
1216        ymax = qmax
1217        ymin = -qmax
1218       
1219        x=  numpy.linspace(start= -1*qmax,
1220                               stop=qmax,
1221                               num=qstep,
1222                               endpoint=True) 
1223        y = numpy.linspace(start=-1*qmax,
1224                               stop= qmax,
1225                               num= qstep,
1226                               endpoint=True)
1227         
1228        ## use data info instead
1229        new_x = numpy.tile(x, (len(y),1))
1230        new_y = numpy.tile(y, (len(x),1))
1231        new_y = new_y.swapaxes(0,1)
1232       
1233        # all data reuire now in 1d array
1234        qx_data = new_x.flatten()
1235        qy_data = new_y.flatten()
1236       
1237        q_data = numpy.sqrt(qx_data*qx_data+qy_data*qy_data)
1238        # set all True (standing for unmasked) as default
1239        mask    = numpy.ones(len(qx_data), dtype = bool)
1240       
1241        # calculate the range of qx and qy: this way,
1242        # it is a little more independent
1243        x_size = xmax- xmin
1244        y_size = ymax -ymin
1245       
1246        # store x and y bin centers in q space
1247        x_bins  = x
1248        y_bins  = y
1249        # bin size: x- & y-directions
1250        xstep = x_size/len(x_bins-1)
1251        ystep = y_size/len(y_bins-1)
1252       
1253        #theory.data = numpy.zeros(len(mask))
1254        theory.err_data = numpy.ones(len(mask))
1255        theory.qx_data = qx_data
1256        theory.qy_data = qy_data 
1257        theory.q_data = q_data
1258        theory.mask = mask           
1259        theory.x_bins = x_bins 
1260        theory.y_bins = y_bins   
1261       
1262        # max and min taking account of the bin sizes
1263        theory.xmin = xmin
1264        theory.xmax = xmax
1265        theory.ymin = ymin
1266        theory.ymax = ymax
1267        theory.group_id = str(page_id) + " Model2D"
1268        theory.id = str(page_id) + " Model2D"
1269 
1270    def _complete1D(self, x,y, page_id, elapsed,index,model,
1271                    toggle_mode_on=False,state=None, 
1272                    data=None, update_chisqr=True):
1273        """
1274        Complete plotting 1D data
1275        """ 
1276        try:
1277            new_plot = Data1D(x=x, y=y)
1278            new_plot.is_data = False
1279            new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
1280            if data != None:
1281                _xaxis, _xunit = data.get_xaxis() 
1282                _yaxis, _yunit = data.get_yaxis() 
1283                new_plot.title = data.name
1284                #if the theory is already plotted use the same group id
1285                #to replot
1286                if page_id in self.page_finder:
1287                    theory_data = self.page_finder[page_id].get_theory_data()
1288                    if theory_data is not None:
1289                       data.group_id = theory_data.group_id
1290                #data is plotted before the theory, then take its group_id
1291                #assign to the new theory
1292                new_plot.group_id = data.group_id
1293               
1294            else:
1295                _xaxis, _xunit = "\\rm{Q}", 'A^{-1}'
1296                _yaxis, _yunit = "\\rm{Intensity} ", "cm^{-1}"
1297                new_plot.title = "Analytical model 1D "
1298                #find a group id to plot theory without data
1299                new_plot.group_id =  str(page_id) + " Model1D" 
1300            new_plot.id =  str(page_id) + " Model1D" 
1301           
1302            #find if this theory was already plotted and replace that plot given
1303            #the same id
1304           
1305            theory_data = self.page_finder[page_id].get_theory_data()
1306            if theory_data is not None:
1307                new_plot.id = theory_data.id
1308             
1309            new_plot.name = model.name + " ["+ str(model.__class__.__name__)+ "]"
1310            new_plot.xaxis(_xaxis, _xunit)
1311            new_plot.yaxis(_yaxis, _yunit)
1312            if toggle_mode_on:
1313                new_plot.id =  str(page_id) + " Model" 
1314                wx.PostEvent(self.parent, 
1315                             NewPlotEvent(group_id=str(page_id) + " Model2D",
1316                                               action="Hide"))
1317           
1318            self.page_finder[page_id].set_theory_data(new_plot)
1319            if data is None:
1320                data_id = None
1321            else:
1322                data_id = data.id
1323
1324            self.parent.update_theory(data_id=data_id, 
1325                                       theory=new_plot,
1326                                       state=state)   
1327 
1328            current_pg = self.fit_panel.get_page_by_id(page_id)
1329            title = new_plot.title
1330           
1331            wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1332                                            title= str(title)))
1333
1334            self.page_finder[page_id].set_theory_data(new_plot)
1335
1336            if update_chisqr:
1337                wx.PostEvent(current_pg,
1338                             Chi2UpdateEvent(output=self._cal_chisqr(data=data,
1339                                                        page_id=page_id,
1340                                                        index=index)))
1341            else:
1342                self._plot_residuals(page_id, data, index)
1343
1344            msg = "Computation  completed!"
1345            wx.PostEvent( self.parent, StatusEvent(status=msg, type="stop" ))
1346
1347            #self.current_pg.state.theory_data = deepcopy(self.theory_data)
1348        except:
1349            raise
1350            #msg = " Error occurred when drawing %s Model 1D: " % new_plot.name
1351            #msg += " %s"  % sys.exc_value
1352            #wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1353   
1354    def _update2D(self, output,time=None):
1355        """
1356        Update the output of plotting model
1357        """
1358        wx.PostEvent(self.parent, StatusEvent(status="Plot \
1359        #updating ... ", type="update"))
1360        #self.ready_fit()
1361 
1362    def _complete2D(self, image, data, model, page_id,  elapsed, index, qmin,
1363                     qmax, toggle_mode_on=False,state=None,qstep=DEFAULT_NPTS, 
1364                     update_chisqr=True):
1365        """
1366        Complete get the result of modelthread and create model 2D
1367        that can be plot.
1368        """
1369        err_image = numpy.zeros(numpy.shape(image))
1370       
1371        new_plot= Data2D(image=image, err_image=err_image)
1372        new_plot.name = model.name
1373        new_plot.title = "Analytical model 2D "
1374        if data is None:
1375            self._fill_default_model2D(theory=new_plot, 
1376                                       qmax=qmax, 
1377                                       page_id=page_id,
1378                                       qstep=qstep,
1379                                        qmin= qmin)
1380           
1381        else:
1382            new_plot.id = str(page_id) + " Model2D"
1383            new_plot.group_id = str(page_id) + " Model2D"
1384            new_plot.x_bins = data.x_bins
1385            new_plot.y_bins = data.y_bins
1386            new_plot.detector = data.detector
1387            new_plot.source = data.source
1388            new_plot.is_data = False 
1389            new_plot.qx_data = data.qx_data
1390            new_plot.qy_data = data.qy_data
1391            new_plot.q_data = data.q_data
1392            #numpy.zeros(len(data.err_data))#data.err_data
1393            new_plot.err_data = err_image
1394            new_plot.mask = data.mask
1395            ## plot boundaries
1396            new_plot.ymin = data.ymin
1397            new_plot.ymax = data.ymax
1398            new_plot.xmin = data.xmin
1399            new_plot.xmax = data.xmax
1400            title = data.title
1401            if len(title) > 1:
1402                new_plot.title = "Model2D for " + data.name
1403        new_plot.is_data = False
1404        new_plot.name = model.name + " ["+ str(model.__class__.__name__)+ "]"
1405
1406        theory_data = deepcopy(new_plot)
1407        theory_data.name = "Unknown"
1408        if toggle_mode_on:
1409            new_plot.id = str(page_id) + " Model"     
1410            wx.PostEvent(self.parent, 
1411                             NewPlotEvent(group_id=str(page_id) + " Model1D",
1412                                               action="Hide"))
1413       
1414        self.page_finder[page_id].set_theory_data(new_plot)
1415        if data is None:
1416            data_id = None
1417        else:
1418            data_id = data.id
1419        self.parent.update_theory(data_id=data_id, 
1420                                       theory=new_plot,
1421                                       state=state) 
1422        current_pg = self.fit_panel.get_page_by_id(page_id)
1423        title = new_plot.title
1424
1425        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1426                                               title=title))
1427        self.page_finder[page_id].set_theory_data(new_plot)
1428        # Chisqr in fitpage
1429        if update_chisqr:
1430            wx.PostEvent(current_pg,
1431                         Chi2UpdateEvent(output=\
1432                                    self._cal_chisqr(data=data,
1433                                                     page_id=page_id,
1434                                                     index=index)))
1435        else:
1436            self._plot_residuals(page_id, data, index)
1437        msg = "Computation  completed!"
1438        wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1439   
1440    def _draw_model2D(self, model, page_id, data=None, smearer=None,
1441                      description=None, enable2D=False,
1442                      state=None,
1443                      toggle_mode_on=False,
1444                      qmin=DEFAULT_QMIN, qmax=DEFAULT_QMAX,
1445                      qstep=DEFAULT_NPTS,
1446                      update_chisqr=True):
1447        """
1448        draw model in 2D
1449       
1450        :param model: instance of the model to draw
1451        :param description: the description of the model
1452        :param enable2D: when True allows to draw model 2D
1453        :param qmin: the minimum value to  draw model 2D
1454        :param qmax: the maximum value to draw model 2D
1455        :param qstep: the number of division of Qx and Qy of the model to draw
1456           
1457        """
1458        x=  numpy.linspace(start=-1*qmax,
1459                               stop=qmax,
1460                               num=qstep,
1461                               endpoint=True) 
1462        y = numpy.linspace(start= -1*qmax,
1463                               stop=qmax,
1464                               num=qstep,
1465                               endpoint=True)
1466        if model is None:
1467            msg = "Panel with ID: %s does not contained model" % str(page_id)
1468            raise ValueError, msg
1469        ## use data info instead
1470        if data is not None:
1471            ## check if data2D to plot
1472            if hasattr(data, "x_bins"):
1473                enable2D = True
1474                x = data.x_bins
1475                y = data.y_bins
1476               
1477        if not enable2D:
1478            return None, None
1479        try:
1480            from model_thread import Calc2D
1481            ## If a thread is already started, stop it
1482            if (self.calc_2D is not None) and self.calc_2D.isrunning():
1483                self.calc_2D.stop()
1484            self.calc_2D = Calc2D(x=x,
1485                                    y=y,
1486                                    model=model, 
1487                                    data=data,
1488                                    page_id=page_id,
1489                                    smearer=smearer,
1490                                    qmin=qmin,
1491                                    qmax=qmax,
1492                                    qstep=qstep,
1493                                    toggle_mode_on=toggle_mode_on,
1494                                    state=state,
1495                                    completefn=self._complete2D,
1496                                    #updatefn= self._update2D,
1497                                    update_chisqr=update_chisqr)
1498
1499            self.calc_2D.queue()
1500
1501        except:
1502            raise
1503            #msg = " Error occurred when drawing %s Model 2D: " % model.name
1504            #msg += " %s" % sys.exc_value
1505            #wx.PostEvent(self.parent, StatusEvent(status=msg))
1506
1507    def _draw_model1D(self, model, page_id, data=None, smearer=None,
1508                qmin=DEFAULT_QMIN, qmax=DEFAULT_QMAX, 
1509                state=None,
1510                toggle_mode_on=False,
1511                qstep=DEFAULT_NPTS, update_chisqr=True, 
1512                enable1D=True):
1513        """
1514        Draw model 1D from loaded data1D
1515       
1516        :param data: loaded data
1517        :param model: the model to plot
1518       
1519        """
1520        x=  numpy.linspace(start=qmin,
1521                           stop=qmax,
1522                           num=qstep,
1523                           endpoint=True
1524                           )
1525        if data is not None:
1526            ## check for data2D
1527            if hasattr(data,"x_bins"):
1528                return
1529            x = data.x
1530            if qmin == None :
1531                qmin == DEFAULT_QMIN
1532
1533            if qmax == None:
1534                qmax == DEFAULT_QMAX
1535        if not enable1D:
1536            return 
1537        try:
1538            from model_thread import Calc1D
1539            ## If a thread is already started, stop it
1540            if (self.calc_1D is not None) and self.calc_1D.isrunning():
1541                self.calc_1D.stop()
1542            self.calc_1D = Calc1D(x=x,
1543                                  data=data,
1544                                  model=model,
1545                                  page_id=page_id, 
1546                                  qmin=qmin,
1547                                  qmax=qmax,
1548                                  smearer=smearer,
1549                                  state=state,
1550                                  toggle_mode_on=toggle_mode_on,
1551                                  completefn=self._complete1D,
1552                                  #updatefn = self._update1D,
1553                                  update_chisqr=update_chisqr)
1554
1555            self.calc_1D.queue()
1556        except:
1557            msg = " Error occurred when drawing %s Model 1D: " % model.name
1558            msg += " %s" % sys.exc_value
1559            wx.PostEvent(self.parent, StatusEvent(status=msg))
1560
1561    def _cal_chisqr(self, page_id, data=None, index=None): 
1562        """
1563        Get handy Chisqr using the output from draw1D and 2D,
1564        instead of calling expansive CalcChisqr in guithread
1565        """
1566        # default chisqr
1567        chisqr = None
1568
1569        # return None if data == None
1570        if data == None: return chisqr
1571       
1572        # Get data: data I, theory I, and data dI in order
1573        if data.__class__.__name__ == "Data2D":
1574            if index == None: 
1575                index = numpy.ones(len(data.data),ntype=bool)
1576            # get rid of zero error points
1577            index = index & (data.err_data != 0 ) 
1578            index = index & (numpy.isfinite(data.data)) 
1579            fn = data.data[index] 
1580            theory_data = self.page_finder[page_id].get_theory_data()
1581            gn = theory_data.data[index]
1582            en = data.err_data[index]
1583        else:
1584            # 1 d theory from model_thread is only in the range of index
1585            if index == None: 
1586                index = numpy.ones(len(data.y), ntype=bool)
1587            if data.dy == None or data.dy == []:
1588                dy = numpy.ones(len(data.y))
1589            else:
1590                ## Set consitently w/AbstractFitengine:
1591                # But this should be corrected later.
1592                dy = deepcopy(data.dy)
1593                dy[dy==0] = 1 
1594            fn = data.y[index] 
1595            theory_data = self.page_finder[page_id].get_theory_data()
1596            gn = theory_data.y
1597            en = dy[index]
1598
1599        # residual
1600        res = (fn - gn) / en
1601        residuals = res[numpy.isfinite(res)]
1602        # get chisqr only w/finite
1603        chisqr = numpy.average(residuals * residuals)
1604       
1605        self._plot_residuals(page_id, data, index)
1606        return chisqr
1607   
1608
1609       
1610    def _plot_residuals(self, page_id, data=None, index=None): 
1611        """
1612        Plot the residuals
1613       
1614        :param data: data
1615        :param index: index array (bool)
1616        : Note: this is different from the residuals in cal_chisqr()
1617        """
1618        if data == None: 
1619            return 
1620       
1621        # Get data: data I, theory I, and data dI in order
1622        if data.__class__.__name__ == "Data2D":
1623            # build residuals
1624            #print data
1625            residuals = Data2D()
1626            #residuals.copy_from_datainfo(data)
1627            # Not for trunk the line below, instead use the line above
1628            data.clone_without_data(len(data.data), residuals)
1629            residuals.data = None
1630            fn = data.data#[index]
1631            theory_data = self.page_finder[page_id].get_theory_data()
1632            gn = theory_data.data#[index]
1633            en = data.err_data#[index]
1634            residuals.data = (fn - gn) / en
1635            residuals.qx_data = data.qx_data#[index]
1636            residuals.qy_data = data.qy_data #[index]
1637            residuals.q_data = data.q_data#[index]
1638            residuals.err_data = numpy.ones(len(residuals.data))#[index]
1639            residuals.xmin = min(residuals.qx_data)
1640            residuals.xmax = max(residuals.qx_data)
1641            residuals.ymin = min(residuals.qy_data)
1642            residuals.ymax = max(residuals.qy_data)
1643            residuals.q_data = data.q_data#[index]
1644            residuals.mask = data.mask
1645            residuals.scale = 'linear'
1646            #print "print data",residuals
1647            # check the lengths
1648            if len(residuals.data) != len(residuals.q_data):
1649                return
1650
1651        else:
1652            # 1 d theory from model_thread is only in the range of index
1653            if data.dy == None or data.dy == []:
1654                dy = numpy.ones(len(data.y))
1655            else:
1656                ## Set consitently w/AbstractFitengine:
1657                ## But this should be corrected later.
1658                dy = deepcopy(data.dy)
1659                dy[dy==0] = 1 
1660            fn = data.y[index] 
1661            theory_data = self.page_finder[page_id].get_theory_data()
1662            gn = theory_data.y
1663            en = dy[index]
1664            # build residuals
1665            residuals = Data1D()
1666            residuals.y = (fn - gn) / en
1667            residuals.x = data.x[index]
1668            residuals.dy = numpy.ones(len(residuals.y))
1669            residuals.dx = None
1670            residuals.dxl = None
1671            residuals.dxw = None
1672            residuals.ytransform = 'y'
1673            # For latter scale changes
1674            residuals.xaxis('\\rm{Q} ', 'A^{-1}')
1675            residuals.yaxis('\\rm{Residuals} ', 'normalized')
1676           
1677        new_plot = residuals
1678        if data.id == None:
1679            data.id = data.name
1680        name  = data.id
1681        new_plot.name = "Residuals for " + str(data.name)
1682        ## allow to highlight data when plotted
1683        new_plot.interactive = True
1684        ## when 2 data have the same id override the 1 st plotted
1685        new_plot.id = new_plot.name#name + " residuals"
1686        ##group_id specify on which panel to plot this data
1687        new_plot.group_id = new_plot.id
1688        #new_plot.is_data = True
1689        ##post data to plot
1690        title = new_plot.name
1691       
1692        # plot data
1693        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title=title))   
1694       
1695#def profile(fn, *args, **kw):
1696#    import cProfile, pstats, os
1697#    global call_result
1698#    def call():
1699#        global call_result
1700#        call_result = fn(*args, **kw)
1701#    cProfile.runctx('call()', dict(call=call), {}, 'profile.out')
1702#    stats = pstats.Stats('profile.out')
1703#    #stats.sort_stats('time')
1704#    stats.sort_stats('calls')
1705#    stats.print_stats()
1706#    os.unlink('profile.out')
1707#    return call_result
1708if __name__ == "__main__":
1709    i = Plugin()
1710   
1711   
1712   
1713   
Note: See TracBrowser for help on using the repository browser.