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

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

removed print statement

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