source: sasview/sansview/perspectives/fitting/fitting.py @ c22305e

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

remove redundant code to set the panel on focus in add fit tab

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