source: sasview/sansview/perspectives/fitting/fitting.py @ 4cac267a

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 4cac267a was 3831ea1e, checked in by Jae Cho <jhjcho@…>, 13 years ago

fixed select for fitting from popupmenu on plot

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