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

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 d5c2f4d was 2c6b224, checked in by Jae Cho <jhjcho@…>, 13 years ago

added a menu item : Ftol setup

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