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

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

moving features from the branch

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