source: sasview/sansview/perspectives/fitting/fitting.py @ 61ffd1e

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 61ffd1e was 908b817, checked in by Gervaise Alina <gervyh@…>, 14 years ago

edit menu fitting label

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