source: sasview/sansview/perspectives/fitting/fitting.py @ 58c90ba

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

print statement on drawmodel

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