source: sasview/sansview/perspectives/fitting/fitting.py @ 07c8630

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 07c8630 was 59afa71, checked in by Jae Cho <jhjcho@…>, 13 years ago

fixed more for simfit

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