source: sasview/fittingview/src/sans/perspectives/fitting/fitting.py @ 32cd80c

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 32cd80c was ad3fa1e, checked in by Gervaise Alina <gervyh@…>, 13 years ago

remove unused attribute

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