source: sasview/sansview/perspectives/fitting/fitting.py @ 1c95d15

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 1c95d15 was bb9f322, checked in by Gervaise Alina <gervyh@…>, 14 years ago

make the model manager a singleton

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