source: sasview/sansview/perspectives/fitting/fitting.py @ 735c179

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 735c179 was bdc25e2, checked in by Jae Cho <jhjcho@…>, 13 years ago

removed some sleep commands

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