source: sasview/fittingview/src/sans/perspectives/fitting/fitting.py @ 7fd4afd

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

a little better back-to bookmark/still some problem to fix

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