source: sasview/sansview/perspectives/fitting/fitting.py @ 70468a5

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 70468a5 was 0145a25, checked in by Jae Cho <jhjcho@…>, 13 years ago

minor changes for plot labeling

  • Property mode set to 100644
File size: 67.6 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                #if model.output_name.lower().count("reflectivity") > 0:
1308                #    _yaxis, _yunit = "\\rm{%s}"% model.output_name, \
1309                #                                "%s"% model.output_unit
1310                #else:
1311                _yaxis, _yunit = data.get_yaxis() 
1312                _xaxis, _xunit = data.get_xaxis() 
1313                new_plot.title = data.name
1314                #if the theory is already plotted use the same group id
1315                #to replot
1316                if page_id in self.page_finder:
1317                    theory_data = self.page_finder[page_id].get_theory_data()
1318                    if theory_data is not None:
1319                       data.group_id = theory_data.group_id
1320                #data is plotted before the theory, then take its group_id
1321                #assign to the new theory
1322                new_plot.group_id = data.group_id
1323               
1324            else:
1325                _xaxis, _xunit = "\\rm{%s}"% model.input_name, \
1326                                                "%s"% model.input_unit
1327                _yaxis, _yunit = "\\rm{%s}"% model.output_name, \
1328                                                "%s"% model.output_unit
1329                new_plot.title = "Analytical model 1D "
1330                #find a group id to plot theory without data
1331                new_plot.group_id =  str(page_id) + " Model1D" 
1332            new_plot.id =  str(page_id) + " Model1D" 
1333           
1334            #find if this theory was already plotted and replace that plot given
1335            #the same id
1336           
1337            theory_data = self.page_finder[page_id].get_theory_data()
1338            if theory_data is not None:
1339                new_plot.id = theory_data.id
1340             
1341            new_plot.name = model.name + " ["+ str(model.__class__.__name__)+ "]"
1342            new_plot.xaxis(_xaxis, _xunit)
1343            new_plot.yaxis(_yaxis, _yunit)
1344            if toggle_mode_on:
1345                new_plot.id =  str(page_id) + " Model" 
1346                wx.PostEvent(self.parent, 
1347                             NewPlotEvent(group_id=str(page_id) + " Model2D",
1348                                               action="Hide"))
1349           
1350            self.page_finder[page_id].set_theory_data(new_plot)
1351            if data is None:
1352                data_id = None
1353            else:
1354                data_id = data.id
1355
1356            self.parent.update_theory(data_id=data_id, 
1357                                       theory=new_plot,
1358                                       state=state)   
1359 
1360            current_pg = self.fit_panel.get_page_by_id(page_id)
1361            title = new_plot.title
1362
1363            wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1364                                            title= str(title)))
1365
1366            self.page_finder[page_id].set_theory_data(new_plot)
1367
1368            if update_chisqr:
1369                wx.PostEvent(current_pg,
1370                             Chi2UpdateEvent(output=self._cal_chisqr(data=data,
1371                                                        page_id=page_id,
1372                                                        index=index)))
1373            else:
1374                self._plot_residuals(page_id, data, index)
1375
1376            msg = "Computation  completed!"
1377            wx.PostEvent( self.parent, StatusEvent(status=msg, type="stop" ))
1378
1379            #self.current_pg.state.theory_data = deepcopy(self.theory_data)
1380        except:
1381            raise
1382            #msg = " Error occurred when drawing %s Model 1D: " % new_plot.name
1383            #msg += " %s"  % sys.exc_value
1384            #wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1385   
1386    def _update2D(self, output,time=None):
1387        """
1388        Update the output of plotting model
1389        """
1390        wx.PostEvent(self.parent, StatusEvent(status="Plot \
1391        #updating ... ", type="update"))
1392        #self.ready_fit()
1393 
1394    def _complete2D(self, image, data, model, page_id,  elapsed, index, qmin,
1395                     qmax, toggle_mode_on=False,state=None,qstep=DEFAULT_NPTS, 
1396                     update_chisqr=True):
1397        """
1398        Complete get the result of modelthread and create model 2D
1399        that can be plot.
1400        """
1401        err_image = numpy.zeros(numpy.shape(image))
1402       
1403        new_plot= Data2D(image=image, err_image=err_image)
1404        new_plot.name = model.name
1405        new_plot.title = "Analytical model 2D "
1406        if data is None:
1407            self._fill_default_model2D(theory=new_plot, 
1408                                       qmax=qmax, 
1409                                       page_id=page_id,
1410                                       qstep=qstep,
1411                                        qmin= qmin)
1412           
1413        else:
1414            new_plot.id = str(page_id) + " Model2D"
1415            new_plot.group_id = str(page_id) + " Model2D"
1416            new_plot.x_bins = data.x_bins
1417            new_plot.y_bins = data.y_bins
1418            new_plot.detector = data.detector
1419            new_plot.source = data.source
1420            new_plot.is_data = False 
1421            new_plot.qx_data = data.qx_data
1422            new_plot.qy_data = data.qy_data
1423            new_plot.q_data = data.q_data
1424            #numpy.zeros(len(data.err_data))#data.err_data
1425            new_plot.err_data = err_image
1426            new_plot.mask = data.mask
1427            ## plot boundaries
1428            new_plot.ymin = data.ymin
1429            new_plot.ymax = data.ymax
1430            new_plot.xmin = data.xmin
1431            new_plot.xmax = data.xmax
1432            title = data.title
1433            if len(title) > 1:
1434                new_plot.title = "Model2D for " + data.name
1435        new_plot.is_data = False
1436        new_plot.name = model.name + " ["+ str(model.__class__.__name__)+ "]"
1437
1438        theory_data = deepcopy(new_plot)
1439        theory_data.name = "Unknown"
1440        if toggle_mode_on:
1441            new_plot.id = str(page_id) + " Model"     
1442            wx.PostEvent(self.parent, 
1443                             NewPlotEvent(group_id=str(page_id) + " Model1D",
1444                                               action="Hide"))
1445       
1446        self.page_finder[page_id].set_theory_data(new_plot)
1447        if data is None:
1448            data_id = None
1449        else:
1450            data_id = data.id
1451        self.parent.update_theory(data_id=data_id, 
1452                                       theory=new_plot,
1453                                       state=state) 
1454        current_pg = self.fit_panel.get_page_by_id(page_id)
1455        title = new_plot.title
1456
1457        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1458                                               title=title))
1459        self.page_finder[page_id].set_theory_data(new_plot)
1460        # Chisqr in fitpage
1461        if update_chisqr:
1462            wx.PostEvent(current_pg,
1463                         Chi2UpdateEvent(output=\
1464                                    self._cal_chisqr(data=data,
1465                                                     page_id=page_id,
1466                                                     index=index)))
1467        else:
1468            self._plot_residuals(page_id, data, index)
1469        msg = "Computation  completed!"
1470        wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1471   
1472    def _draw_model2D(self, model, page_id, data=None, smearer=None,
1473                      description=None, enable2D=False,
1474                      state=None,
1475                      toggle_mode_on=False,
1476                      qmin=DEFAULT_QMIN, qmax=DEFAULT_QMAX,
1477                      qstep=DEFAULT_NPTS,
1478                      update_chisqr=True):
1479        """
1480        draw model in 2D
1481       
1482        :param model: instance of the model to draw
1483        :param description: the description of the model
1484        :param enable2D: when True allows to draw model 2D
1485        :param qmin: the minimum value to  draw model 2D
1486        :param qmax: the maximum value to draw model 2D
1487        :param qstep: the number of division of Qx and Qy of the model to draw
1488           
1489        """
1490        x=  numpy.linspace(start=-1*qmax,
1491                               stop=qmax,
1492                               num=qstep,
1493                               endpoint=True) 
1494        y = numpy.linspace(start= -1*qmax,
1495                               stop=qmax,
1496                               num=qstep,
1497                               endpoint=True)
1498        if model is None:
1499            msg = "Panel with ID: %s does not contained model" % str(page_id)
1500            raise ValueError, msg
1501        ## use data info instead
1502        if data is not None:
1503            ## check if data2D to plot
1504            if hasattr(data, "x_bins"):
1505                enable2D = True
1506                x = data.x_bins
1507                y = data.y_bins
1508               
1509        if not enable2D:
1510            return None, None
1511        try:
1512            from model_thread import Calc2D
1513            ## If a thread is already started, stop it
1514            if (self.calc_2D is not None) and self.calc_2D.isrunning():
1515                self.calc_2D.stop()
1516            self.calc_2D = Calc2D(x=x,
1517                                    y=y,
1518                                    model=model, 
1519                                    data=data,
1520                                    page_id=page_id,
1521                                    smearer=smearer,
1522                                    qmin=qmin,
1523                                    qmax=qmax,
1524                                    qstep=qstep,
1525                                    toggle_mode_on=toggle_mode_on,
1526                                    state=state,
1527                                    completefn=self._complete2D,
1528                                    #updatefn= self._update2D,
1529                                    update_chisqr=update_chisqr)
1530
1531            self.calc_2D.queue()
1532
1533        except:
1534            raise
1535            #msg = " Error occurred when drawing %s Model 2D: " % model.name
1536            #msg += " %s" % sys.exc_value
1537            #wx.PostEvent(self.parent, StatusEvent(status=msg))
1538
1539    def _draw_model1D(self, model, page_id, data=None, smearer=None,
1540                qmin=DEFAULT_QMIN, qmax=DEFAULT_QMAX, 
1541                state=None,
1542                toggle_mode_on=False,
1543                qstep=DEFAULT_NPTS, update_chisqr=True, 
1544                enable1D=True):
1545        """
1546        Draw model 1D from loaded data1D
1547       
1548        :param data: loaded data
1549        :param model: the model to plot
1550       
1551        """
1552        x=  numpy.linspace(start=qmin,
1553                           stop=qmax,
1554                           num=qstep,
1555                           endpoint=True
1556                           )
1557        if data is not None:
1558            ## check for data2D
1559            if hasattr(data,"x_bins"):
1560                return
1561            x = data.x
1562            if qmin == None :
1563                qmin == DEFAULT_QMIN
1564
1565            if qmax == None:
1566                qmax == DEFAULT_QMAX
1567        if not enable1D:
1568            return 
1569        try:
1570            from model_thread import Calc1D
1571            ## If a thread is already started, stop it
1572            if (self.calc_1D is not None) and self.calc_1D.isrunning():
1573                self.calc_1D.stop()
1574            self.calc_1D = Calc1D(x=x,
1575                                  data=data,
1576                                  model=model,
1577                                  page_id=page_id, 
1578                                  qmin=qmin,
1579                                  qmax=qmax,
1580                                  smearer=smearer,
1581                                  state=state,
1582                                  toggle_mode_on=toggle_mode_on,
1583                                  completefn=self._complete1D,
1584                                  #updatefn = self._update1D,
1585                                  update_chisqr=update_chisqr)
1586
1587            self.calc_1D.queue()
1588        except:
1589            msg = " Error occurred when drawing %s Model 1D: " % model.name
1590            msg += " %s" % sys.exc_value
1591            wx.PostEvent(self.parent, StatusEvent(status=msg))
1592
1593    def _cal_chisqr(self, page_id, data=None, index=None): 
1594        """
1595        Get handy Chisqr using the output from draw1D and 2D,
1596        instead of calling expansive CalcChisqr in guithread
1597        """
1598        # default chisqr
1599        chisqr = None
1600
1601        # return None if data == None
1602        if data == None: return chisqr
1603       
1604        # Get data: data I, theory I, and data dI in order
1605        if data.__class__.__name__ == "Data2D":
1606            if index == None: 
1607                index = numpy.ones(len(data.data),ntype=bool)
1608            # get rid of zero error points
1609            index = index & (data.err_data != 0 ) 
1610            index = index & (numpy.isfinite(data.data)) 
1611            fn = data.data[index] 
1612            theory_data = self.page_finder[page_id].get_theory_data()
1613            gn = theory_data.data[index]
1614            en = data.err_data[index]
1615        else:
1616            # 1 d theory from model_thread is only in the range of index
1617            if index == None: 
1618                index = numpy.ones(len(data.y), ntype=bool)
1619            if data.dy == None or data.dy == []:
1620                dy = numpy.ones(len(data.y))
1621            else:
1622                ## Set consitently w/AbstractFitengine:
1623                # But this should be corrected later.
1624                dy = deepcopy(data.dy)
1625                dy[dy==0] = 1 
1626            fn = data.y[index] 
1627            theory_data = self.page_finder[page_id].get_theory_data()
1628            gn = theory_data.y
1629            en = dy[index]
1630
1631        # residual
1632        res = (fn - gn) / en
1633        residuals = res[numpy.isfinite(res)]
1634        # get chisqr only w/finite
1635        chisqr = numpy.average(residuals * residuals)
1636       
1637        self._plot_residuals(page_id, data, index)
1638        return chisqr
1639   
1640
1641       
1642    def _plot_residuals(self, page_id, data=None, index=None): 
1643        """
1644        Plot the residuals
1645       
1646        :param data: data
1647        :param index: index array (bool)
1648        : Note: this is different from the residuals in cal_chisqr()
1649        """
1650        if data == None: 
1651            return 
1652       
1653        # Get data: data I, theory I, and data dI in order
1654        if data.__class__.__name__ == "Data2D":
1655            # build residuals
1656            #print data
1657            residuals = Data2D()
1658            #residuals.copy_from_datainfo(data)
1659            # Not for trunk the line below, instead use the line above
1660            data.clone_without_data(len(data.data), residuals)
1661            residuals.data = None
1662            fn = data.data#[index]
1663            theory_data = self.page_finder[page_id].get_theory_data()
1664            gn = theory_data.data#[index]
1665            en = data.err_data#[index]
1666            residuals.data = (fn - gn) / en
1667            residuals.qx_data = data.qx_data#[index]
1668            residuals.qy_data = data.qy_data #[index]
1669            residuals.q_data = data.q_data#[index]
1670            residuals.err_data = numpy.ones(len(residuals.data))#[index]
1671            residuals.xmin = min(residuals.qx_data)
1672            residuals.xmax = max(residuals.qx_data)
1673            residuals.ymin = min(residuals.qy_data)
1674            residuals.ymax = max(residuals.qy_data)
1675            residuals.q_data = data.q_data#[index]
1676            residuals.mask = data.mask
1677            residuals.scale = 'linear'
1678            #print "print data",residuals
1679            # check the lengths
1680            if len(residuals.data) != len(residuals.q_data):
1681                return
1682
1683        else:
1684            # 1 d theory from model_thread is only in the range of index
1685            if data.dy == None or data.dy == []:
1686                dy = numpy.ones(len(data.y))
1687            else:
1688                ## Set consitently w/AbstractFitengine:
1689                ## But this should be corrected later.
1690                dy = deepcopy(data.dy)
1691                dy[dy==0] = 1 
1692            fn = data.y[index] 
1693            theory_data = self.page_finder[page_id].get_theory_data()
1694            gn = theory_data.y
1695            en = dy[index]
1696            # build residuals
1697            residuals = Data1D()
1698            residuals.y = (fn - gn) / en
1699            residuals.x = data.x[index]
1700            residuals.dy = numpy.ones(len(residuals.y))
1701            residuals.dx = None
1702            residuals.dxl = None
1703            residuals.dxw = None
1704            residuals.ytransform = 'y'
1705            # For latter scale changes
1706            residuals.xaxis('\\rm{Q} ', 'A^{-1}')
1707            residuals.yaxis('\\rm{Residuals} ', 'normalized')
1708           
1709        new_plot = residuals
1710        if data.id == None:
1711            data.id = data.name
1712        name  = data.id
1713        new_plot.name = "Residuals for " + str(data.name)
1714        ## allow to highlight data when plotted
1715        new_plot.interactive = True
1716        ## when 2 data have the same id override the 1 st plotted
1717        new_plot.id = new_plot.name#name + " residuals"
1718        ##group_id specify on which panel to plot this data
1719        new_plot.group_id = new_plot.id
1720        #new_plot.is_data = True
1721        ##post data to plot
1722        title = new_plot.name
1723       
1724        # plot data
1725        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title=title))   
1726       
1727#def profile(fn, *args, **kw):
1728#    import cProfile, pstats, os
1729#    global call_result
1730#    def call():
1731#        global call_result
1732#        call_result = fn(*args, **kw)
1733#    cProfile.runctx('call()', dict(call=call), {}, 'profile.out')
1734#    stats = pstats.Stats('profile.out')
1735#    #stats.sort_stats('time')
1736#    stats.sort_stats('calls')
1737#    stats.print_stats()
1738#    os.unlink('profile.out')
1739#    return call_result
1740if __name__ == "__main__":
1741    i = Plugin()
1742   
1743   
1744   
1745   
Note: See TracBrowser for help on using the repository browser.