source: sasview/sansview/perspectives/fitting/fitting.py @ 818589a

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 818589a was 818589a, checked in by Jae Cho <jhjcho@…>, 14 years ago

fixed a problem on report not including all plotpanels

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