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

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 ea4dfe0 was 8b481a51, checked in by Gervaise Alina <gervyh@…>, 13 years ago

add complete function for simul fit

  • Property mode set to 100644
File size: 63.6 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.scipy_id = wx.NewId()
98        self.park_id = wx.NewId()
99       
100        self.temp_state = []
101        self.state_index = 0
102        self.sfile_ext = None
103        # take care of saving  data, model and page associated with each other
104        self.page_finder = {}
105        # Log startup
106        logging.info("Fitting plug-in started") 
107       
108    def populate_menu(self, owner):
109        """
110        Create a menu for the Fitting plug-in
111       
112        :param id: id to create a menu
113        :param owner: owner of menu
114       
115        :return: list of information to populate the main menu
116       
117        """
118        #Menu for fitting
119        self.menu1 = wx.Menu()
120        id1 = wx.NewId()
121        simul_help = "Add new fit panel"
122        self.menu1.Append(id1, '&New Fit Page',simul_help)
123        wx.EVT_MENU(owner, id1, self.on_add_new_page)
124        self.menu1.AppendSeparator()
125        id1 = wx.NewId()
126        simul_help = "Simultaneous Fit"
127        self.menu1.Append(id1, '&Simultaneous Fit',simul_help)
128        wx.EVT_MENU(owner, id1, self.on_add_sim_page)
129        self.menu1.AppendSeparator()
130        #Set park engine
131       
132        scipy_help= "Scipy Engine: Perform Simple fit. More in Help window...."
133        self.menu1.AppendCheckItem(self.scipy_id, "Simple Fit  [Scipy]",scipy_help) 
134        wx.EVT_MENU(owner, self.scipy_id,  self._onset_engine_scipy)
135       
136        park_help = "Park Engine: Perform Complex fit. More in Help window...."
137        self.menu1.AppendCheckItem(self.park_id, "Complex Fit  [Park]",park_help) 
138        wx.EVT_MENU(owner, self.park_id,  self._onset_engine_park)
139       
140        self.menu1.FindItemById(self.scipy_id).Check(True)
141        self.menu1.FindItemById(self.park_id).Check(False)
142        #create  menubar items
143        return [(self.menu1, self.sub_menu)]
144               
145    def on_add_sim_page(self, event):
146        """
147        Create a page to access simultaneous fit option
148        """
149        if self.sim_page != None:
150            msg= "Simultaneous Fit page already opened"
151            wx.PostEvent(self.parent, StatusEvent(status= msg))
152            return 
153       
154        self.sim_page= self.fit_panel.add_sim_page()
155       
156    def help(self, evt):
157        """
158        Show a general help dialog.
159        """
160        from help_panel import  HelpWindow
161        frame = HelpWindow(None, -1, 'HelpWindow')   
162        frame.Show(True)
163       
164    def get_context_menu(self, plotpanel=None):
165        """
166        Get the context menu items available for P(r).them allow fitting option
167        for Data2D and Data1D only.
168       
169        :param graph: the Graph object to which we attach the context menu
170       
171        :return: a list of menu items with call-back function
172       
173        :note: if Data1D was generated from Theory1D 
174                the fitting option is not allowed
175               
176        """
177        graph = plotpanel.graph
178        fit_option = "Select data for fitting"
179        fit_hint =  "Dialog with fitting parameters "
180       
181        if graph.selected_plottable not in plotpanel.plots:
182            return []
183        item = plotpanel.plots[graph.selected_plottable]
184        if item.__class__.__name__ is "Data2D": 
185            if hasattr(item,"is_data"):
186                if item.is_data:
187                    return [[fit_option, fit_hint, self._onSelect]]
188                else:
189                    return [] 
190            return [[fit_option, fit_hint, self._onSelect]]
191        else:
192           
193            # if is_data is true , this in an actual data loaded
194            #else it is a data created from a theory model
195            if hasattr(item,"is_data"):
196                if item.is_data:
197                    return [[fit_option, fit_hint,
198                              self._onSelect]]
199                else:
200                    return [] 
201        return []   
202
203
204    def get_panels(self, parent):
205        """
206        Create and return a list of panel objects
207        """
208        self.parent = parent
209        #self.parent.Bind(EVT_FITSTATE_UPDATE, self.on_set_state_helper)
210        # Creation of the fit panel
211        self.fit_panel = FitPanel(parent=self.parent, manager=self)
212        self.on_add_new_page(event=None)
213        #Set the manager for the main panel
214        self.fit_panel.set_manager(self)
215        # List of windows used for the perspective
216        self.perspective = []
217        self.perspective.append(self.fit_panel.window_name)
218       
219        #index number to create random model name
220        self.index_model = 0
221        self.index_theory= 0
222        self.parent.Bind(EVT_SLICER_PANEL, self._on_slicer_event)
223       
224        self.parent.Bind(EVT_REMOVE_DATA, self._closed_fitpage)
225        self.parent.Bind(EVT_SLICER_PARS_UPDATE, self._onEVT_SLICER_PANEL)
226        self.parent._mgr.Bind(wx.aui.EVT_AUI_PANE_CLOSE,self._onclearslicer)   
227        #Create reader when fitting panel are created
228        self.state_reader = Reader(self.set_state)   
229        #append that reader to list of available reader
230        loader = Loader()
231        loader.associate_file_reader(".fitv", self.state_reader)
232        loader.associate_file_reader(".svs", self.state_reader)
233        from sans.perspectives.calculator.sld_panel import SldPanel
234        #Send the fitting panel to guiframe
235        self.mypanels.append(self.fit_panel) 
236        self.mypanels.append(SldPanel(parent=self.parent, base=self.parent))
237        return self.mypanels
238   
239    def clear_panel(self):
240        """
241        """
242        self.fit_panel.clear_panel()
243       
244    def set_default_perspective(self):
245        """
246        Call back method that True to notify the parent that the current plug-in
247        can be set as default  perspective.
248        when returning False, the plug-in is not candidate for an automatic
249        default perspective setting
250        """
251        return True
252   
253    def delete_data(self, data):
254        """
255        delete  the given data from panel
256        """
257        self.fit_panel.delete_data(data)
258       
259    def set_data(self, data_list=None):
260        """
261        receive a list of data to fit
262        """
263        if data_list is None:
264            data_list = []
265        selected_data_list = []
266        if len(data_list) > MAX_NBR_DATA :
267            from fitting_widgets import DataDialog
268            dlg = DataDialog(data_list=data_list, nb_data=MAX_NBR_DATA)
269            if dlg.ShowModal() == wx.ID_OK:
270                selected_data_list = dlg.get_data()
271        else:
272            selected_data_list = data_list
273        try:
274            for data in selected_data_list:
275                self.add_fit_page(data=data)
276                wx.PostEvent(self.parent, NewPlotEvent(plot=data, 
277                                                       title=str(data.title)))
278               
279        except:
280            raise
281            #msg = "Fitting Set_data: " + str(sys.exc_value)
282            #wx.PostEvent(self.parent, StatusEvent(status=msg, info="error"))
283   
284    def set_top_panel(self):
285        """
286        Close default (welcome) panel
287        """
288        if 'default' in self.parent.panels:
289            self.parent.on_close_welcome_panel()
290
291             
292    def set_theory(self,  theory_list=None):
293        """
294        """
295        #set the model state for a given theory_state:
296        for item in theory_list:
297            try:
298                _, theory_state = item
299                self.fit_panel.set_model_state(theory_state)
300            except:
301                msg = "Fitting: cannot deal with the theory received"
302                logging.error("set_theory " + msg + "\n" + str(sys.exc_value))
303                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error"))
304           
305    def set_state(self, state=None, datainfo=None, format=None):
306        """
307        Call-back method for the fit page state reader.
308        This method is called when a .fitv/.svs file is loaded.
309       
310        : param state: PageState object
311        : param datainfo: data
312        """
313        #state = self.state_reader.get_state()
314        if state != None:
315            state = state.clone()
316            # store fitting state in temp_state
317            self.temp_state.append(state) 
318        else:
319            self.temp_state = []
320        # index to start with for a new set_state
321        self.state_index = 0
322        # state file format
323        self.sfile_ext = format
324       
325        self.on_set_state_helper(event=None)
326
327    def  on_set_state_helper(self,event=None):
328        """
329        Set_state_helper. This actually sets state after plotting data from state file.
330       
331        : event: FitStateUpdateEvent called by dataloader.plot_data from guiframe
332        """
333        if len(self.temp_state) == 0:
334            if self.state_index==0 and len(self.mypanels) <= 0 and self.sfile_ext =='.svs':
335                self.fit_panel.add_default_pages()
336                self.temp_state = []
337                self.state_index = 0
338            return
339       
340        try:
341            # Load fitting state
342            state = self.temp_state[self.state_index]
343            #panel state should have model selection to set_state
344            if state.formfactorcombobox != None:
345                #set state
346                data = self.parent.create_gui_data(state.data)
347                data.group_id = state.data.group_id
348                self.parent.add_data(data_list={data.id:data})
349                wx.PostEvent(self.parent, NewPlotEvent(plot=data,
350                                        title=data.title))
351                #need to be fix later make sure we are sendind guiframe.data
352                #to panel
353                state.data = data
354                page = self.fit_panel.set_state(state)   
355            else:
356                #just set data because set_state won't work
357                data = self.parent.create_gui_data(state.data)
358                data.group_id = state.data.group_id
359                self.parent.add_data(data_list={data.id:data})
360                wx.PostEvent(self.parent, NewPlotEvent(plot=data,
361                                        title=data.title))
362                page = self.add_fit_page(data)
363                caption = page.window_name
364                self.store_data(page=page.id, data=data, caption=caption)
365                self.mypanels.append(page) 
366               
367            # get ready for the next set_state
368            self.state_index += 1
369
370            #reset state variables to default when all set_state is finished.
371            if len(self.temp_state) == self.state_index:
372               
373                self.temp_state = []
374                #self.state_index = 0
375                # Make sure the user sees the fitting panel after loading
376                #self.parent.set_perspective(self.perspective)
377                self.on_perspective(event=None)
378        except:
379            self.state_index==0
380            self.temp_state = []
381            raise
382                 
383    def save_fit_state(self, filepath, fitstate): 
384        """
385        save fit page state into file
386        """
387        self.state_reader.write(filename=filepath, fitstate=fitstate)
388       
389    def set_fit_range(self, uid, qmin, qmax):
390        """
391        Set the fitting range of a given page
392        """
393        self.page_finder[uid].set_range(qmin=qmin, qmax=qmax)
394                   
395    def schedule_for_fit(self,value=0, uid=None,fitproblem =None): 
396        """
397        Set the fit problem field to 0 or 1 to schedule that problem to fit.
398        Schedule the specified fitproblem or get the fit problem related to
399        the current page and set value.
400       
401        :param value: integer 0 or 1
402        :param fitproblem: fitproblem to schedule or not to fit
403       
404        """   
405        if fitproblem !=None:
406            fitproblem.schedule_tofit(value)
407        else:
408            self.page_finder[uid].schedule_tofit(value)
409         
410    def get_page_finder(self):
411        """
412        return self.page_finder used also by simfitpage.py
413        """ 
414        return self.page_finder
415   
416    def set_page_finder(self,modelname,names,values):
417        """
418        Used by simfitpage.py to reset a parameter given the string constrainst.
419         
420        :param modelname: the name ot the model for with the parameter has to reset
421        :param value: can be a string in this case.
422        :param names: the paramter name
423         
424        :note: expecting park used for fit.
425         
426        """ 
427        sim_page_id = self.sim_page.uid
428        for uid, value in self.page_finder.iteritems():
429            if uid != sim_page_id:
430                list = value.get_model()
431                model = list[0]
432                if model.name == modelname:
433                    value.set_model_param(names, values)
434                    break
435         
436    def split_string(self,item): 
437        """
438        receive a word containing dot and split it. used to split parameterset
439        name into model name and parameter name example: ::
440       
441            paramaterset (item) = M1.A
442            Will return model_name = M1 , parameter name = A
443           
444        """
445        if string.find(item,".")!=-1:
446            param_names= re.split("\.",item)
447            model_name=param_names[0]           
448            ##Assume max len is 3; eg., M0.radius.width
449            if len(param_names) == 3:
450                param_name=param_names[1]+"."+param_names[2]
451            else:
452                param_name=param_names[1]                   
453            return model_name,param_name
454   
455    def set_ftol(self, ftol=None):
456        """
457        Set ftol: Relative error desired in the sum of chi squares. 
458        """
459        # check if it is flaot
460        try:
461            f_tol = float(ftol)
462        except:
463            # default
464            f_tol = 1.49012e-08
465           
466        self.ftol = f_tol
467             
468    def stop_fit(self, uid):
469        """
470        Stop the fit engine
471        """
472        if uid in self.fit_thread_list.keys():
473            calc_fit = self.fit_thread_list[uid]
474            if calc_fit is not  None and calc_fit.isrunning():
475                calc_fit.stop()
476                msg = "Fit stop!"
477                wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
478        #set the fit button label of page when fit stop is trigger from
479        #simultaneous fit pane
480        if  self.sim_page is not None and uid == self.sim_page.uid:
481            for uid, value in self.page_finder.iteritems():
482                if value.get_scheduled() == 1:
483                    if uid in self.fit_panel.opened_pages.keys():
484                        panel = self.fit_panel.opened_pages[uid]
485                        panel. _on_fit_complete()
486 
487    def set_smearer(self, uid, smearer, qmin=None, qmax=None, draw=True, 
488                    enable2D=False):
489        """
490        Get a smear object and store it to a fit problem
491       
492        :param smearer: smear object to allow smearing data
493       
494        """   
495        if uid not in self.page_finder.keys():
496            msg = "Cannot find ID: %s in page_finder" % str(uid)
497            raise ValueError, msg
498        self.page_finder[uid].set_smearer(smearer)
499        self.page_finder[uid].set_enable2D(enable2D)
500        if draw:
501            ## draw model 1D with smeared data
502            data =  self.page_finder[uid].get_fit_data()
503            model = self.page_finder[uid].get_model()
504            if model is None:
505                return
506            enable1D = True
507            enable2D = self.page_finder[uid].get_enable2D()
508            if enable2D:
509                enable1D = False
510
511            ## if user has already selected a model to plot
512            ## redraw the model with data smeared
513            smear = self.page_finder[uid].get_smearer()
514            self.draw_model(model=model, data=data, page_id=uid, smearer=smear,
515                enable1D=enable1D, enable2D=enable2D,
516                qmin=qmin, qmax=qmax)
517
518    def draw_model(self, model, page_id, data=None, smearer=None,
519                   enable1D=True, enable2D=False,
520                   state=None,
521                   toggle_mode_on=False,
522                   qmin=DEFAULT_QMIN, qmax=DEFAULT_QMAX, 
523                   qstep=DEFAULT_NPTS,
524                   update_chisqr=True):
525        """
526        Draw model.
527       
528        :param model: the model to draw
529        :param name: the name of the model to draw
530        :param data: the data on which the model is based to be drawn
531        :param description: model's description
532        :param enable1D: if true enable drawing model 1D
533        :param enable2D: if true enable drawing model 2D
534        :param qmin:  Range's minimum value to draw model
535        :param qmax:  Range's maximum value to draw model
536        :param qstep: number of step to divide the x and y-axis
537        :param update_chisqr: update chisqr [bool]
538             
539        """
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):
568        """
569        perform fit
570        """
571        ##  count the number of fitproblem schedule to fit
572        fitproblem_count = 0
573        for value in self.page_finder.values():
574            if value.get_scheduled() == 1:
575                fitproblem_count += 1
576               
577        ## if simultaneous fit change automatically the engine to park
578        if fitproblem_count > 1:
579            self._on_change_engine(engine='park')
580           
581        self.fitproblem_count = fitproblem_count 
582         
583        from sans.fit.Fitting import Fit
584        fitter = Fit(self._fit_engine)
585       
586        if self._fit_engine == "park":
587            engineType = "Simultaneous Fit"
588        else:
589            engineType = "Single Fit"
590           
591        fproblemId = 0
592        self.current_pg = None
593        list_page_id = []
594        for page_id, value in self.page_finder.iteritems():
595            try:
596                if value.get_scheduled() == 1:
597                    #Get list of parameters name to fit
598                    pars = []
599                    templist = []
600                   
601                    page = self.fit_panel.get_page_by_id(page_id)
602                    templist = page.get_param_list()
603                    # missing fit parameters
604                    #if not templist:
605                    #    return
606                    # have the list
607                    for element in templist:
608                        name = str(element[1])
609                        pars.append(name)
610                    #Set Engine  (model , data) related to the page on
611                    self._fit_helper(value=value, pars=pars,
612                                     fitter=fitter,
613                                      fitproblem_id=fproblemId,
614                                      title=engineType) 
615                    list_page_id.append(page_id)
616                    fproblemId += 1 
617                    current_page_id = page_id
618            except:
619                #raise
620                msg= "%s error: %s" % (engineType, sys.exc_value)
621                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
622                                                      type="stop"))
623                return 
624        ## If a thread is already started, stop it
625        #if self.calc_fit!= None and self.calc_fit.isrunning():
626        #    self.calc_fit.stop()
627         #Handler used for park engine displayed message
628        handler = ConsoleUpdate(parent=self.parent,
629                                manager=self,
630                                improvement_delta=0.1)
631       
632        ## perform single fit
633        if fitproblem_count == 1:
634            calc_fit = FitThread(handler = handler,
635                                    fn=fitter,
636                                    pars=pars,
637                                    page_id=list_page_id,
638                                    completefn=self._single_fit_completed,
639                                    ftol=self.ftol)
640        else:
641            current_page_id = self.sim_page.uid
642            ## Perform more than 1 fit at the time
643            calc_fit = FitThread(handler=handler,
644                                    fn=fitter,
645                                    page_id=list_page_id,
646                                    updatefn=handler.update_fit,
647                                    completefn=self._simul_fit_completed,
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.FindItemById(self.park_id).Check(True)
1070            self.menu1.FindItemById(self.scipy_id).Check(False)
1071        else:
1072            self.menu1.FindItemById(self.park_id).Check(False)
1073            self.menu1.FindItemById(self.scipy_id).Check(True)
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           
1264            self.page_finder[page_id].set_theory_data(new_plot)
1265           
1266            if update_chisqr:
1267                wx.PostEvent(current_pg,
1268                             Chi2UpdateEvent(output=self._cal_chisqr(data=data,
1269                                                        page_id=page_id,
1270                                                        index=index)))
1271            else:
1272                self._plot_residuals(page_id, data, index)
1273
1274            msg = "Plot 1D  complete !"
1275            wx.PostEvent( self.parent, StatusEvent(status=msg, type="stop" ))
1276            #self.current_pg.state.theory_data = deepcopy(self.theory_data)
1277        except:
1278            raise
1279            #msg = " Error occurred when drawing %s Model 1D: " % new_plot.name
1280            #msg += " %s"  % sys.exc_value
1281            #wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1282   
1283    def _update2D(self, output,time=None):
1284        """
1285        Update the output of plotting model
1286        """
1287        wx.PostEvent(self.parent, StatusEvent(status="Plot \
1288        #updating ... ", type="update"))
1289        self.ready_fit()
1290 
1291    def _complete2D(self, image, data, model, page_id,  elapsed, index, qmin,
1292                     qmax, toggle_mode_on=False,state=None,qstep=DEFAULT_NPTS, 
1293                     update_chisqr=True):
1294        """
1295        Complete get the result of modelthread and create model 2D
1296        that can be plot.
1297        """
1298        err_image = numpy.zeros(numpy.shape(image))
1299       
1300        new_plot= Data2D(image=image, err_image=err_image)
1301        new_plot.name = model.name
1302        new_plot.title = "Analytical model 2D "
1303        if data is None:
1304            self._fill_default_model2D(theory=new_plot, 
1305                                       qmax=qmax, 
1306                                       page_id=page_id,
1307                                       qstep=qstep,
1308                                        qmin= qmin)
1309           
1310        else:
1311            new_plot.id = str(page_id) + " Model2D"
1312            new_plot.group_id = str(page_id) + " Model2D"
1313            new_plot.x_bins = data.x_bins
1314            new_plot.y_bins = data.y_bins
1315            new_plot.detector = data.detector
1316            new_plot.source = data.source
1317            new_plot.is_data = False 
1318            new_plot.qx_data = data.qx_data
1319            new_plot.qy_data = data.qy_data
1320            new_plot.q_data = data.q_data
1321            #numpy.zeros(len(data.err_data))#data.err_data
1322            new_plot.err_data = err_image
1323            new_plot.mask = data.mask
1324            ## plot boundaries
1325            new_plot.ymin = data.ymin
1326            new_plot.ymax = data.ymax
1327            new_plot.xmin = data.xmin
1328            new_plot.xmax = data.xmax
1329            title = data.title
1330            if len(title) > 1:
1331                new_plot.title = "Model2D for " + data.name
1332        new_plot.is_data = False
1333        new_plot.name = model.name + " ["+ str(model.__class__.__name__)+ "]"
1334
1335        theory_data = deepcopy(new_plot)
1336        theory_data.name = "Unknown"
1337        if toggle_mode_on:
1338            new_plot.id = str(page_id) + " Model"     
1339            wx.PostEvent(self.parent, 
1340                             NewPlotEvent(group_id=str(page_id) + " Model1D",
1341                                               action="Hide"))
1342       
1343        self.page_finder[page_id].set_theory_data(new_plot)
1344        if data is None:
1345            data_id = None
1346        else:
1347            data_id = data.id
1348        self.parent.update_theory(data_id=data_id, 
1349                                       theory=new_plot,
1350                                       state=state) 
1351        current_pg = self.fit_panel.get_page_by_id(page_id)
1352        title = new_plot.title
1353
1354        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1355                                               title=title))
1356        self.page_finder[page_id].set_theory_data(new_plot)
1357        # Chisqr in fitpage
1358        if update_chisqr:
1359            wx.PostEvent(current_pg,
1360                         Chi2UpdateEvent(output=\
1361                                    self._cal_chisqr(data=data,
1362                                                     page_id=page_id,
1363                                                     index=index)))
1364        else:
1365            self._plot_residuals(page_id, data, index)
1366        msg = "Plot 2D complete !"
1367        wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1368   
1369    def _draw_model2D(self, model, page_id, data=None, smearer=None,
1370                      description=None, enable2D=False,
1371                      state=None,
1372                      toggle_mode_on=False,
1373                      qmin=DEFAULT_QMIN, qmax=DEFAULT_QMAX,
1374                      qstep=DEFAULT_NPTS,
1375                      update_chisqr=True):
1376        """
1377        draw model in 2D
1378       
1379        :param model: instance of the model to draw
1380        :param description: the description of the model
1381        :param enable2D: when True allows to draw model 2D
1382        :param qmin: the minimum value to  draw model 2D
1383        :param qmax: the maximum value to draw model 2D
1384        :param qstep: the number of division of Qx and Qy of the model to draw
1385           
1386        """
1387        x=  numpy.linspace(start=-1*qmax,
1388                               stop=qmax,
1389                               num=qstep,
1390                               endpoint=True) 
1391        y = numpy.linspace(start= -1*qmax,
1392                               stop=qmax,
1393                               num=qstep,
1394                               endpoint=True)
1395        if model is None:
1396            msg = "Panel with ID: %s does not contained model" % str(page_id)
1397            raise ValueError, msg
1398        ## use data info instead
1399        if data is not None:
1400            ## check if data2D to plot
1401            if hasattr(data, "x_bins"):
1402                enable2D = True
1403                x = data.x_bins
1404                y = data.y_bins
1405               
1406        if not enable2D:
1407            return None, None
1408        try:
1409            from model_thread import Calc2D
1410            ## If a thread is already started, stop it
1411            if (self.calc_2D is not None) and self.calc_2D.isrunning():
1412                self.calc_2D.stop()
1413            self.calc_2D = Calc2D(x=x,
1414                                    y=y,
1415                                    model=model, 
1416                                    data=data,
1417                                    page_id=page_id,
1418                                    smearer=smearer,
1419                                    qmin=qmin,
1420                                    qmax=qmax,
1421                                    qstep=qstep,
1422                                    toggle_mode_on=toggle_mode_on,
1423                                    state=state,
1424                                    completefn=self._complete2D,
1425                                    #updatefn= self._update2D,
1426                                    update_chisqr=update_chisqr)
1427
1428            self.calc_2D.queue()
1429
1430        except:
1431            raise
1432            #msg = " Error occurred when drawing %s Model 2D: " % model.name
1433            #msg += " %s" % sys.exc_value
1434            #wx.PostEvent(self.parent, StatusEvent(status=msg))
1435
1436    def _draw_model1D(self, model, page_id, data=None, smearer=None,
1437                qmin=DEFAULT_QMIN, qmax=DEFAULT_QMAX, 
1438                state=None,
1439                toggle_mode_on=False,
1440                qstep=DEFAULT_NPTS, update_chisqr=True, 
1441                enable1D=True):
1442        """
1443        Draw model 1D from loaded data1D
1444       
1445        :param data: loaded data
1446        :param model: the model to plot
1447       
1448        """
1449        x=  numpy.linspace(start=qmin,
1450                           stop=qmax,
1451                           num=qstep,
1452                           endpoint=True
1453                           )
1454        if data is not None:
1455            ## check for data2D
1456            if hasattr(data,"x_bins"):
1457                return
1458            x = data.x
1459            if qmin == None :
1460                qmin == DEFAULT_QMIN
1461
1462            if qmax == None:
1463                qmax == DEFAULT_QMAX
1464        if not enable1D:
1465            return 
1466        try:
1467            from model_thread import Calc1D
1468            ## If a thread is already started, stop it
1469            if (self.calc_1D is not None) and self.calc_1D.isrunning():
1470                self.calc_1D.stop()
1471            self.calc_1D = Calc1D(x=x,
1472                                  data=data,
1473                                  model=model,
1474                                  page_id=page_id, 
1475                                  qmin=qmin,
1476                                  qmax=qmax,
1477                                  smearer=smearer,
1478                                  state=state,
1479                                  toggle_mode_on=toggle_mode_on,
1480                                  completefn=self._complete1D,
1481                                  #updatefn = self._update1D,
1482                                  update_chisqr=update_chisqr)
1483
1484            self.calc_1D.queue()
1485
1486        except:
1487            msg = " Error occurred when drawing %s Model 1D: " % model.name
1488            msg += " %s" % sys.exc_value
1489            wx.PostEvent(self.parent, StatusEvent(status=msg))
1490
1491    def _cal_chisqr(self, page_id, data=None, index=None): 
1492        """
1493        Get handy Chisqr using the output from draw1D and 2D,
1494        instead of calling expansive CalcChisqr in guithread
1495        """
1496        # default chisqr
1497        chisqr = None
1498
1499        # return None if data == None
1500        if data == None: return chisqr
1501       
1502        # Get data: data I, theory I, and data dI in order
1503        if data.__class__.__name__ == "Data2D":
1504            if index == None: 
1505                index = numpy.ones(len(data.data),ntype=bool)
1506            # get rid of zero error points
1507            index = index & (data.err_data != 0 ) 
1508            index = index & (numpy.isfinite(data.data)) 
1509            fn = data.data[index] 
1510            theory_data = self.page_finder[page_id].get_theory_data()
1511            gn = theory_data.data[index]
1512            en = data.err_data[index]
1513        else:
1514            # 1 d theory from model_thread is only in the range of index
1515            if index == None: 
1516                index = numpy.ones(len(data.y), ntype=bool)
1517            if data.dy == None or data.dy == []:
1518                dy = numpy.ones(len(data.y))
1519            else:
1520                ## Set consitently w/AbstractFitengine:
1521                # But this should be corrected later.
1522                dy = deepcopy(data.dy)
1523                dy[dy==0] = 1 
1524            fn = data.y[index] 
1525            theory_data = self.page_finder[page_id].get_theory_data()
1526            gn = theory_data.y
1527            en = dy[index]
1528
1529        # residual
1530        res = (fn - gn) / en
1531        residuals = res[numpy.isfinite(res)]
1532        # get chisqr only w/finite
1533        chisqr = numpy.average(residuals * residuals)
1534       
1535        self._plot_residuals(page_id, data, index)
1536        return chisqr
1537   
1538
1539       
1540    def _plot_residuals(self, page_id, data=None, index=None): 
1541        """
1542        Plot the residuals
1543       
1544        :param data: data
1545        :param index: index array (bool)
1546        : Note: this is different from the residuals in cal_chisqr()
1547        """
1548        if data == None: 
1549            return 
1550       
1551        # Get data: data I, theory I, and data dI in order
1552        if data.__class__.__name__ == "Data2D":
1553            # build residuals
1554            #print data
1555            residuals = Data2D()
1556            #residuals.copy_from_datainfo(data)
1557            # Not for trunk the line below, instead use the line above
1558            data.clone_without_data(len(data.data), residuals)
1559            residuals.data = None
1560            fn = data.data#[index]
1561            theory_data = self.page_finder[page_id].get_theory_data()
1562            gn = theory_data.data#[index]
1563            en = data.err_data#[index]
1564            residuals.data = (fn - gn) / en
1565            residuals.qx_data = data.qx_data#[index]
1566            residuals.qy_data = data.qy_data #[index]
1567            residuals.q_data = data.q_data#[index]
1568            residuals.err_data = numpy.ones(len(residuals.data))#[index]
1569            residuals.xmin = min(residuals.qx_data)
1570            residuals.xmax = max(residuals.qx_data)
1571            residuals.ymin = min(residuals.qy_data)
1572            residuals.ymax = max(residuals.qy_data)
1573            residuals.q_data = data.q_data#[index]
1574            residuals.mask = data.mask
1575            residuals.scale = 'linear'
1576            #print "print data",residuals
1577            # check the lengths
1578            if len(residuals.data) != len(residuals.q_data):
1579                return
1580
1581        else:
1582            # 1 d theory from model_thread is only in the range of index
1583            if data.dy == None or data.dy == []:
1584                dy = numpy.ones(len(data.y))
1585            else:
1586                ## Set consitently w/AbstractFitengine:
1587                ## But this should be corrected later.
1588                dy = deepcopy(data.dy)
1589                dy[dy==0] = 1 
1590            fn = data.y[index] 
1591            theory_data = self.page_finder[page_id].get_theory_data()
1592            gn = theory_data.y
1593            en = dy[index]
1594            # build residuals
1595            residuals = Data1D()
1596            residuals.y = (fn - gn) / en
1597            residuals.x = data.x[index]
1598            residuals.dy = numpy.ones(len(residuals.y))
1599            residuals.dx = None
1600            residuals.dxl = None
1601            residuals.dxw = None
1602            residuals.ytransform = 'y'
1603            # For latter scale changes
1604            residuals.xaxis('\\rm{Q} ', 'A^{-1}')
1605            residuals.yaxis('\\rm{Residuals} ', 'normalized')
1606           
1607        new_plot = residuals
1608        if data.id == None:
1609            data.id = data.name
1610        name  = data.id
1611        new_plot.name = "Residuals for " + str(data.name)
1612        ## allow to highlight data when plotted
1613        new_plot.interactive = True
1614        ## when 2 data have the same id override the 1 st plotted
1615        new_plot.id = new_plot.name#name + " residuals"
1616        ##group_id specify on which panel to plot this data
1617        new_plot.group_id = new_plot.id
1618        #new_plot.is_data = True
1619        ##post data to plot
1620        title = new_plot.name
1621       
1622        # plot data
1623        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title=title))   
1624       
1625#def profile(fn, *args, **kw):
1626#    import cProfile, pstats, os
1627#    global call_result
1628#    def call():
1629#        global call_result
1630#        call_result = fn(*args, **kw)
1631#    cProfile.runctx('call()', dict(call=call), {}, 'profile.out')
1632#    stats = pstats.Stats('profile.out')
1633#    #stats.sort_stats('time')
1634#    stats.sort_stats('calls')
1635#    stats.print_stats()
1636#    os.unlink('profile.out')
1637#    return call_result
1638if __name__ == "__main__":
1639    i = Plugin()
1640   
1641   
1642   
1643   
Note: See TracBrowser for help on using the repository browser.