source: sasview/sansview/perspectives/fitting/fitting.py @ 3ce9937

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 3ce9937 was bc03f06, checked in by Jae Cho <jhjcho@…>, 14 years ago

minor fix about id or uid(0)

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